big-moving.ru/api/soft/ajaxorg/lib/ace/ext/beautify.js

409 lines
15 KiB
JavaScript
Raw Normal View History

2022-06-24 15:29:23 +05:00
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
// [WIP]
define(function(require, exports, module) {
"use strict";
var TokenIterator = require("../token_iterator").TokenIterator;
function is(token, type) {
return token.type.lastIndexOf(type + ".xml") > -1;
}
// do not indent after singleton tags or <html>
exports.singletonTags = ["area", "base", "br", "col", "command", "embed", "hr", "html", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"];
// insert a line break after block level tags
exports.blockTags = ["article", "aside", "blockquote", "body", "div", "dl", "fieldset", "footer", "form", "head", "header", "html", "nav", "ol", "p", "script", "section", "style", "table", "tbody", "tfoot", "thead", "ul"];
exports.beautify = function(session) {
var iterator = new TokenIterator(session, 0, 0);
var token = iterator.getCurrentToken();
var tabString = session.getTabString();
var singletonTags = exports.singletonTags;
var blockTags = exports.blockTags;
var nextToken;
var breakBefore = false;
var spaceBefore = false;
var spaceAfter = false;
var code = "";
var value = "";
var tagName = "";
var depth = 0;
var lastDepth = 0;
var lastIndent = 0;
var indent = 0;
var unindent = 0;
var roundDepth = 0;
var curlyDepth = 0;
var row;
var curRow = 0;
var rowsToAdd = 0;
var rowTokens = [];
var abort = false;
var i;
var indentNextLine = false;
var inTag = false;
var inCSS = false;
var inBlock = false;
var levels = {0: 0};
var parents = [];
var trimNext = function() {
if (nextToken && nextToken.value && nextToken.type !== 'string.regexp')
nextToken.value = nextToken.value.trim();
};
var trimLine = function() {
code = code.replace(/ +$/, "");
};
var trimCode = function() {
code = code.trimRight();
breakBefore = false;
};
while (token !== null) {
curRow = iterator.getCurrentTokenRow();
rowTokens = iterator.$rowTokens;
nextToken = iterator.stepForward();
if (typeof token !== "undefined") {
value = token.value;
unindent = 0;
// mode
inCSS = (tagName === "style" || session.$modeId === "ace/mode/css");
// in tag
if (is(token, "tag-open")) {
inTag = true;
// are we in a block tag
if (nextToken)
inBlock = (blockTags.indexOf(nextToken.value) !== -1);
// html indentation
if (value === "</") {
// line break before closing tag unless we just had one
if (inBlock && !breakBefore && rowsToAdd < 1)
rowsToAdd++;
if (inCSS)
rowsToAdd = 1;
unindent = 1;
inBlock = false;
}
} else if (is(token, "tag-close")) {
inTag = false;
// comments
} else if (is(token, "comment.start")) {
inBlock = true;
} else if (is(token, "comment.end")) {
inBlock = false;
}
// line break before }
if (!inTag && !rowsToAdd && token.type === "paren.rparen" && token.value.substr(0, 1) === "}") {
rowsToAdd++;
}
// add rows
if (curRow !== row) {
rowsToAdd = curRow;
if (row)
rowsToAdd -= row;
}
if (rowsToAdd) {
trimCode();
for (; rowsToAdd > 0; rowsToAdd--)
code += "\n";
breakBefore = true;
// trim value if not in a comment or string
if (!is(token, "comment") && !token.type.match(/^(comment|string)$/))
value = value.trimLeft();
}
if (value) {
// whitespace
if (token.type === "keyword" && value.match(/^(if|else|elseif|for|foreach|while|switch)$/)) {
parents[depth] = value;
trimNext();
spaceAfter = true;
// space before else, elseif
if (value.match(/^(else|elseif)$/)) {
if (code.match(/\}[\s]*$/)) {
trimCode();
spaceBefore = true;
}
}
// trim value after opening paren
} else if (token.type === "paren.lparen") {
trimNext();
// whitespace after {
if (value.substr(-1) === "{") {
spaceAfter = true;
indentNextLine = false;
if(!inTag)
rowsToAdd = 1;
}
// ensure curly brace is preceeded by whitespace
if (value.substr(0, 1) === "{") {
spaceBefore = true;
// collapse square and curly brackets together
if (code.substr(-1) !== '[' && code.trimRight().substr(-1) === '[') {
trimCode();
spaceBefore = false;
} else if (code.trimRight().substr(-1) === ')') {
trimCode();
} else {
trimLine();
}
}
// remove space before closing paren
} else if (token.type === "paren.rparen") {
unindent = 1;
// ensure curly brace is preceeded by whitespace
if (value.substr(0, 1) === "}") {
if (parents[depth-1] === 'case')
unindent++;
if (code.trimRight().substr(-1) === '{') {
trimCode();
} else {
spaceBefore = true;
if (inCSS)
rowsToAdd+=2;
}
}
// collapse square and curly brackets together
if (value.substr(0, 1) === "]") {
if (code.substr(-1) !== '}' && code.trimRight().substr(-1) === '}') {
spaceBefore = false;
indent++;
trimCode();
}
}
// collapse round brackets together
if (value.substr(0, 1) === ")") {
if (code.substr(-1) !== '(' && code.trimRight().substr(-1) === '(') {
spaceBefore = false;
indent++;
trimCode();
}
}
trimLine();
// add spaces around conditional operators
} else if ((token.type === "keyword.operator" || token.type === "keyword") && value.match(/^(=|==|===|!=|!==|&&|\|\||and|or|xor|\+=|.=|>|>=|<|<=|=>)$/)) {
trimCode();
trimNext();
spaceBefore = true;
spaceAfter = true;
// remove space before semicolon
} else if (token.type === "punctuation.operator" && value === ';') {
trimCode();
trimNext();
spaceAfter = true;
if (inCSS)
rowsToAdd++;
// space after colon or comma
} else if (token.type === "punctuation.operator" && value.match(/^(:|,)$/)) {
trimCode();
trimNext();
// line break after commas in curly block
if (value.match(/^(,)$/) && curlyDepth>0 && roundDepth===0) {
rowsToAdd++;
} else {
spaceAfter = true;
breakBefore = false;
}
// ensure space before php closing tag
} else if (token.type === "support.php_tag" && value === "?>" && !breakBefore) {
trimCode();
spaceBefore = true;
// remove excess space before HTML attribute
} else if (is(token, "attribute-name") && code.substr(-1).match(/^\s$/)) {
spaceBefore = true;
// remove space around attribute equals
} else if (is(token, "attribute-equals")) {
trimLine();
trimNext();
// remove space before HTML closing tag
} else if (is(token, "tag-close")) {
trimLine();
if(value === "/>")
spaceBefore = true;
}
// add indent to code unless multiline string or comment
if (breakBefore && !(token.type.match(/^(comment)$/) && !value.substr(0, 1).match(/^[/#]$/)) && !(token.type.match(/^(string)$/) && !value.substr(0, 1).match(/^['"]$/))) {
indent = lastIndent;
if(depth > lastDepth) {
indent++;
for (i=depth; i > lastDepth; i--)
levels[i] = indent;
} else if(depth < lastDepth)
indent = levels[depth];
lastDepth = depth;
lastIndent = indent;
if(unindent)
indent -= unindent;
if (indentNextLine && !roundDepth) {
indent++;
indentNextLine = false;
}
for (i = 0; i < indent; i++)
code += tabString;
}
if (token.type === "keyword" && value.match(/^(case|default)$/)) {
parents[depth] = value;
depth++;
}
if (token.type === "keyword" && value.match(/^(break)$/)) {
if(parents[depth-1] && parents[depth-1].match(/^(case|default)$/)) {
depth--;
}
}
// indent one line after if or else
if (token.type === "paren.lparen") {
roundDepth += (value.match(/\(/g) || []).length;
curlyDepth += (value.match(/\{/g) || []).length;
depth += value.length;
}
if (token.type === "keyword" && value.match(/^(if|else|elseif|for|while)$/)) {
indentNextLine = true;
roundDepth = 0;
} else if (!roundDepth && value.trim() && token.type !== "comment")
indentNextLine = false;
if (token.type === "paren.rparen") {
roundDepth -= (value.match(/\)/g) || []).length;
curlyDepth -= (value.match(/\}/g) || []).length;
for (i = 0; i < value.length; i++) {
depth--;
if(value.substr(i, 1)==='}' && parents[depth]==='case') {
depth--;
}
}
}
// add to code
if (spaceBefore && !breakBefore) {
trimLine();
if (code.substr(-1) !== "\n")
code += " ";
}
code += value;
if (spaceAfter)
code += " ";
breakBefore = false;
spaceBefore = false;
spaceAfter = false;
// line break after block tag or doctype
if ((is(token, "tag-close") && (inBlock || blockTags.indexOf(tagName) !== -1)) || (is(token, "doctype") && value === ">")) {
// undo linebreak if tag is immediately closed
if (inBlock && nextToken && nextToken.value === "</")
rowsToAdd = -1;
else
rowsToAdd = 1;
}
// html indentation
if (is(token, "tag-open") && value === "</") {
depth--;
// indent after opening tag
} else if (is(token, "tag-open") && value === "<" && singletonTags.indexOf(nextToken.value) === -1) {
depth++;
// remove indent if unknown singleton
} else if (is(token, "tag-name")) {
tagName = value;
} else if (is(token, "tag-close") && value === "/>" && singletonTags.indexOf(tagName) === -1){
depth--;
}
row = curRow;
}
}
token = nextToken;
}
code = code.trim();
session.doc.setValue(code);
};
exports.commands = [{
name: "beautify",
description: "Format selection (Beautify)",
exec: function(editor) {
exports.beautify(editor.session);
},
bindKey: "Ctrl-Shift-B"
}];
});