Files
FOF-ACE-Editor/lib/ace/src-noconflict/ext-language_tools.js
2025-08-16 16:14:03 +08:00

2989 lines
88 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

ace.define("ace/snippets", ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter", "ace/lib/lang", "ace/range", "ace/range_list", "ace/keyboard/hash_handler", "ace/tokenizer", "ace/clipboard", "ace/lib/dom", "ace/editor", "ace/ext-emmet"], function (require, exports, module) {
"use strict";
var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
var lang = require("./lib/lang");
var Range = require("./range").Range;
var RangeList = require("./range_list").RangeList;
var HashHandler = require("./keyboard/hash_handler").HashHandler;
var Tokenizer = require("./tokenizer").Tokenizer;
var clipboard = require("./clipboard");
var VARIABLES = {
CURRENT_WORD: function (editor) {
return editor.session.getTextRange(editor.session.getWordRange());
},
SELECTION: function (editor, name, indentation) {
var text = editor.session.getTextRange();
if (indentation)
return text.replace(/\n\r?([ \t]*\S)/g, "\n" + indentation + "$1");
return text;
},
CURRENT_LINE: function (editor) {
return editor.session.getLine(editor.getCursorPosition().row);
},
PREV_LINE: function (editor) {
return editor.session.getLine(editor.getCursorPosition().row - 1);
},
LINE_INDEX: function (editor) {
return editor.getCursorPosition().row;
},
LINE_NUMBER: function (editor) {
return editor.getCursorPosition().row + 1;
},
SOFT_TABS: function (editor) {
return editor.session.getUseSoftTabs() ? "YES" : "NO";
},
TAB_SIZE: function (editor) {
return editor.session.getTabSize();
},
CLIPBOARD: function (editor) {
return clipboard.getText && clipboard.getText();
},
FILENAME: function (editor) {
return /[^/\\]*$/.exec(this.FILEPATH(editor))[0];
},
FILENAME_BASE: function (editor) {
return /[^/\\]*$/.exec(this.FILEPATH(editor))[0].replace(/\.[^.]*$/, "");
},
DIRECTORY: function (editor) {
return this.FILEPATH(editor).replace(/[^/\\]*$/, "");
},
FILEPATH: function (editor) {
return "/not implemented.txt";
},
WORKSPACE_NAME: function () {
return "Unknown";
},
FULLNAME: function () {
return "Unknown";
},
BLOCK_COMMENT_START: function (editor) {
var mode = editor.session.$mode || {};
return mode.blockComment && mode.blockComment.start || "";
},
BLOCK_COMMENT_END: function (editor) {
var mode = editor.session.$mode || {};
return mode.blockComment && mode.blockComment.end || "";
},
LINE_COMMENT: function (editor) {
var mode = editor.session.$mode || {};
return mode.lineCommentStart || "";
},
CURRENT_YEAR: date.bind(null, {
year: "numeric"
}),
CURRENT_YEAR_SHORT: date.bind(null, {
year: "2-digit"
}),
CURRENT_MONTH: date.bind(null, {
month: "numeric"
}),
CURRENT_MONTH_NAME: date.bind(null, {
month: "long"
}),
CURRENT_MONTH_NAME_SHORT: date.bind(null, {
month: "short"
}),
CURRENT_DATE: date.bind(null, {
day: "2-digit"
}),
CURRENT_DAY_NAME: date.bind(null, {
weekday: "long"
}),
CURRENT_DAY_NAME_SHORT: date.bind(null, {
weekday: "short"
}),
CURRENT_HOUR: date.bind(null, {
hour: "2-digit",
hour12: false
}),
CURRENT_MINUTE: date.bind(null, {
minute: "2-digit"
}),
CURRENT_SECOND: date.bind(null, {
second: "2-digit"
})
};
VARIABLES.SELECTED_TEXT = VARIABLES.SELECTION;
function date(dateFormat) {
var str = new Date().toLocaleString("en-us", dateFormat);
return str.length == 1 ? "0" + str : str;
}
var SnippetManager = function () {
this.snippetMap = {};
this.snippetNameMap = {};
};
(function () {
oop.implement(this, EventEmitter);
this.getTokenizer = function () {
return SnippetManager.$tokenizer || this.createTokenizer();
};
this.createTokenizer = function () {
function TabstopToken(str) {
str = str.substr(1);
if (/^\d+$/.test(str))
return [{
tabstopId: parseInt(str, 10)
}];
return [{
text: str
}];
}
function escape(ch) {
return "(?:[^\\\\" + ch + "]|\\\\.)";
}
var formatMatcher = {
regex: "/(" + escape("/") + "+)/",
onMatch: function (val, state, stack) {
var ts = stack[0];
ts.fmtString = true;
ts.guard = val.slice(1, -1);
ts.flag = "";
return "";
},
next: "formatString"
};
SnippetManager.$tokenizer = new Tokenizer({
start: [{
regex: /\\./,
onMatch: function (val, state, stack) {
var ch = val[1];
if (ch == "}" && stack.length) {
val = ch;
} else if ("`$\\".indexOf(ch) != -1) {
val = ch;
}
return [val];
}
},
{
regex: /}/,
onMatch: function (val, state, stack) {
return [stack.length ? stack.shift() : val];
}
},
{
regex: /\$(?:\d+|\w+)/,
onMatch: TabstopToken
},
{
regex: /\$\{[\dA-Z_a-z]+/,
onMatch: function (str, state, stack) {
var t = TabstopToken(str.substr(1));
stack.unshift(t[0]);
return t;
},
next: "snippetVar"
},
{
regex: /\n/,
token: "newline",
merge: false
}
],
snippetVar: [{
regex: "\\|" + escape("\\|") + "*\\|",
onMatch: function (val, state, stack) {
var choices = val.slice(1, -1).replace(/\\[,|\\]|,/g, function (operator) {
return operator.length == 2 ? operator[1] : "\x00";
}).split("\x00").map(function (value) {
return {
value: value
};
});
stack[0].choices = choices;
return [choices[0]];
},
next: "start"
},
formatMatcher,
{
regex: "([^:}\\\\]|\\\\.)*:?",
token: "",
next: "start"
}
],
formatString: [{
regex: /:/,
onMatch: function (val, state, stack) {
if (stack.length && stack[0].expectElse) {
stack[0].expectElse = false;
stack[0].ifEnd = {
elseEnd: stack[0]
};
return [stack[0].ifEnd];
}
return ":";
}
},
{
regex: /\\./,
onMatch: function (val, state, stack) {
var ch = val[1];
if (ch == "}" && stack.length)
val = ch;
else if ("`$\\".indexOf(ch) != -1)
val = ch;
else if (ch == "n")
val = "\n";
else if (ch == "t")
val = "\t";
else if ("ulULE".indexOf(ch) != -1)
val = {
changeCase: ch,
local: ch > "a"
};
return [val];
}
},
{
regex: "/\\w*}",
onMatch: function (val, state, stack) {
var next = stack.shift();
if (next)
next.flag = val.slice(1, -1);
this.next = next && next.tabstopId ? "start" : "";
return [next || val];
},
next: "start"
},
{
regex: /\$(?:\d+|\w+)/,
onMatch: function (val, state, stack) {
return [{
text: val.slice(1)
}];
}
},
{
regex: /\${\w+/,
onMatch: function (val, state, stack) {
var token = {
text: val.slice(2)
};
stack.unshift(token);
return [token];
},
next: "formatStringVar"
},
{
regex: /\n/,
token: "newline",
merge: false
},
{
regex: /}/,
onMatch: function (val, state, stack) {
var next = stack.shift();
this.next = next && next.tabstopId ? "start" : "";
return [next || val];
},
next: "start"
}
],
formatStringVar: [{
regex: /:\/\w+}/,
onMatch: function (val, state, stack) {
var ts = stack[0];
ts.formatFunction = val.slice(2, -1);
return [stack.shift()];
},
next: "formatString"
},
formatMatcher,
{
regex: /:[\?\-+]?/,
onMatch: function (val, state, stack) {
if (val[1] == "+")
stack[0].ifEnd = stack[0];
if (val[1] == "?")
stack[0].expectElse = true;
},
next: "formatString"
},
{
regex: "([^:}\\\\]|\\\\.)*:?",
token: "",
next: "formatString"
}
]
});
return SnippetManager.$tokenizer;
};
this.tokenizeTmSnippet = function (str, startState) {
return this.getTokenizer().getLineTokens(str, startState).tokens.map(function (x) {
return x.value || x;
});
};
this.getVariableValue = function (editor, name, indentation) {
if (/^\d+$/.test(name))
return (this.variables.__ || {})[name] || "";
if (/^[A-Z]\d+$/.test(name))
return (this.variables[name[0] + "__"] || {})[name.substr(1)] || "";
name = name.replace(/^TM_/, "");
if (!this.variables.hasOwnProperty(name))
return "";
var value = this.variables[name];
if (typeof value == "function")
value = this.variables[name](editor, name, indentation);
return value == null ? "" : value;
};
this.variables = VARIABLES;
this.tmStrFormat = function (str, ch, editor) {
if (!ch.fmt) return str;
var flag = ch.flag || "";
var re = ch.guard;
re = new RegExp(re, flag.replace(/[^gim]/g, ""));
var fmtTokens = typeof ch.fmt == "string" ? this.tokenizeTmSnippet(ch.fmt, "formatString") : ch.fmt;
var _self = this;
var formatted = str.replace(re, function () {
var oldArgs = _self.variables.__;
_self.variables.__ = [].slice.call(arguments);
var fmtParts = _self.resolveVariables(fmtTokens, editor);
var gChangeCase = "E";
for (var i = 0; i < fmtParts.length; i++) {
var ch = fmtParts[i];
if (typeof ch == "object") {
fmtParts[i] = "";
if (ch.changeCase && ch.local) {
var next = fmtParts[i + 1];
if (next && typeof next == "string") {
if (ch.changeCase == "u")
fmtParts[i] = next[0].toUpperCase();
else
fmtParts[i] = next[0].toLowerCase();
fmtParts[i + 1] = next.substr(1);
}
} else if (ch.changeCase) {
gChangeCase = ch.changeCase;
}
} else if (gChangeCase == "U") {
fmtParts[i] = ch.toUpperCase();
} else if (gChangeCase == "L") {
fmtParts[i] = ch.toLowerCase();
}
}
_self.variables.__ = oldArgs;
return fmtParts.join("");
});
return formatted;
};
this.tmFormatFunction = function (str, ch, editor) {
if (ch.formatFunction == "upcase")
return str.toUpperCase();
if (ch.formatFunction == "downcase")
return str.toLowerCase();
return str;
};
this.resolveVariables = function (snippet, editor) {
var result = [];
var indentation = "";
var afterNewLine = true;
for (var i = 0; i < snippet.length; i++) {
var ch = snippet[i];
if (typeof ch == "string") {
result.push(ch);
if (ch == "\n") {
afterNewLine = true;
indentation = "";
} else if (afterNewLine) {
indentation = /^\t*/.exec(ch)[0];
afterNewLine = /\S/.test(ch);
}
continue;
}
if (!ch) continue;
afterNewLine = false;
if (ch.fmtString) {
var j = snippet.indexOf(ch, i + 1);
if (j == -1) j = snippet.length;
ch.fmt = snippet.slice(i + 1, j);
i = j;
}
if (ch.text) {
var value = this.getVariableValue(editor, ch.text, indentation) + "";
if (ch.fmtString)
value = this.tmStrFormat(value, ch, editor);
if (ch.formatFunction)
value = this.tmFormatFunction(value, ch, editor);
if (value && !ch.ifEnd) {
result.push(value);
gotoNext(ch);
} else if (!value && ch.ifEnd) {
gotoNext(ch.ifEnd);
}
} else if (ch.elseEnd) {
gotoNext(ch.elseEnd);
} else if (ch.tabstopId != null) {
result.push(ch);
} else if (ch.changeCase != null) {
result.push(ch);
}
}
function gotoNext(ch) {
var i1 = snippet.indexOf(ch, i + 1);
if (i1 !== -1)
i = i1;
}
return result;
};
// 输入处理后的代码段
this.insertSnippetForSelection = function (editor, snippetText) {
var cursor = editor.getCursorPosition();
var line = editor.session.getLine(cursor.row);
var tabString = editor.session.getTabString();
var indentString = line.match(/^\s*/)[0];
if (cursor.column < indentString.length)
indentString = indentString.slice(0, cursor.column);
snippetText = snippetText.replace(/\r/g, "");
var tokens = this.tokenizeTmSnippet(snippetText);
tokens = this.resolveVariables(tokens, editor);
if (window.is_moveModule && line.length > cursor.column) {
tokens.push('\n');
tokens.splice(1, 0, '\t');
}
tokens = tokens.map(function (x) {
if (x == "\n")
return x + indentString;
if (typeof x == "string")
return x.replace(/\t/g, tabString);
return x;
});
var tabstops = [];
tokens.forEach(function (p, i) {
if (typeof p != "object")
return;
var id = p.tabstopId;
var ts = tabstops[id];
if (!ts) {
ts = tabstops[id] = [];
ts.index = id;
ts.value = "";
ts.parents = {};
}
if (ts.indexOf(p) !== -1)
return;
if (p.choices && !ts.choices)
ts.choices = p.choices;
ts.push(p);
var i1 = tokens.indexOf(p, i + 1);
if (i1 === -1)
return;
var value = tokens.slice(i + 1, i1);
var isNested = value.some(function (t) {
return typeof t === "object";
});
if (isNested && !ts.value) {
ts.value = value;
} else if (value.length && (!ts.value || typeof ts.value !== "string")) {
ts.value = value.join("");
}
});
tabstops.forEach(function (ts) {
ts.length = 0;
});
var expanding = {};
function copyValue(val) {
var copy = [];
for (var i = 0; i < val.length; i++) {
var p = val[i];
if (typeof p == "object") {
if (expanding[p.tabstopId])
continue;
var j = val.lastIndexOf(p, i - 1);
p = copy[j] || {
tabstopId: p.tabstopId
};
}
copy[i] = p;
}
return copy;
}
for (var i = 0; i < tokens.length; i++) {
var p = tokens[i];
if (typeof p != "object")
continue;
var id = p.tabstopId;
var ts = tabstops[id];
var i1 = tokens.indexOf(p, i + 1);
if (expanding[id]) {
if (expanding[id] === p) {
delete expanding[id];
Object.keys(expanding).forEach(function (parentId) {
ts.parents[parentId] = true;
});
}
continue;
}
expanding[id] = p;
var value = ts.value;
if (typeof value !== "string")
value = copyValue(value);
else if (p.fmt)
value = this.tmStrFormat(value, p, editor);
tokens.splice.apply(tokens, [i + 1, Math.max(0, i1 - i)].concat(value, p));
if (ts.indexOf(p) === -1)
ts.push(p);
}
var row = 0,
column = 0;
var text = "";
tokens.forEach(function (t) {
if (typeof t === "string") {
var lines = t.split("\n");
if (lines.length > 1) {
column = lines[lines.length - 1].length;
row += lines.length - 1;
} else
column += t.length;
text += t;
} else if (t) {
if (!t.start)
t.start = {
row: row,
column: column
};
else
t.end = {
row: row,
column: column
};
}
});
var range = editor.getSelectionRange();
var end = editor.session.replace(range, text);
var tabstopManager = new TabstopManager(editor);
var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
};
// 插入提示
this.insertSnippet = function (editor, snippetText, values = false) {
var self = this;
if (editor.inVirtualSelectionMode)
return self.insertSnippetForSelection(editor, snippetText);
editor.forEachSelection(function () {
self.insertSnippetForSelection(editor, snippetText);
}, null, {
keepOrder: true
});
// TODO tab下一个输入的功能加上了如果存在属性值就自动弹出选择框
if (editor.tabstopManager) {
editor.tabstopManager.tabNext();
if (values) {
setTimeout(() => {
editor.completer.showPopup(editor);
}, 50)
}
}
};
this.$getScope = function (editor) {
var scope = editor.session.$mode.$id || "";
scope = scope.split("/").pop();
if (scope === "html" || scope === "php") {
if (scope === "php" && !editor.session.$mode.inlinePhp)
scope = "html";
var c = editor.getCursorPosition();
var state = editor.session.getState(c.row);
if (typeof state === "object") {
state = state[0];
}
if (state.substring) {
if (state.substring(0, 3) == "js-")
scope = "javascript";
else if (state.substring(0, 4) == "css-")
scope = "css";
else if (state.substring(0, 4) == "php-")
scope = "php";
}
}
return scope;
};
this.getActiveScopes = function (editor) {
var scope = this.$getScope(editor);
var scopes = [scope];
var snippetMap = this.snippetMap;
if (snippetMap[scope] && snippetMap[scope].includeScopes) {
scopes.push.apply(scopes, snippetMap[scope].includeScopes);
}
scopes.push("_");
return scopes;
};
this.expandWithTab = function (editor, options) {
var self = this;
var result = editor.forEachSelection(function () {
return self.expandSnippetForSelection(editor, options);
}, null, {
keepOrder: true
});
if (result && editor.tabstopManager)
editor.tabstopManager.tabNext();
return result;
};
this.expandSnippetForSelection = function (editor, options) {
var cursor = editor.getCursorPosition();
var line = editor.session.getLine(cursor.row);
var before = line.substring(0, cursor.column);
var after = line.substr(cursor.column);
var snippetMap = this.snippetMap;
var snippet;
this.getActiveScopes(editor).some(function (scope) {
var snippets = snippetMap[scope];
if (snippets)
snippet = this.findMatchingSnippet(snippets, before, after);
return !!snippet;
}, this);
if (!snippet)
return false;
if (options && options.dryRun)
return true;
editor.session.doc.removeInLine(cursor.row,
cursor.column - snippet.replaceBefore.length,
cursor.column + snippet.replaceAfter.length
);
this.variables.M__ = snippet.matchBefore;
this.variables.T__ = snippet.matchAfter;
this.insertSnippetForSelection(editor, snippet.content);
this.variables.M__ = this.variables.T__ = null;
return true;
};
this.findMatchingSnippet = function (snippetList, before, after) {
for (var i = snippetList.length; i--;) {
var s = snippetList[i];
if (s.startRe && !s.startRe.test(before))
continue;
if (s.endRe && !s.endRe.test(after))
continue;
if (!s.startRe && !s.endRe)
continue;
s.matchBefore = s.startRe ? s.startRe.exec(before) : [""];
s.matchAfter = s.endRe ? s.endRe.exec(after) : [""];
s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : "";
s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : "";
return s;
}
};
this.snippetMap = {};
this.snippetNameMap = {};
this.register = function (snippets, scope) {
var snippetMap = this.snippetMap;
var snippetNameMap = this.snippetNameMap;
var self = this;
if (!snippets)
snippets = [];
function wrapRegexp(src) {
if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
src = "(?:" + src + ")";
return src || "";
}
function guardedRegexp(re, guard, opening) {
re = wrapRegexp(re);
guard = wrapRegexp(guard);
if (opening) {
re = guard + re;
if (re && re[re.length - 1] != "$")
re = re + "$";
} else {
re = re + guard;
if (re && re[0] != "^")
re = "^" + re;
}
return new RegExp(re);
}
function addSnippet(s) {
if (!s.scope)
s.scope = scope || "_";
scope = s.scope;
if (!snippetMap[scope]) {
snippetMap[scope] = [];
snippetNameMap[scope] = {};
}
let map = snippetNameMap[scope];
if (s.name) {
let old = map[s.name];
if (old) {
// TODO 这里匹配每个代码提示是否相同 s.body 原来的
if (s.id && s.id === old.id && s.p_snippet && s.p_snippet === old.p_snippet) {
self.unregister(old);
}
/* else if (s.p_snippet === old.p_snippet) {
self.unregister(old);
}*/
}
map[s.name] = s;
}
// 修改+1
try {
let findSnippet = snippetMap[scope].find(v => {
//if (v.name === s.name && v.p_snippet === s.p_snippet && v.end != undefined || s.code && v.end != undefined) {
if (v.name === s.name && v.p_snippet === s.p_snippet && v.end != undefined /*|| s.code && v.end != undefined*/) {
return true;
};
});
if (findSnippet) {
findSnippet = s;
} else {
snippetMap[scope].push(s);
}
} catch (error) {
snippetMap[scope].push(s);
}
if (s.prefix)
s.tabTrigger = s.prefix;
if (!s.body) s.body = s.name;
if (!s.content && s.body)
s.content = Array.isArray(s.body) ? s.body.join("\n") : s.body;
s.score = s.score || 100;
delete (s.body);
if (s.tabTrigger && !s.trigger) {
if (!s.guard && /^\w/.test(s.tabTrigger))
s.guard = "\\b";
s.trigger = lang.escapeRegExp(s.tabTrigger);
}
if (!s.trigger && !s.guard && !s.endTrigger && !s.endGuard)
return;
s.startRe = guardedRegexp(s.trigger, s.guard, true);
s.triggerRe = new RegExp(s.trigger);
s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
s.endTriggerRe = new RegExp(s.endTrigger);
}
if (Array.isArray(snippets)) {
snippets.forEach(addSnippet);
} else {
Object.keys(snippets).forEach(function (key) {
addSnippet(snippets[key]);
});
}
this._signal("registerSnippets", {
scope: scope
});
SnippetManager = this;
};
this.unregister = function (snippets, scope) {
var snippetMap = this.snippetMap;
var snippetNameMap = this.snippetNameMap;
function removeSnippet(s) {
var nameMap = snippetNameMap[s.scope || scope];
// TODO 删除了nameMap[s.name] 判断
if (nameMap) {
delete nameMap[s.name];
var map = snippetMap[s.scope || scope];
var i = map && map.findIndex(m => m.id === s.id && (m.p_snippet === s.p_name || m.p_snippet === s.p_snippet));
if (i >= 0)
map.splice(i, 1);
}
}
if (snippets.content)
removeSnippet(snippets);
else if (Array.isArray(snippets))
snippets.forEach(removeSnippet);
};
// 清除项目代码
this.clearRegister = function () {
var snippetMap = this.snippetMap;
var snippetNameMap = this.snippetNameMap;
function del(list = []) {
let keys = Object.keys(list);
(keys.length > 0) && keys.forEach(key => {
list[key] = (list[key].length > 0) ? list[key].filter(v => {
if (v.tabTrigger && v.tabTrigger.indexOf("页面提示") === -1 || !v.tabTrigger) {
return v;
}
}) : list[key];
})
return list;
};
this.snippetMap = del(snippetMap);
this.snippetNameMap = del(snippetNameMap);
}
// TODO 处理原始代码段
this.parseSnippetFile = function (str) {
str = str.replace(/\r/g, "");
var list = [],
snippet = {};
var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm;
var m;
while (m = re.exec(str)) {
if (m[1]) {
try {
snippet = JSON.parse(m[1]);
list.push(snippet);
} catch (e) { }
}
if (m[4]) {
snippet.content = m[4].replace(/^\t/gm, "");
list.push(snippet);
snippet = {};
} else {
var key = m[2],
val = m[3];
if (key == "regex") {
var guardRe = /\/((?:[^\/\\]|\\.)*)|$/g;
snippet.guard = guardRe.exec(val)[1];
snippet.trigger = guardRe.exec(val)[1];
snippet.endTrigger = guardRe.exec(val)[1];
snippet.endGuard = guardRe.exec(val)[1];
} else if (key == "snippet") {
snippet.tabTrigger = val.match(/^\S*/)[0];
if (!snippet.name)
snippet.name = val;
} else if (key) {
snippet[key] = val;
}
}
}
return list;
};
this.getSnippetByName = function (name, editor) {
var snippetMap = this.snippetNameMap;
var snippet;
this.getActiveScopes(editor).some(function (scope) {
var snippets = snippetMap[scope];
if (snippets)
snippet = snippets[name];
return !!snippet;
}, this);
return snippet;
};
this.getSnippetById = function (id, scope) {
var snippetMap = this.snippetMap;
var snippet;
var snippets = snippetMap[scope];
if (snippets)
snippet = snippets.find(snippet => snippet.id == id);
return snippet;
};
}).call(SnippetManager.prototype);
// 选项卡停止管理器
var TabstopManager = function (editor) {
if (editor.tabstopManager)
return editor.tabstopManager;
editor.tabstopManager = this;
this.$onChange = this.onChange.bind(this);
this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule;
this.$onChangeSession = this.onChangeSession.bind(this);
this.$onAfterExec = this.onAfterExec.bind(this);
this.attach(editor);
};
(function () {
this.attach = function (editor) {
this.index = 0;
this.ranges = [];
this.tabstops = [];
this.$openTabstops = null;
this.selectedTabstop = null;
this.editor = editor;
this.editor.on("change", this.$onChange);
this.editor.on("changeSelection", this.$onChangeSelection);
this.editor.on("changeSession", this.$onChangeSession);
this.editor.commands.on("afterExec", this.$onAfterExec);
this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
};
this.detach = function () {
this.tabstops.forEach(this.removeTabstopMarkers, this);
this.ranges = null;
this.tabstops = null;
this.selectedTabstop = null;
this.editor.removeListener("change", this.$onChange);
this.editor.removeListener("changeSelection", this.$onChangeSelection);
this.editor.removeListener("changeSession", this.$onChangeSession);
this.editor.commands.removeListener("afterExec", this.$onAfterExec);
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
this.editor.tabstopManager = null;
this.editor = null;
};
this.onChange = function (delta) {
var isRemove = delta.action[0] == "r";
var selectedTabstop = this.selectedTabstop || {};
var parents = selectedTabstop.parents || {};
var tabstops = (this.tabstops || []).slice();
for (var i = 0; i < tabstops.length; i++) {
var ts = tabstops[i];
var active = ts == selectedTabstop || parents[ts.index];
ts.rangeList.$bias = active ? 0 : 1;
if (delta.action == "remove" && ts !== selectedTabstop) {
var parentActive = ts.parents && ts.parents[selectedTabstop.index];
var startIndex = ts.rangeList.pointIndex(delta.start, parentActive);
startIndex = startIndex < 0 ? -startIndex - 1 : startIndex + 1;
var endIndex = ts.rangeList.pointIndex(delta.end, parentActive);
endIndex = endIndex < 0 ? -endIndex - 1 : endIndex - 1;
var toRemove = ts.rangeList.ranges.slice(startIndex, endIndex);
for (var j = 0; j < toRemove.length; j++)
this.removeRange(toRemove[j]);
}
ts.rangeList.$onChange(delta);
}
var session = this.editor.session;
if (!this.$inChange && isRemove && session.getLength() == 1 && !session.getValue())
this.detach();
};
this.updateLinkedFields = function () {
var ts = this.selectedTabstop;
if (!ts || !ts.hasLinkedRanges || !ts.firstNonLinked)
return;
this.$inChange = true;
var session = this.editor.session;
var text = session.getTextRange(ts.firstNonLinked);
for (var i = 0; i < ts.length; i++) {
var range = ts[i];
if (!range.linked)
continue;
var original = range.original;
var fmt = exports.snippetManager.tmStrFormat(text, original, this.editor);
session.replace(range, fmt);
}
this.$inChange = false;
};
this.onAfterExec = function (e) {
if (e.command && !e.command.readOnly)
this.updateLinkedFields();
};
this.onChangeSelection = function () {
if (!this.editor)
return;
var lead = this.editor.selection.lead;
var anchor = this.editor.selection.anchor;
var isEmpty = this.editor.selection.isEmpty();
for (var i = 0; i < this.ranges.length; i++) {
if (this.ranges[i].linked)
continue;
var containsLead = this.ranges[i].contains(lead.row, lead.column);
var containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column);
if (containsLead && containsAnchor)
return;
}
this.detach();
};
this.onChangeSession = function () {
this.detach();
};
this.tabNext = function (dir) {
var max = this.tabstops.length;
var index = this.index + (dir || 1);
index = Math.min(Math.max(index, 1), max);
if (index == max)
index = 0;
this.selectTabstop(index);
if (index === 0)
this.detach();
};
this.selectTabstop = function (index) {
this.$openTabstops = null;
var ts = this.tabstops[this.index];
if (ts)
this.addTabstopMarkers(ts);
this.index = index;
ts = this.tabstops[this.index];
if (!ts || !ts.length)
return;
this.selectedTabstop = ts;
var range = ts.firstNonLinked || ts;
if (ts.choices) range.cursor = range.start;
if (!this.editor.inVirtualSelectionMode) {
var sel = this.editor.multiSelect;
sel.toSingleRange(range);
for (var i = 0; i < ts.length; i++) {
if (ts.hasLinkedRanges && ts[i].linked)
continue;
sel.addRange(ts[i].clone(), true);
}
} else {
this.editor.selection.fromOrientedRange(range);
}
this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
if (this.selectedTabstop && this.selectedTabstop.choices)
this.editor.execCommand("startAutocomplete", {
matches: this.selectedTabstop.choices
});
};
this.addTabstops = function (tabstops, start, end) {
var useLink = this.useLink || !this.editor.getOption("enableMultiselect");
if (!this.$openTabstops)
this.$openTabstops = [];
if (!tabstops[0]) {
var p = Range.fromPoints(end, end);
moveRelative(p.start, start);
moveRelative(p.end, start);
tabstops[0] = [p];
tabstops[0].index = 0;
}
var i = this.index;
var arg = [i + 1, 0];
var ranges = this.ranges;
tabstops.forEach(function (ts, index) {
var dest = this.$openTabstops[index] || ts;
for (var i = 0; i < ts.length; i++) {
var p = ts[i];
var range = Range.fromPoints(p.start, p.end || p.start);
movePoint(range.start, start);
movePoint(range.end, start);
range.original = p;
range.tabstop = dest;
ranges.push(range);
if (dest != ts)
dest.unshift(range);
else
dest[i] = range;
if (p.fmtString || (dest.firstNonLinked && useLink)) {
range.linked = true;
dest.hasLinkedRanges = true;
} else if (!dest.firstNonLinked)
dest.firstNonLinked = range;
}
if (!dest.firstNonLinked)
dest.hasLinkedRanges = false;
if (dest === ts) {
arg.push(dest);
this.$openTabstops[index] = dest;
}
this.addTabstopMarkers(dest);
dest.rangeList = dest.rangeList || new RangeList();
dest.rangeList.$bias = 0;
dest.rangeList.addList(dest);
}, this);
if (arg.length > 2) {
if (this.tabstops.length)
arg.push(arg.splice(2, 1)[0]);
this.tabstops.splice.apply(this.tabstops, arg);
}
};
this.addTabstopMarkers = function (ts) {
var session = this.editor.session;
ts.forEach(function (range) {
if (!range.markerId)
range.markerId = session.addMarker(range, "ace_snippet-marker", "text");
});
};
this.removeTabstopMarkers = function (ts) {
var session = this.editor.session;
ts.forEach(function (range) {
session.removeMarker(range.markerId);
range.markerId = null;
});
};
this.removeRange = function (range) {
var i = range.tabstop.indexOf(range);
if (i != -1) range.tabstop.splice(i, 1);
i = this.ranges.indexOf(range);
if (i != -1) this.ranges.splice(i, 1);
i = range.tabstop.rangeList.ranges.indexOf(range);
if (i != -1) range.tabstop.splice(i, 1);
this.editor.session.removeMarker(range.markerId);
if (!range.tabstop.length) {
i = this.tabstops.indexOf(range.tabstop);
if (i != -1)
this.tabstops.splice(i, 1);
if (!this.tabstops.length)
this.detach();
}
};
this.keyboardHandler = new HashHandler();
this.keyboardHandler.bindKeys({
"Tab": function (editor) {
if (exports.snippetManager && exports.snippetManager.expandWithTab(editor))
return;
editor.tabstopManager.tabNext(1);
editor.renderer.scrollCursorIntoView();
},
"Shift-Tab": function (editor) {
editor.tabstopManager.tabNext(-1);
editor.renderer.scrollCursorIntoView();
},
"Esc": function (editor) {
editor.tabstopManager.detach();
}
});
}).call(TabstopManager.prototype);
var movePoint = function (point, diff) {
if (point.row == 0)
point.column += diff.column;
point.row += diff.row;
};
var moveRelative = function (point, start) {
if (point.row == start.row)
point.column -= start.column;
point.row -= start.row;
};
require("./lib/dom").importCssString("\
.ace_snippet-marker {\
-moz-box-sizing: border-box;\
box-sizing: border-box;\
background: rgba(194, 193, 208, 0.09);\
border: 1px dotted rgba(211, 208, 235, 0.62);\
position: absolute;\
}");
exports.snippetManager = new SnippetManager();
var Editor = require("./editor").Editor;
(function () {
this.insertSnippet = function (content, options) {
return exports.snippetManager.insertSnippet(this, content, options);
};
this.expandSnippet = function (options) {
return exports.snippetManager.expandWithTab(this, options);
};
}).call(Editor.prototype);
});
ace.define("ace/autocomplete/popup", ["require", "exports", "module", "ace/virtual_renderer", "ace/editor", "ace/range", "ace/lib/event", "ace/lib/lang", "ace/lib/dom"], function (require, exports, module) {
"use strict";
var Renderer = require("../virtual_renderer").VirtualRenderer;
var Editor = require("../editor").Editor;
var Range = require("../range").Range;
var event = require("../lib/event");
var lang = require("../lib/lang");
var dom = require("../lib/dom");
var $singleLineEditor = function (el) {
var renderer = new Renderer(el);
renderer.$maxLines = 4;
var editor = new Editor(renderer);
editor.setHighlightActiveLine(false);
editor.setShowPrintMargin(false);
editor.renderer.setShowGutter(false);
editor.renderer.setHighlightGutterLine(false);
editor.$mouseHandler.$focusTimeout = 0;
editor.$highlightTagPending = true;
return editor;
};
// TODO 提示器 的 左侧
var AcePopup = function (parentNode) {
var el = dom.createElement("div");
var popup = new $singleLineEditor(el);
if (parentNode)
parentNode.appendChild(el);
el.style.display = "none";
popup.renderer.content.style.cursor = "default";
popup.renderer.setStyle("ace_autocomplete");
popup.setOption("displayIndentGuides", false);
popup.setOption("dragDelay", 150);
var noop = function () { };
popup.focus = noop;
popup.$isFocused = true;
popup.renderer.$cursorLayer.restartTimer = noop;
popup.renderer.$cursorLayer.element.style.opacity = 0;
popup.renderer.$maxLines = 8;
popup.renderer.$keepTextAreaAtCursor = false;
popup.setHighlightActiveLine(false);
popup.session.highlight("");
popup.session.$searchHighlight.clazz = "ace_highlight-marker";
popup.on("mousedown", function (e) {
var pos = e.getDocumentPosition();
popup.selection.moveToPosition(pos);
selectionMarker.start.row = selectionMarker.end.row = pos.row;
e.stop();
});
var lastMouseEvent;
var hoverMarker = new Range(-1, 0, -1, Infinity);
var selectionMarker = new Range(-1, 0, -1, Infinity);
selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
popup.setSelectOnHover = function (val) {
if (!val) {
hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
} else if (hoverMarker.id) {
popup.session.removeMarker(hoverMarker.id);
hoverMarker.id = null;
}
};
popup.setSelectOnHover(false);
popup.on("mousemove", function (e) {
if (!lastMouseEvent) {
lastMouseEvent = e;
return;
}
if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
return;
}
lastMouseEvent = e;
lastMouseEvent.scrollTop = popup.renderer.scrollTop;
var row = lastMouseEvent.getDocumentPosition().row;
if (hoverMarker.start.row != row) {
if (!hoverMarker.id)
popup.setRow(row);
setHoverMarker(row);
}
});
popup.renderer.on("beforeRender", function () {
if (lastMouseEvent && hoverMarker.start.row != -1) {
lastMouseEvent.$pos = null;
var row = lastMouseEvent.getDocumentPosition().row;
if (!hoverMarker.id)
popup.setRow(row);
setHoverMarker(row, true);
}
});
popup.renderer.on("afterRender", function () {
var row = popup.getRow();
var t = popup.renderer.$textLayer;
var selected = t.element.childNodes[row - t.config.firstRow];
if (selected !== t.selectedNode && t.selectedNode)
dom.removeCssClass(t.selectedNode, "ace_selected");
t.selectedNode = selected;
if (selected)
dom.addCssClass(selected, "ace_selected");
});
var hideHoverMarker = function () {
setHoverMarker(-1);
};
var setHoverMarker = function (row, suppressRedraw) {
if (row !== hoverMarker.start.row) {
hoverMarker.start.row = hoverMarker.end.row = row;
if (!suppressRedraw)
popup.session._emit("changeBackMarker");
popup._emit("changeHoverMarker");
}
};
popup.getHoveredRow = function () {
return hoverMarker.start.row;
};
event.addListener(popup.container, "mouseout", hideHoverMarker);
popup.on("hide", hideHoverMarker);
popup.on("changeSelection", hideHoverMarker);
popup.session.doc.getLength = function () {
return popup.data.length;
};
popup.session.doc.getLine = function (i) {
var data = popup.data[i];
if (typeof data == "string")
return data;
return (data && data.value) || "";
};
var bgTokenizer = popup.session.bgTokenizer;
// TODO 提示左侧列表
bgTokenizer.$tokenizeRow = function (row) {
var data = popup.data[row];
var tokens = [];
if (!data)
return tokens;
if (typeof data == "string")
data = {
value: data
};
var caption = data.title || data.caption || data.value || data.name;
if (!caption) {
return;
}
var snippet = data.title || (data.snippet ? data.snippet.replace(/\${[\S\s]*}/g, "") : '');
// 显示图标
if (data.iconClass)
tokens.push({
type: data.iconClass,
value: " "
});
function addToken(value, className) {
value && tokens.push({
type: (data.className || "") + (className || ""),
value: value
});
}
var lower = caption.toLowerCase();
var filterText = (popup.filterText || "").toLowerCase();
var lastIndex = 0;
var lastI = 0;
// TODO 如果提示的字段太长,对字段做些处理
function getLength(str) {
var chineseCharCount = str.match(/[\u4e00-\u9fa5]/g) ? str.match(/[\u4e00-\u9fa5]/g).length : 0;
var englishCharCount = str.length - chineseCharCount;
var length = chineseCharCount * 2 + englishCharCount;
return length;
}
function getSubstring(str, length) {
var chineseCharCount = str.match(/[\u4e00-\u9fa5]/g) ? str.match(/[\u4e00-\u9fa5]/g).length : 0;
var englishCharCount = str.length - chineseCharCount;
var totalLength = chineseCharCount * 2 + englishCharCount;
if (length >= totalLength) {
return str; // 如果指定长度大于等于总长度,则返回整个字符串
}
var substring = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
var char = str.charAt(i);
if (/[\u4e00-\u9fa5]/.test(char)) {
count += 2; // 中文字符长度加2
} else {
count += 1; // 英文字符长度加1
}
if (count <= length) {
substring += char; // 将字符添加到结果字符串中
} else {
break; // 如果长度超过了指定长度,则退出循环
}
}
return substring;
}
if (snippet && getLength(snippet) > 20) {
data.longSnippet = snippet;
snippet = getSubstring(snippet, 20) + '...';
}
if (caption && getLength(caption) > 20) {
data.longSnippet = caption;
caption = getSubstring(caption, 20) + '...';
}
for (var i = 0; i <= filterText.length; i++) {
if (i != lastI && (data.matchMask & (1 << i) || i == filterText.length)) {
var sub = filterText.slice(lastI, i);
lastI = i;
var index = lower.indexOf(sub, lastIndex);
if (index == -1) continue;
if (caption.indexOf('|') !== -1) {
addToken(snippet.slice(lastIndex, index), "");
lastIndex = index + sub.length;
addToken(snippet.slice(index, lastIndex), "completion-highlight");
} else {
addToken(caption.slice(lastIndex, index), "");
lastIndex = index + sub.length;
addToken(caption.slice(index, lastIndex), "completion-highlight");
}
}
}
if (caption.indexOf('|') !== -1) {
addToken(snippet.slice(lastIndex, snippet.length), "");
} else {
addToken(caption.slice(lastIndex, caption.length), "");
}
if (data.meta)
tokens.push({
type: "completion-meta",
value: typeTips[data.meta] ? typeTips[data.meta] : data.meta
});
if (data.message)
tokens.push({
type: "completion-message",
value: data.message
});
return tokens;
};
bgTokenizer.$updateOnChange = noop;
bgTokenizer.start = noop;
popup.session.$computeWidth = function () {
return this.screenWidth = 0;
};
popup.isOpen = false;
popup.isTopdown = false;
popup.autoSelect = true;
popup.filterText = "";
popup.data = [];
popup.setData = function (list, filterText) {
popup.filterText = filterText || "";
popup.setValue(lang.stringRepeat("\n", list.length), -1);
popup.data = list || [];
popup.setRow(0);
};
popup.getData = function (row) {
return popup.data[row];
};
popup.getRow = function () {
return selectionMarker.start.row;
};
popup.setRow = function (line) {
line = Math.max(this.autoSelect ? 0 : -1, Math.min(this.data.length, line));
if (selectionMarker.start.row != line) {
popup.selection.clearSelection();
selectionMarker.start.row = selectionMarker.end.row = line || 0;
popup.session._emit("changeBackMarker");
popup.moveCursorTo(line || 0, 0);
if (popup.isOpen)
popup._signal("select");
}
};
popup.on("changeSelection", function () {
if (popup.isOpen)
popup.setRow(popup.selection.lead.row);
popup.renderer.scrollCursorIntoView();
});
popup.hide = function () {
this.container.style.display = "none";
this._signal("hide");
popup.isOpen = false;
};
popup.tryShow = function (pos, lineHeight, anchor, forceShow) {
if (!forceShow && popup.isOpen && popup.anchorPos && popup.anchor &&
popup.anchorPos.top === pos.top && popup.anchorPos.left === pos.left &&
popup.anchor === anchor) {
return true;
}
var el = this.container;
var screenHeight = window.innerHeight;
var screenWidth = window.innerWidth;
var renderer = this.renderer;
var maxH = renderer.$maxLines * lineHeight * 1.4;
var dims = {
top: 0,
bottom: 0,
left: 0
};
var spaceBelow = screenHeight - pos.top - 3 * this.$borderSize - lineHeight;
var spaceAbove = pos.top - 3 * this.$borderSize;
if (!anchor) {
if (spaceAbove <= spaceBelow || spaceBelow >= maxH) {
anchor = "bottom";
} else {
anchor = "top";
}
}
if (anchor === "top") {
dims.bottom = pos.top - this.$borderSize;
dims.top = dims.bottom - maxH;
} else if (anchor === "bottom") {
dims.top = pos.top + lineHeight + this.$borderSize;
dims.bottom = dims.top + maxH;
}
var fitsX = dims.top >= 0 && dims.bottom <= screenHeight;
if (!forceShow && !fitsX) {
return false;
}
if (!fitsX) {
if (anchor === "top") {
renderer.$maxPixelHeight = spaceAbove;
} else {
renderer.$maxPixelHeight = spaceBelow;
}
} else {
renderer.$maxPixelHeight = null;
}
if (anchor === "top") {
el.style.top = "";
el.style.bottom = (screenHeight - dims.bottom) + "px";
popup.isTopdown = false;
} else {
el.style.top = dims.top + "px";
el.style.bottom = "";
popup.isTopdown = true;
}
el.style.display = "";
var left = pos.left;
if (left + el.offsetWidth > screenWidth)
left = screenWidth - el.offsetWidth;
el.style.left = left + "px";
el.style.right = "";
if (!popup.isOpen) {
popup.isOpen = true;
this._signal("show");
lastMouseEvent = null;
}
popup.anchorPos = pos;
popup.anchor = anchor;
return true;
};
popup.show = function (pos, lineHeight, topdownOnly) {
this.tryShow(pos, lineHeight, topdownOnly ? "bottom" : undefined, true);
};
popup.goTo = function (where) {
var row = this.getRow();
var max = this.session.getLength() - 1;
switch (where) {
case "up":
row = row <= 0 ? max : row - 1;
break;
case "down":
row = row >= max ? -1 : row + 1;
break;
case "start":
row = 0;
break;
case "end":
row = max;
break;
}
this.setRow(row);
};
popup.getTextLeftOffset = function () {
return this.$borderSize + this.renderer.$padding + this.$imageSize;
};
popup.$imageSize = 0;
popup.$borderSize = 1;
return popup;
};
dom.importCssString("\
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
background-color: #CAD6FA;\
z-index: 1;\
}\
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
background-color: #3a674e;\
}\
.ace_editor.ace_autocomplete .ace_line-hover {\
border: 1px solid #abbffe;\
margin-top: -1px;\
background: rgba(233,233,253,0.4);\
position: absolute;\
z-index: 2;\
}\
.ace_dark.ace_editor.ace_autocomplete .ace_line-hover {\
border: 1px solid rgba(109, 150, 13, 0.8);\
background: rgba(58, 103, 78, 0.62);\
}\
.ace_completion-meta {\
opacity: 0.5;\
margin: 0 1em;\
float: right;\
}\
.ace_completion-message {\
color: blue;\
}\
.ace_editor.ace_autocomplete .ace_completion-highlight{\
color: #2d69c7;\
}\
.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight{\
color: #93ca12;\
}\
.ace_editor.ace_autocomplete {\
width: 300px;\
z-index: 200000;\
border: 1px lightgray solid;\
position: fixed;\
box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
line-height: 1.4;\
background: #fefefe;\
color: #111;\
}\
.ace_dark.ace_editor.ace_autocomplete {\
border: 1px #484747 solid;\
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.51);\
line-height: 1.4;\
background: #25282c;\
color: #c1c1c1;\
}", "autocompletion.css");
exports.AcePopup = AcePopup;
exports.$singleLineEditor = $singleLineEditor;
});
ace.define("ace/autocomplete/util", ["require", "exports", "module"], function (require, exports, module) {
"use strict";
exports.parForEach = function (array, fn, callback) {
var completed = 0;
var arLength = array.length;
if (arLength === 0)
callback();
for (var i = 0; i < arLength; i++) {
fn(array[i], function (result, err) {
completed++;
if (completed === arLength)
callback(result, err);
});
}
};
// TODO 这里是输入规则
// var ID_REGEX = /[a-zA-Z_0-9#.<>:\$\(\'")\-\u00A2-\u2000\u2070-\uFFFF@\[\]\s]/;
var ID_REGEX = /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF@\-\.#:\[\]]/;
exports.retrievePrecedingIdentifier = function (text, pos, regex) {
let scopes = (SnippetManager.getActiveScopes(EDITOR) || ["html"])[0];
if (scopes === 'javascript') {
ID_REGEX = /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF@\-\.#:]/;
} else {
ID_REGEX = /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF@\-\.#:\[\]]/;
}
regex = regex || ID_REGEX;
let run = false;
if (scopes === 'html') {
run = true;
}
if (!run && text.length > pos && ID_REGEX.test(text[pos])) {
return false;
}
var buf = [];
var contentIndex = -1;
for (var i = pos - 1; i >= 0; i--) {
if (regex.test(text[i])) {
if (buf.indexOf(')') === -1 && /"|'/.test(text[i]) || /\s+/.test(text[i]) && contentIndex == -1) {
break;
}
buf.push(text[i]);
if (contentIndex === -1) {
contentIndex = ["'", '"'].findIndex(v => v === text[i])
} else if (contentIndex !== -1 && ["'", '"'][contentIndex] === text[i]) {
contentIndex = -1;
};
} else {
break;
}
}
let needle = buf.reverse().join("");
if (/\(|\{|\[/.test(needle)) {
let needleArr = needle.split("");
let tip = [];
let sy = ["(", "[", "{"];
let sy2 = [")", "]", "}"];
needleArr.forEach(v => {
let index = sy.indexOf(v);
let index2 = sy2.indexOf(v);
if (index !== -1) {
tip.push({
text: v,
key: index
})
}
if (index2 !== -1) {
tip.forEach((v, k) => {
if (v.key === index2) {
tip.splice(k, 1)
}
})
}
})
if (tip.length > 0) {
let sy = tip[tip.length - 1].text;
let reg = "\\";
let re = new RegExp("[\\S\\s]*?" + reg + sy + "", "g");
needle = needle.replace(re, "")
}
}
if (/^\s+$|^\s*\/\/|^\d/.test(text)) {
return false
}
if (scopes === 'html') {
if (/<!--/.test(text)) {
return false
}
if (/[<《]/g.test(needle)) {
needle = needle.replace(/[\S\s]+[<《]/g, "《")
} else if (text.length > pos && /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF]/.test(text[pos])) {
return false
}
}
if (scopes === 'css') {
if (/\/\*/.test(text)) {
return false
}
if (text[pos - 1] === ";") {
return false
}
}
tipsRule = '';
if (scopes === 'javascript') {
var quotesArr = [];
var quotesArr2 = [];
let texts = text.split("");
let run = true;
let text1 = '';
let text2 = '';
texts.forEach((v, i) => {
if (v == "\"" && quotesArr2.length % 2 === 0) {
quotesArr.push(i)
};
if (quotesArr.length % 2 === 0) {
text1 = '';
} else {
text1 = text1 + v;
}
if (v == "'" && quotesArr.length % 2 === 0) {
quotesArr2.push(i)
};
if (quotesArr2.length % 2 === 0) {
text2 = '';
} else {
text2 = text2 + v;
}
if (i === (pos - 1)) {
if (quotesArr.length % 2 !== 0 || quotesArr2.length % 2 !== 0) {
run = false;
};
if (/@/g.test(text1) || /@/g.test(text2)) {
run = true;
}
return false;
}
})
if (!run) {
return false;
}
if (/^\./.test(needle) && /^\s/.test(text[pos - needle.length - 1])) {
return false;
}
needle = needle.replace(/\[|\]/g, "");
}
return needle;
};
exports.retrieveFollowingIdentifier = function (text, pos, regex) {
let scopes = (SnippetManager.getActiveScopes(EDITOR) || ["html"])[0];
if (scopes === 'javascript') {
ID_REGEX = /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF@\-\.#:]/;
} else {
ID_REGEX = /[a-zA-Z_0-9\u00A2-\u2000\u2070-\uFFFF@\-\.#:\[\]]/;
}
regex = regex || ID_REGEX;
var buf = [];
for (var i = pos; i < text.length; i++) {
if (regex.test(text[i]))
buf.push(text[i]);
else
break;
}
return buf;
};
/**
* TODO 获取输入的值
* * pos当前输入的坐标
* * line当前行数
*/
exports.getCompletionPrefix = function (editor) {
var pos = editor.getCursorPosition();
var line = editor.session.getLine(pos.row);
var prefix;
editor.completers.forEach(function (completer) {
if (completer.identifierRegexps) {
completer.identifierRegexps.forEach(function (identifierRegex) {
if (!prefix && identifierRegex) {
prefix = this.retrievePrecedingIdentifier(line, pos.column, identifierRegex);
}
}.bind(this));
}
}.bind(this));
return prefix || this.retrievePrecedingIdentifier(line, pos.column);
};
});
ace.define("ace/autocomplete", ["require", "exports", "module", "ace/keyboard/hash_handler", "ace/autocomplete/popup", "ace/autocomplete/util", "ace/lib/lang", "ace/lib/dom", "ace/snippets", "ace/config"], function (require, exports, module) {
"use strict";
var HashHandler = require("./keyboard/hash_handler").HashHandler;
var AcePopup = require("./autocomplete/popup").AcePopup;
var util = require("./autocomplete/util");
var lang = require("./lib/lang");
var dom = require("./lib/dom");
var snippetManager = require("./snippets").snippetManager;
var config = require("./config");
var Autocomplete = function () {
this.autoInsert = false;
this.autoSelect = true;
this.exactMatch = false;
this.gatherCompletionsId = 0;
this.keyboardHandler = new HashHandler();
this.keyboardHandler.bindKeys(this.commands);
this.blurListener = this.blurListener.bind(this);
this.changeListener = this.changeListener.bind(this);
this.mousedownListener = this.mousedownListener.bind(this);
this.mousewheelListener = this.mousewheelListener.bind(this);
this.changeTimer = lang.delayedCall(function () {
this.updateCompletions(true);
}.bind(this));
this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50);
};
(function () {
// TODO ACE自带的提示器接口
this.$init = function () {
this.popup = new AcePopup(document.body || document.documentElement);
this.popup.on("click", function (e) {
this.insertMatch();
e.stop();
}.bind(this));
this.popup.focus = this.editor.focus.bind(this.editor);
this.popup.on("show", this.tooltipTimer.bind(null, null));
this.popup.on("select", this.tooltipTimer.bind(null, null));
this.popup.on("changeHoverMarker", this.tooltipTimer.bind(null, null));
return this.popup;
};
this.getPopup = function () {
return this.popup || this.$init();
};
this.openPopup = function (editor, prefix, keepPopupPosition) {
if (!this.popup)
this.$init();
this.popup.autoSelect = this.autoSelect;
this.popup.setData(this.completions.filtered, this.completions.filterText);
editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
var renderer = editor.renderer;
this.popup.setRow(this.autoSelect ? 0 : -1);
if (!keepPopupPosition) {
this.popup.setTheme(editor.getTheme());
this.popup.setFontSize(editor.getFontSize());
var lineHeight = renderer.layerConfig.lineHeight;
var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
pos.left -= this.popup.getTextLeftOffset();
var rect = editor.container.getBoundingClientRect();
pos.top += rect.top - renderer.layerConfig.offset;
pos.left += rect.left - editor.renderer.scrollLeft;
pos.left += renderer.gutterWidth;
this.popup.show(pos, lineHeight);
} else if (keepPopupPosition && !prefix) {
this.detach();
}
this.changeTimer.cancel();
};
this.detach = function () {
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
this.editor.off("changeSelection", this.changeListener);
this.editor.off("blur", this.blurListener);
this.editor.off("mousedown", this.mousedownListener);
this.editor.off("mousewheel", this.mousewheelListener);
this.changeTimer.cancel();
this.hideDocTooltip();
this.gatherCompletionsId += 1;
if (this.popup && this.popup.isOpen)
this.popup.hide();
if (this.base)
this.base.detach();
this.activated = false;
this.completions = this.base = null;
};
this.changeListener = function (e) {
var cursor = this.editor.selection.lead;
if (!this.base) return;
if (cursor.row != this.base.row || cursor.column < this.base.column) {
this.detach();
}
if (this.activated)
this.changeTimer.schedule();
else
this.detach();
};
this.blurListener = function (e) {
var el = document.activeElement;
var text = this.editor.textInput.getElement();
var fromTooltip = e.relatedTarget && this.tooltipNode && this.tooltipNode.contains(e.relatedTarget);
var container = this.popup && this.popup.container;
if (el != text && el.parentNode != container && !fromTooltip &&
el != this.tooltipNode && e.relatedTarget != text
) {
this.detach();
}
};
this.mousedownListener = function (e) {
this.detach();
};
this.mousewheelListener = function (e) {
this.detach();
};
this.goTo = function (where) {
this.popup.goTo(where);
};
// TODO 代码器插入匹配的代码
this.insertMatch = function (data, options) {
if (!data)
data = this.popup.getData(this.popup.getRow());
if (!data)
return false;
var completions = this.completions;
this.editor.startOperation({
command: {
name: "insertMatch"
}
});
if (data.completer && data.completer.insertMatch) {
data.completer.insertMatch(this.editor, data);
} else {
var ranges = this.editor.selection.getAllRanges();
var pos = this.editor.getCursorPosition();
var prev = this.editor.session.getLine(pos.row).substr(0, pos.column - completions.filterText.length).split("").reverse()[0];
var scopes = (SnippetManager.getActiveScopes(EDITOR) || [""])[0]
if (completions.filterText) {
for (var i = 0, range; range = ranges[i]; i++) {
// TODO 删除当前选择
if (/\./.test(completions.filterText)) {
let arr = completions.filterText.split('.');
range.start.column -= arr[arr.length - 1].length;
} else {
range.start.column -= completions.filterText.length;
}
if (scopes === "css" && /\./.test(completions.filterText)) {
range.start.column -= 1;
}
this.editor.session.remove(range);
}
}
// html删除标签左括号
// if (scopes === "html" && /^[<|《]/g.test(prev) && data.meta === 'label') {
if (scopes === "html" && /^[<|《]/g.test(prev) && ['label', 'user'].includes(data.meta)) {
for (var i = 0, range; range = ranges[i]; i++) {
range.start.column -= 1;
this.editor.session.remove(range);
}
}
if (data.snippet) {
snippetManager.insertSnippet(this.editor, data.snippet, data.values);
} else {
this.editor.execCommand("insertstring", data.value || data);
}
}
if (this.completions == completions)
this.detach();
this.editor.endOperation();
};
// 是否允许网页/桌面所有提示
// TODO 提示器快捷键配置
this.commands = {
"Up": function (editor) {
editor.completer.goTo("up");
},
"Down": function (editor) {
editor.completer.goTo("down");
},
"Ctrl-Up|Ctrl-Home": function (editor) {
editor.completer.goTo("start");
},
"Ctrl-Down|Ctrl-End": function (editor) {
editor.completer.goTo("end");
},
"Esc": function (editor) {
editor.completer.detach();
},
"Return": function (editor) {
return editor.completer.insertMatch();
},
"Shift-Return": function (editor) {
editor.completer.insertMatch(null, {
deleteSuffix: true
});
},
["Tab"]: function (editor) {
var result = editor.completer.insertMatch();
if (!result && !editor.tabstopManager)
editor.completer.goTo("down");
else
return result;
},
"PageUp": function (editor) {
editor.completer.popup.gotoPageUp();
},
"PageDown": function (editor) {
editor.completer.popup.gotoPageDown();
}
};
// TODO 循环所有提示器
this.gatherCompletions = function (editor, callback) {
var session = editor.getSession();
var pos = editor.getCursorPosition();
var prefix = util.getCompletionPrefix(editor) || "";
this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length);
this.base.$insertRight = true;
var scopes = (snippetManager.getActiveScopes(EDITOR) || ["html"])[0];
var matches = [];
var arr = ["内置代码段提示器", "海鸥分析提示器", "采集当前页面单词提示器"];
if (scopes === "javascript") {
arr = ["内置代码段提示器", "海鸥分析提示器", "采集当前页面单词提示器", "关键字提示器"];
}
// 如果没有@,则跳过@关键字提示 || 没有prefix则跳过提示器
var completers = editor.completers.filter((completer, i) => {
if (!/^@/.test(prefix) && completer.name === "@关键字提示器") {
return false
};
if (!prefix && arr.includes(completer.name)) {
return false;
}
return true;
});
var total = completers.length;
// 提示器循环
completers.forEach(function (completer, i) {
completer.getCompletions(editor, session, pos, prefix, function (err, results) {
if (!err && results)
matches = matches.concat(results);
callback(null, {
prefix: prefix ? prefix.replace(/\s+/, "") : "",
matches: matches,
finished: (--total == 0)
});
});
});
return true;
};
this.showPopup = function (editor, options) {
if (this.editor)
this.detach();
this.activated = true;
this.editor = editor;
if (editor.completer != this) {
if (editor.completer)
editor.completer.detach();
editor.completer = this;
}
editor.on("changeSelection", this.changeListener);
editor.on("blur", this.blurListener);
editor.on("mousedown", this.mousedownListener);
editor.on("mousewheel", this.mousewheelListener);
this.updateCompletions(false, options);
};
this.updateCompletions = function (keepPopupPosition, options) {
if (keepPopupPosition && this.base && this.completions) {
var pos = this.editor.getCursorPosition();
var prefix = this.editor.session.getTextRange({
start: this.base,
end: pos
});
if (prefix == this.completions.filterText)
return;
this.completions.setFilter(prefix);
if (!this.completions.filtered.length)
return this.detach();
if (this.completions.filtered.length == 1 &&
this.completions.filtered[0].value == prefix &&
!this.completions.filtered[0].snippet)
return this.detach();
this.openPopup(this.editor, prefix, keepPopupPosition);
return;
}
if (options && options.matches) {
var pos = this.editor.getSelectionRange().start;
this.base = this.editor.session.doc.createAnchor(pos.row, pos.column);
this.base.$insertRight = true;
// TODO 更新选择器数据
this.completions = new FilteredList(options.matches);
// TODO 打开选择器
return this.openPopup(this.editor, "", keepPopupPosition);
}
var _id = this.gatherCompletionsId;
// TODO 这里就是匹配弹出匹配值了
this.gatherCompletions(this.editor, function (err, results) {
if (!results.finished) return;
var detachIfFinished = function () {
if (!results.finished) return;
return this.detach();
}.bind(this);
var prefix = results.prefix;
var matches = results && results.matches;
if (!matches || !matches.length)
return detachIfFinished();
if (prefix.indexOf(results.prefix) !== 0 || _id != this.gatherCompletionsId)
return;
this.completions = new FilteredList(matches);
if (this.exactMatch)
this.completions.exactMatch = true;
// TODO 提示器匹配方法 setFilter
this.completions.setFilter(prefix);
var filtered = this.completions.filtered;
if (!filtered.length)
return detachIfFinished();
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
return detachIfFinished();
if (this.autoInsert && filtered.length == 1 && results.finished)
return this.insertMatch(filtered[0]);
this.openPopup(this.editor, prefix, keepPopupPosition);
}.bind(this));
};
this.cancelContextMenu = function () {
this.editor.$mouseHandler.cancelContextMenu();
};
this.updateDocTooltip = function () {
var popup = this.popup;
var all = popup.data;
var selected = all && (all[popup.getHoveredRow()] || all[popup.getRow()]);
var doc = null;
if (!selected || !this.editor || !this.popup.isOpen)
return this.hideDocTooltip();
this.editor.completers.some(function (completer) {
if (completer.getDocTooltip)
doc = completer.getDocTooltip(selected);
return doc;
});
if (!doc && typeof selected != "string")
doc = selected;
if (typeof doc == "string")
doc = {
docText: doc
};
if (!doc || !(doc.docHTML || doc.docText))
return this.hideDocTooltip();
this.showDocTooltip(doc);
};
// TODO 提示html弹出代码
this.showDocTooltip = function (item) {
if (!this.tooltipNode) {
this.tooltipNode = dom.createElement("div");
this.tooltipNode.className = "ace_tooltip ace_doc-tooltip";
this.tooltipNode.style.margin = 0;
this.tooltipNode.style.pointerEvents = "auto";
this.tooltipNode.tabIndex = -1;
this.tooltipNode.onblur = this.blurListener.bind(this);
this.tooltipNode.onclick = this.onTooltipClick.bind(this);
}
var tooltipNode = this.tooltipNode;
if (item.docHTML) {
tooltipNode.innerHTML = item.docHTML;
} else if (item.docText) {
tooltipNode.textContent = item.docText;
}
if (!tooltipNode.parentNode)
document.body.appendChild(tooltipNode);
var popup = this.popup;
var rect = popup.container.getBoundingClientRect();
tooltipNode.style.top = popup.container.style.top;
tooltipNode.style.bottom = popup.container.style.bottom;
tooltipNode.style.maxWidth = window.innerWidth - rect.left - rect.width - 5 + "px";
tooltipNode.style.maxHeight = window.innerHeight - rect.top - 24 + "px";
tooltipNode.style.overflow = "hidden";
tooltipNode.style.overflowY = "auto";
tooltipNode.style.display = "block";
if (window.innerWidth - rect.right < 320) {
if (rect.left < 320) {
if (popup.isTopdown) {
tooltipNode.style.top = rect.bottom + "px";
tooltipNode.style.left = rect.left + "px";
tooltipNode.style.right = "";
tooltipNode.style.bottom = "";
} else {
tooltipNode.style.top = popup.container.offsetTop - tooltipNode.offsetHeight + "px";
tooltipNode.style.left = rect.left + "px";
tooltipNode.style.right = "";
tooltipNode.style.bottom = "";
}
} else {
tooltipNode.style.right = window.innerWidth - rect.left + "px";
tooltipNode.style.left = "";
tooltipNode.style.maxWidth = rect.left - 5 + "px";
}
} else {
tooltipNode.style.left = (rect.right + 1) + "px";
tooltipNode.style.right = "";
}
// 渲染页面代码
document.querySelectorAll("pre code").forEach(block => Prism.highlightElement(block));
};
this.hideDocTooltip = function () {
this.tooltipTimer.cancel();
if (!this.tooltipNode) return;
var el = this.tooltipNode;
if (!this.editor.isFocused() && document.activeElement == el)
this.editor.focus();
this.tooltipNode = null;
if (el.parentNode)
el.parentNode.removeChild(el);
};
this.onTooltipClick = function (e) {
var a = e.target;
while (a && a != this.tooltipNode) {
if (a.nodeName == "A" && a.href) {
a.rel = "noreferrer";
a.target = "_blank";
break;
}
a = a.parentNode;
}
};
this.destroy = function () {
this.detach();
if (this.popup) {
this.popup.destroy();
var el = this.popup.container;
if (el && el.parentNode)
el.parentNode.removeChild(el);
}
if (this.editor && this.editor.completer == this)
this.editor.completer == null;
this.popup = null;
};
}).call(Autocomplete.prototype);
Autocomplete.for = function (editor) {
if (editor.completer) {
return editor.completer;
}
if (config.get("sharedPopups")) {
if (!Autocomplete.$shared)
Autocomplete.$sharedInstance = new Autocomplete();
editor.completer = Autocomplete.$sharedInstance;
} else {
editor.completer = new Autocomplete();
editor.once("destroy", function (e, editor) {
editor.completer.destroy();
});
}
return editor.completer;
};
Autocomplete.startCommand = {
name: "startAutocomplete",
description: "启动提示器",
exec: function (editor, options) {
var completer = Autocomplete.for(editor);
completer.autoInsert = false;
completer.autoSelect = true;
completer.showPopup(editor, options);
completer.cancelContextMenu();
},
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
};
var FilteredList = function (array, filterText) {
this.all = array;
this.filtered = array;
this.filterText = filterText || "";
this.exactMatch = false;
};
(function () {
// TODO 这里应该是匹配对应的提示
this.setFilter = function (str) {
if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
var matches = this.filtered;
else
var matches = this.all;
this.filterText = str;
this.scopes = (snippetManager.getActiveScopes(EDITOR) || ["html"])[0];
function getLength(str) {
var chineseCharCount = str.match(/[\u4e00-\u9fa5]/g) ? str.match(/[\u4e00-\u9fa5]/g).length : 0;
var englishCharCount = str.length - chineseCharCount;
var length = chineseCharCount * 2 + englishCharCount;
return length;
}
matches = this.filterCompletions(matches, this.filterText);
matches = matches.sort(function (a, b) {
let al = a.title || a.name || a.value || a.snippet || a.caption || "";
let bl = b.title || b.name || b.value || b.snippet || b.caption || "";
let aZhLength = /[\u4e00-\u9fa5]/g.test(al) ? getLength(al) : 20;
let bZhLength = /[\u4e00-\u9fa5]/g.test(bl) ? getLength(bl) : 20;
return (
b.exactMatch - a.exactMatch ||
b.other - a.other ||
aZhLength - bZhLength ||
b.score - a.score ||
al.length - bl.length ||
b.$score - a.$score ||
al.localeCompare(bl)
);
});
let keywords = [] = matches.filter((item, i) => {
if (item.title && /@[\S\s]+/g.test(item.title)) {
delete matches[i];
return true;
}
})
matches = keywords.length > 0 ? [...matches, ...keywords] : matches;
let analysis = [] = matches.filter((item, i) => {
if (item && item.meta && !/\./g.test(item.meta)) {
delete matches[i];
return true;
}
})
matches = analysis.length > 0 ? [...matches, ...analysis] : matches;
var prev = null;
matches = matches.filter(function (item) {
if (item) {
var caption = item.snippet || item.caption || item.value;
if (caption === prev) return false;
prev = caption;
return true;
}
});
this.filtered = matches;
};
// TODO 匹配方法
this.filterCompletions = function (items, needle) {
let needles = [];
let isJavascript = this.scopes === "javascript";
let isCss = this.scopes === "css";
let isHtml = this.scopes === "html";
if (/*needle != "." &&*/ !isCss) {
if (isJavascript) {
needles = needle.split('.');
needle = needles[needles.length - 1];
}
}
// 替换通用匹配符
needle = needle.replace(/\./g, "\\.");
let results = [];
let chineseNeedle = '';
// 是否中英混写
if (/[\u4e00-\u9FA5]/.test(needle)) {
chineseNeedle = pinyin(needle, {
toneType: 'none',
type: 'array'
}).join("");
};
loop: for (var i = 0, item; item = items[i]; i++) {
var caption = item.caption || item.value || item.snippet || item.name;
if (!caption) continue;
var matchMask = 0;
var penalty = 0;
if (isCss) {
if (needle.indexOf("@") !== -1) {
window.keyWordState = null;
};
if (window.keyWordState === "label" && item.meta !== "attr" || window.keyWordState === "label" && item.p_snippet) {
continue loop;
}
if (window.keyWordState === "attr" && item.p_snippet !== window.attrValueParentName) {
continue loop;
};
if (item.score === 99999) {
continue loop;
}
}
if (isJavascript && needles.length > 1) {
if (item.score !== 99999) {
continue loop;
}
}
// 匹配+匹配程度
if (this.exactMatch) {
if (needle !== caption.substr(0, needle.length))
continue loop;
} else {
let captions = caption.split('|');
let isloop = false;
try {
isloop = captions.some(caption => {
let reg = new RegExp('^' + needle, "gi");
let value = reg.exec(caption);
if (value) {
penalty = -(caption.length - reg.lastIndex);
return true;
}
if (chineseNeedle) {
let reg2 = new RegExp('^' + chineseNeedle, "gi");
if (reg2.exec(caption)) {
penalty = -(caption.length - reg2.lastIndex);
return true;
}
}
});
} catch (error) {
}
if (!isloop) {
continue loop;
}
}
// if (!penalty && needle == item.value) {
// continue loop;
// }
item.matchMask = matchMask;
item.exactMatch = (penalty < 0) ? 0 : 1;
item.$score = (item.score || 0) - penalty;
// 提示的规则替换
if (tipsRule && /\(\)/g.test(item.snippet)) {
item.snippet = item.snippet.replace(/\(\)/, '');
}
results.push(item);
};
// 排除关键字与提示重复
if (results.length > 0) {
let resultsArr = [];
results.forEach((v, k) => {
let index = results.findIndex(v1 => v1.caption === v.caption && v1 != v);
if (index !== -1 && results[k]["meta"] !== "关键字" || index === -1) {
resultsArr.push(results[k]);
};
});
return resultsArr;
} else {
return results;
}
};
}).call(FilteredList.prototype);
exports.Autocomplete = Autocomplete;
exports.FilteredList = FilteredList;
});
ace.define("ace/autocomplete/text_completer", ["require", "exports", "module", "ace/range", "ace/snippets", "ace/config"], function (require, exports, module) {
var Range = require("../range").Range;
var snippetManager = require("../snippets").snippetManager;
var splitRegex = /[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/; //通过这个正则实现页面单词分割
/**
* 匹配规则
*/
function getWordIndex(doc, pos) {
var textBefore = doc.getTextRange(Range.fromPoints({
row: 0,
column: 0
}, pos));
return textBefore.split(splitRegex).length - 1;
}
/**
* TODO 提取页面函数
* js单独一个提示
* html的class。id提示通用
* css的提示html可用
*/
function wordDistance(editor, pos) {
let token = EDITOR.session.getTokenAt(pos.row, pos.column);
let scopes = [];
if (token && token.type.match(/(tag-name|tag-open|tag-whitespace|attribute-name|attribute-value)\.xml$/))
scopes = "html";
else
scopes = snippetManager.getActiveScopes(EDITOR)[0];
// getAllPromptCode([{
// id: LAY_ID,
// type: scopes
// }], false, editor.doc.$lines, scopes);
return [];
}
// TODO 这里用的是采集当前页面单词作为提示器+
exports.name = '采集当前页面单词提示器';
exports.getCompletions = function (editor, session, pos, prefix, callback) {
var wordScore = wordDistance(session, pos);
callback(null, wordScore.map(function (word, key) {
return {
id: word['id'] || LAY_ID + key,
title: word['title'] || word['name'],
returnClass: word['ReturnClass'],
className: word["ClassName"],
caption: word['caption'] || word['name'],
snippet: word['body'] || word['name'],
new_caption: word['new_name'] || '',
p_snippet: word['p_name'] || '',
tabTrigger: word['tabTrigger'],
meta: word['meta'] || '',
itemType: word['itemType'] || '',
type: word.type || "snippet",
start: word.start || '',
end: word.end || word.start || '',
score: word.score || 1000,
T_prototype: word.T_prototype || "2",
IfType: word.IfType || 0,
values: word.values ? true : false,
explain: word.explain || "",
description: word.description || "",
parameter: word.parameter || '',
iconClass: word.meta ? (typeIcon[word.meta] || typeIcon["?"]) : '' // 图标
};
}));
};
})
// TODO 这里应该是提示的集合
ace.define("ace/ext/language_tools", ["require", "exports", "module", "ace/snippets", "ace/autocomplete", "ace/config", "ace/lib/lang", "ace/autocomplete/util", "ace/autocomplete/text_completer", "ace/editor", "ace/ext/tern", "ace/config"], function (require, exports, module) {
"use strict";
var snippetManager = require("../snippets").snippetManager;
var Autocomplete = require("../autocomplete").Autocomplete;
var config = require("../config");
var lang = require("../lib/lang");
var util = require("../autocomplete/util");
// 页面的代码提示
var textCompleter = require("../autocomplete/text_completer");
// 关键字提示
var keyWordCompleter = {
name: "关键字提示器",
getCompletions: function (editor, session, pos, prefix, callback) {
if (session.$mode.completer) {
return session.$mode.completer.getCompletions(editor, session, pos, prefix, callback);
}
var state = editor.session.getState(pos.row);
var completions = session.$mode.getCompletions(state, session, pos, prefix);
callback(null, completions);
}
};
// TODO 代码提示片段
var snippetCompleter = {
name: "内置代码段提示器",
getCompletions: function (editor, session, pos, prefix, callback) {
var scopes = [];
var token = session.getTokenAt(pos.row, pos.column);
if (token && token.type.match(/(tag-name|tag-open|tag-whitespace|attribute-name|attribute-value)\.xml$/))
scopes.push('html-tag');
else
scopes = snippetManager.getActiveScopes(editor);
var snippetMap = snippetManager.snippetMap;
var completions = [];
scopes.forEach(function (scope) {
var snippets = snippetMap[scope] || [];
if (scope === "html") {
snippets = snippets.filter(v => v.meta === "label" || v.meta === "user");
}
if (scope === "css") {
snippets = snippets.filter(v => !["label", "attr"].includes(v.meta) || v.meta === "user");
}
for (var i = snippets.length; i--;) {
var s = snippets[i];
var caption = s.caption || s.name || s.tabTrigger;
if (!caption)
continue;
completions.push({
id: s.id || 0, // id
caption: caption, // 查询字段 说明
snippet: s.content, // 代码内容
title: s.title || '', // 标题
title_en: s.title_en || '', // 英文标题
p_snippet: s.p_name, // 父级
description: s.description || '', // 描述
className: s.className || '', // 当前类名
static: s.static || 3, //静态状态
meta: s.meta || '', // 提示类型
returnText: s.returnText || '', // 返回值解释
returnClass: s.returnClass || '', // 返回值类型
parameter: s.parameter || '', // 参数
codeType: s.codeType || 'html', // 代码段类型
type: s.type || "snippet", //
itemType: s.itemType || '', //
score: s.score || 0, // 排序
T_prototype: s.T_prototype || "2", // 是否原型
IfType: s.IfType || 0, // 是否代码段
values: s.values ? true : false, // 提示值 css用的
explain: s.explain || "", //
iconClass: s.meta ? (typeIcon[s.meta] || typeIcon["?"]) : '' // 图标
});
}
}, this);
callback(null, completions);
},
// TODO 编辑器选择代码提示
getDocTooltip: function (item) {
let snippet = item.snippet;
if (/[\u4E00-\u9FA5]/.test(snippet)) {
snippet = snippet + '|' + pinyin_transliteration(snippet);
};
if (item.snippet && !item.docHTML && item.caption !== snippet || item.description) {
let _parameter = [];
let mode = item.codeType || (snippetManager.getActiveScopes(EDITOR) || ["html"])[0];
if (item.parameter) {
let keys = Object.keys(item.parameter);
keys.forEach((v, k) => {
if (item["type"]) {
let reg = new RegExp(`${v}: ([\\S\\s]+?)[,)]`);
let type = item["type"].match(reg)
if (type && type[1]) {
_parameter.push(`<p>参数[<span style="color: #07c;">${typeTips[type[1]] || type[1]}</span>] - ${v}</p><p style="color: #07c;">${item.parameter[v]}</p>`);
} else {
_parameter.push(`<p>参数 - ${v}</p><p style="color: #07c;">${item.parameter[v]}</p>`);
}
} else {
_parameter.push(`<p>参数 - ${v}</p><p style="color: #07c;">${item.parameter[v]}</p>`);
}
});
};
_parameter = _parameter.join("");
// 支持平台
let UA_html = "";
if (item.pt) {
UA_html = "<p><b>支持平台:</b></p><br><div class='layui-btn-container'>"
for (const key in item.pt) {
if (item.pt[key] === "1") {
UA_html += `<button type="button" class="layui-btn layui-btn-normal layui-btn-xs">${key}</button>`;
}
}
UA_html += "</div>"
}
item.docHTML = [
"<p><b>",
lang.escapeHTML(item.title || item.caption),
item.title_en ? "|" + lang.escapeHTML(item.title_en) : "",
item.type ? `<hr><p><b>${this.docStyle(item.type)}</b></p>` : "",
"</b></p>",
_parameter ? "<hr><div style='max-height:150px;overflow-y: auto;'>" + _parameter + "</div>" : "",
item.description ? "<hr><p><b>简介描述:</b></p>" + item.description.replace(/\n/g, "") : '',
(item.explain || item.snippet) ? "<hr><pre class='line-numbers' data-start='1'><code class='language-" + mode + "'>" + lang.escapeHTML(item.explain || item.snippet) + "</code></pre>" : "",
UA_html
].join("");
};
/*
if (item.longSnippet) {
item.docHTML = `<p><b>${this.docStyle(item.longSnippet)}</b></p><hr>`;
};
*/
},
toChineseType(type = '') {
type = type.trim();
if (/\|/g.test(type)) {
let typesArr = type.split("|");
typesArr = typesArr.map(v => {
return typeTips[v] || v
});
return typesArr.join("|");
};
if (/^\[[\S\s]+\]$/g.test(type)) {
let typesArr = /\[([\S\s]+?)\]/.exec(type)[1].split(",");
typesArr = typesArr.map(v => {
return typeTips[v] || v
});
return `[${typesArr.toString()}]`;
}
return typeTips[type] || type;
},
// 给 fn(string:a) 添加样式
docStyle: function (typeText = '') {
if (/\(/g.test(typeText)) {
typeText = typeText.replace(/fn\(([\s\S]*)\)/g, ($1, $2) => {
if (/fn\(([\s\S]*)\)/g.test($2)) {
$2 = this.docStyle($2)
}
let arr = $2.split(",");
if (arr.length > 0) {
arr.forEach((v, i) => {
if (/[\S\s]+?\?*:[\S\s]+/g.test(v)) {
let attr_arr = v.split(":");
attr_arr[0] = `<span class="Ace-Tern-farg">${attr_arr[0]}</span>`;
attr_arr[1] = `<span class="Ace-Tern-type">${this.toChineseType(attr_arr[1])}</span>`;
arr[i] = attr_arr[0] + ':' + attr_arr[1];
}
})
$2 = arr.join(",");
}
return `函数(${$2})`;
})
typeText = typeText.replace(/->\s*([\S\s]+)/g, ($1, $2) => {
return "-> <span class='Ace-Tern-type'>" + this.toChineseType($2) + "</span>";
})
return typeText;
} else {
return this.toChineseType(typeText);
}
// if (text && text[1] || returnType) {
// let arr = (text && text[1]) ? text[1].split(",") : [];
// arr.forEach((v, k) => {
// let attr_arr = v.split(":");
// arr[k] = `<span class="Ace-Tern-farg">${attr_arr[0]}</span>:<span class="Ace-Tern-type">${this.toChineseType(attr_arr[1])}</span>`;
// })
// if (returnType && returnType[1]) {
// return "函数(" + arr.join(",") + ") -> <span class='Ace-Tern-type'>" + this.toChineseType(returnType[1]) + "</span>";
// };
// return "函数(" + arr.join(",") + ")";
// } else {
// return this.toChineseType(typeText);
// }
}
};
// TODO 分析器提示
var completers = [textCompleter, snippetCompleter, keyWordCompleter];
config.loadModule("ace/ext/tern", function (e) {
var ternCompletor = {
name: "海鸥分析提示器",
getCompletions: function (editor, session, pos, prefix, callback) {
var completions = [];
if ((snippetManager.getActiveScopes(editor) || ["html"])[0] === "javascript") {
e.tern_server.getCompletions(editor, session, pos, prefix, callback);
} else {
callback(null, completions);
};
}
};
completers.push(ternCompletor);
});
exports.setCompleters = function (val, newVal) {
let _index = completers.indexOf(val);
if (_index != -1) {
completers.splice(_index, 1, newVal);
} else {
completers.length = 0;
if (val) completers.push.apply(completers, val);
}
};
exports.addCompleter = function (completer) {
completers.push(completer);
};
exports.textCompleter = textCompleter; //页面提示
exports.keyWordCompleter = keyWordCompleter; //关键字提示
exports.snippetCompleter = snippetCompleter; //代码片段提示
exports.snippetManager = snippetManager;
// 选中提示器按键绑定
var expandSnippet = {
name: "expandSnippet",
description: "展开代码段",
exec: function (editor) {
return snippetManager.expandWithTab(editor);
},
bindKey: "Tab"
};
// 更改mode
var onChangeMode = function (e, editor) {
loadSnippetsForMode(editor.session.$mode);
};
var loadSnippetsForMode = function (mode) {
if (typeof mode == "string")
mode = config.$modes[mode];
if (!mode)
return;
if (!snippetManager.files)
snippetManager.files = {};
loadSnippetFile(mode.$id, mode.snippetFileId);
if (mode.$modes) {
for (const modeKey in mode.$modes) {
loadSnippetsForMode(mode.$modes[modeKey])
}
}
// TODO 没有mode.modes这个参数使用的上面
if (mode.modes)
mode.modes.forEach(loadSnippetsForMode);
};
// TODO 这里收集代码段
var loadSnippetFile = function (id, snippetFilePath) {
if (!snippetFilePath || !id || snippetManager.files[id])
return;
snippetManager.files[id] = {};
config.loadModule(snippetFilePath, function (m) {
if (!m) return;
snippetManager.files[id] = m;
if (!m.snippets && m.snippetText)
m.snippets = snippetManager.parseSnippetFile(m.snippetText);
// 插入代码片段
snippetManager.register(m.snippetJson || [], m.scope);
snippetManager.register(m.snippets || [], m.scope);
if (m.includeScopes) {
snippetManager.snippetMap[m.scope].includeScopes = m.includeScopes;
m.includeScopes.forEach(function (x) {
loadSnippetsForMode("ace/mode/" + x);
});
}
});
};
// 这里应该是处理输入是否打开提示器
var old_prefix = null;
var doLiveAutocomplete = function (e) {
// 在键入时不做大型dom渲染
if (window.isInputTime) {
clearTimeout(window.isInputTime)
}
window.isInputTime = setTimeout(() => {
window.isInputTime = null;
}, 500)
var editor = e.editor;
var hasCompleter = editor.completer && editor.completer.activated;
var pos = editor.getSelectionRange().end;
var tok = editor.session.getTokenAt(pos.row, pos.column);
var scopes = (snippetManager.getActiveScopes(editor) || ["html"])[0];
var line = editor.session.getLine(pos.row);
var prefix = util.getCompletionPrefix(editor);
var prevPrefix = line[pos["column"] - 1];
var prevPrefix2 = line[pos["column"] - 2];
// 如果是html的结束符 >,则停止提示器
if (tok && tok.type === "meta.tag.punctuation.tag-close.xml") {
editor.completer && editor.completer.destroy && editor.completer.destroy();
return false;
};
// 连续输入只执行首次,第一次的相同提示词就会获取所有提示,后面的只是筛选
/*
//谨慎:这里不屏蔽可能会出现提示器@样式提权等提示不出来 但是解除屏蔽可能造成卡顿
if (old_prefix && e.args !== "." && typeof prefix === 'string' && prefix.startsWith(old_prefix)) {
old_prefix = prefix;
return false;
} else {
old_prefix = prefix
}
*/
// 打开分析器提示
if (e.command.name === "backspace" || e.command.name === "insertstring") {
if (tok) {
if (["meta.tag.punctuation.tag-open.xml", "string.attribute-value.xml", "text.tag-whitespace.xml", "identifier", "entity.name.function"].includes(tok.type) || scopes === "css" && tok.type === "text" && !/^\s*$/.test(line)) {
try {
e.editor.ternServer.lastAutoCompleteFireTime = null;
} catch (ex) { }
if (e.command.name === "backspace" && tok.type == "string.attribute-value.xml" && ["'", '"'].indexOf(prevPrefix)) {
return false;
}
if (!hasCompleter) {
e.editor.execCommand("startAutocomplete");
}
return false;
}
};
if (e.args === "@" || e.args === "." && scopes != "javascript" || e.args === "." && !/[\r\n\t\s]/g.test(prevPrefix2)) {
e.editor.execCommand("startAutocomplete");
return false;
}
}
// ACE默认事件
if (e.command.name === "backspace") {
if (hasCompleter && !prefix) {
editor.completer.detach();
}
} else if (e.command.name === "insertstring") {
if (prefix && !hasCompleter) {
e.editor.execCommand("startAutocomplete");
}
}
};
// ace方法配置项
var Editor = require("../editor").Editor;
require("../config").defineOptions(Editor.prototype, "editor", {
enableBasicAutocompletion: {
set: function (val) {
if (val) {
if (!this.completers)
this.completers = Array.isArray(val) ? val : completers;
this.commands.addCommand(Autocomplete.startCommand);
} else {
this.commands.removeCommand(Autocomplete.startCommand);
}
},
value: false
},
enableLiveAutocompletion: {
set: function (val) {
if (val) {
if (!this.completers)
this.completers = Array.isArray(val) ? val : completers;
this.commands.on('afterExec', doLiveAutocomplete);
} else {
this.commands.removeListener('afterExec', doLiveAutocomplete);
}
},
value: false
},
// TODO 添加代码段
enableSnippets: {
set: function (val) {
if (val) {
this.commands.addCommand(expandSnippet);
this.on("changeMode", onChangeMode);
onChangeMode(null, this);
} else {
this.commands.removeCommand(expandSnippet);
this.off("changeMode", onChangeMode);
}
},
value: false
}
});
});
(function () {
ace.require(["ace/ext/language_tools"], function (m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();