Updated codemirror to v4.12.
Did a lot of cleanup to limit the size of the plugin. I kept the emmet plugin.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var WORD = /[\w$]+/, RANGE = 500;
|
||||
|
||||
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
|
||||
var word = options && options.word || WORD;
|
||||
var range = options && options.range || RANGE;
|
||||
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
|
||||
var end = cur.ch, start = end;
|
||||
while (start && word.test(curLine.charAt(start - 1))) --start;
|
||||
var curWord = start != end && curLine.slice(start, end);
|
||||
|
||||
var list = [], seen = {};
|
||||
var re = new RegExp(word.source, "g");
|
||||
for (var dir = -1; dir <= 1; dir += 2) {
|
||||
var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
|
||||
for (; line != endLine; line += dir) {
|
||||
var text = editor.getLine(line), m;
|
||||
while (m = re.exec(text)) {
|
||||
if (line == cur.line && m[0] === curWord) continue;
|
||||
if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
|
||||
seen[m[0]] = true;
|
||||
list.push(m[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("../../mode/css/css"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../../mode/css/css"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
|
||||
"first-letter": 1, "first-line": 1, "first-child": 1,
|
||||
before: 1, after: 1, lang: 1};
|
||||
|
||||
CodeMirror.registerHelper("hint", "css", function(cm) {
|
||||
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
||||
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
||||
if (inner.mode.name != "css") return;
|
||||
|
||||
var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
|
||||
if (/[^\w$_-]/.test(word)) {
|
||||
word = ""; start = end = cur.ch;
|
||||
}
|
||||
|
||||
var spec = CodeMirror.resolveMode("text/css");
|
||||
|
||||
var result = [];
|
||||
function add(keywords) {
|
||||
for (var name in keywords)
|
||||
if (!word || name.lastIndexOf(word, 0) == 0)
|
||||
result.push(name);
|
||||
}
|
||||
|
||||
var st = inner.state.state;
|
||||
if (st == "pseudo" || token.type == "variable-3") {
|
||||
add(pseudoClasses);
|
||||
} else if (st == "block" || st == "maybeprop") {
|
||||
add(spec.propertyKeywords);
|
||||
} else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
|
||||
add(spec.valueKeywords);
|
||||
add(spec.colorKeywords);
|
||||
} else if (st == "media" || st == "media_parens") {
|
||||
add(spec.mediaTypes);
|
||||
add(spec.mediaFeatures);
|
||||
}
|
||||
|
||||
if (result.length) return {
|
||||
list: result,
|
||||
from: CodeMirror.Pos(cur.line, start),
|
||||
to: CodeMirror.Pos(cur.line, end)
|
||||
};
|
||||
});
|
||||
});
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
mod(require("../../lib/codemirror"), require("./xml-hint"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
define(["../../lib/codemirror", "./xml-hint"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
@@ -27,15 +30,20 @@
|
||||
|
||||
function scriptHint(editor, keywords, getToken, options) {
|
||||
// Find the token at the cursor
|
||||
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
||||
var cur = editor.getCursor(), token = getToken(editor, cur);
|
||||
if (/\b(?:string|comment)\b/.test(token.type)) return;
|
||||
token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
|
||||
|
||||
// If it's not a 'word-style' token, ignore the token.
|
||||
if (!/^[\w$_]*$/.test(token.string)) {
|
||||
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
||||
type: token.string == "." ? "property" : null};
|
||||
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
||||
type: token.string == "." ? "property" : null};
|
||||
} else if (token.end > cur.ch) {
|
||||
token.end = cur.ch;
|
||||
token.string = token.string.slice(0, cur.ch - token.start);
|
||||
}
|
||||
|
||||
var tprop = token;
|
||||
// If it is a property, find out what it is a property of.
|
||||
while (tprop.type == "property") {
|
||||
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
||||
@@ -90,7 +98,7 @@
|
||||
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
||||
|
||||
function getCompletions(token, context, keywords, options) {
|
||||
var found = [], start = token.string;
|
||||
var found = [], start = token.string, global = options && options.globalScope || window;
|
||||
function maybeAdd(str) {
|
||||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
|
||||
}
|
||||
@@ -108,27 +116,29 @@
|
||||
if (obj.type && obj.type.indexOf("variable") === 0) {
|
||||
if (options && options.additionalContext)
|
||||
base = options.additionalContext[obj.string];
|
||||
base = base || window[obj.string];
|
||||
if (!options || options.useGlobalScope !== false)
|
||||
base = base || global[obj.string];
|
||||
} else if (obj.type == "string") {
|
||||
base = "";
|
||||
} else if (obj.type == "atom") {
|
||||
base = 1;
|
||||
} else if (obj.type == "function") {
|
||||
if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
||||
(typeof window.jQuery == 'function'))
|
||||
base = window.jQuery();
|
||||
else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
|
||||
base = window._();
|
||||
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
||||
(typeof global.jQuery == 'function'))
|
||||
base = global.jQuery();
|
||||
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
|
||||
base = global._();
|
||||
}
|
||||
while (base != null && context.length)
|
||||
base = base[context.pop().string];
|
||||
if (base != null) gatherCompletions(base);
|
||||
} else {
|
||||
// If not, just look in the window object and any local scope
|
||||
// If not, just look in the global object and any local scope
|
||||
// (reading into JS mode internals to get at the local and global variables)
|
||||
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
||||
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
|
||||
gatherCompletions(window);
|
||||
if (!options || options.useGlobalScope !== false)
|
||||
gatherCompletions(global);
|
||||
forEach(keywords, maybeAdd);
|
||||
}
|
||||
return found;
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
function forEach(arr, f) {
|
||||
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
||||
}
|
||||
|
||||
function arrayContains(arr, item) {
|
||||
if (!Array.prototype.indexOf) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (arr[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return arr.indexOf(item) != -1;
|
||||
}
|
||||
|
||||
function scriptHint(editor, _keywords, getToken) {
|
||||
// Find the token at the cursor
|
||||
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
||||
// If it's not a 'word-style' token, ignore the token.
|
||||
|
||||
if (!/^[\w$_]*$/.test(token.string)) {
|
||||
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
||||
className: token.string == ":" ? "python-type" : null};
|
||||
}
|
||||
|
||||
if (!context) var context = [];
|
||||
context.push(tprop);
|
||||
|
||||
var completionList = getCompletions(token, context);
|
||||
completionList = completionList.sort();
|
||||
|
||||
return {list: completionList,
|
||||
from: CodeMirror.Pos(cur.line, token.start),
|
||||
to: CodeMirror.Pos(cur.line, token.end)};
|
||||
}
|
||||
|
||||
function pythonHint(editor) {
|
||||
return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
|
||||
}
|
||||
CodeMirror.registerHelper("hint", "python", pythonHint);
|
||||
|
||||
var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
|
||||
+ "break except import print class exec in raise continue finally is return def for lambda try";
|
||||
var pythonKeywordsL = pythonKeywords.split(" ");
|
||||
var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
|
||||
|
||||
var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
|
||||
+ "any eval isinstance pow sum basestring execfile issubclass print super"
|
||||
+ "bin file iter property tuple bool filter len range type"
|
||||
+ "bytearray float list raw_input unichr callable format locals reduce unicode"
|
||||
+ "chr frozenset long reload vars classmethod getattr map repr xrange"
|
||||
+ "cmp globals max reversed zip compile hasattr memoryview round __import__"
|
||||
+ "complex hash min set apply delattr help next setattr buffer"
|
||||
+ "dict hex object slice coerce dir id oct sorted intern ";
|
||||
var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
|
||||
var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
|
||||
|
||||
function getCompletions(token, context) {
|
||||
var found = [], start = token.string;
|
||||
function maybeAdd(str) {
|
||||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
|
||||
}
|
||||
|
||||
function gatherCompletions(_obj) {
|
||||
forEach(pythonBuiltinsL, maybeAdd);
|
||||
forEach(pythonBuiltinsU, maybeAdd);
|
||||
forEach(pythonKeywordsL, maybeAdd);
|
||||
forEach(pythonKeywordsU, maybeAdd);
|
||||
}
|
||||
|
||||
if (context) {
|
||||
// If this is a property, see if it belongs to some object we can
|
||||
// find in the current environment.
|
||||
var obj = context.pop(), base;
|
||||
|
||||
if (obj.type == "variable")
|
||||
base = obj.string;
|
||||
else if(obj.type == "variable-3")
|
||||
base = ":" + obj.string;
|
||||
|
||||
while (base != null && context.length)
|
||||
base = base[context.pop().string];
|
||||
if (base != null) gatherCompletions(base);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
});
|
||||
@@ -32,7 +32,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.CodeMirror-hint-active {
|
||||
li.CodeMirror-hint-active {
|
||||
background: #08f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
+83
-43
@@ -1,3 +1,6 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
@@ -11,28 +14,35 @@
|
||||
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
|
||||
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
|
||||
|
||||
// This is the old interface, kept around for now to stay
|
||||
// backwards-compatible.
|
||||
CodeMirror.showHint = function(cm, getHints, options) {
|
||||
// We want a single cursor position.
|
||||
if (cm.listSelections().length > 1 || cm.somethingSelected()) return;
|
||||
if (getHints == null) {
|
||||
if (options && options.async) return;
|
||||
else getHints = CodeMirror.hint.auto;
|
||||
}
|
||||
|
||||
if (cm.state.completionActive) cm.state.completionActive.close();
|
||||
|
||||
var completion = cm.state.completionActive = new Completion(cm, getHints, options || {});
|
||||
CodeMirror.signal(cm, "startCompletion", cm);
|
||||
if (completion.options.async)
|
||||
getHints(cm, function(hints) { completion.showHints(hints); }, completion.options);
|
||||
else
|
||||
return completion.showHints(getHints(cm, completion.options));
|
||||
if (!getHints) return cm.showHint(options);
|
||||
if (options && options.async) getHints.async = true;
|
||||
var newOpts = {hint: getHints};
|
||||
if (options) for (var prop in options) newOpts[prop] = options[prop];
|
||||
return cm.showHint(newOpts);
|
||||
};
|
||||
|
||||
function Completion(cm, getHints, options) {
|
||||
CodeMirror.defineExtension("showHint", function(options) {
|
||||
// We want a single cursor position.
|
||||
if (this.listSelections().length > 1 || this.somethingSelected()) return;
|
||||
|
||||
if (this.state.completionActive) this.state.completionActive.close();
|
||||
var completion = this.state.completionActive = new Completion(this, options);
|
||||
var getHints = completion.options.hint;
|
||||
if (!getHints) return;
|
||||
|
||||
CodeMirror.signal(this, "startCompletion", this);
|
||||
if (getHints.async)
|
||||
getHints(this, function(hints) { completion.showHints(hints); }, completion.options);
|
||||
else
|
||||
return completion.showHints(getHints(this, completion.options));
|
||||
});
|
||||
|
||||
function Completion(cm, options) {
|
||||
this.cm = cm;
|
||||
this.getHints = getHints;
|
||||
this.options = options;
|
||||
this.options = this.buildOptions(options);
|
||||
this.widget = this.onClose = null;
|
||||
}
|
||||
|
||||
@@ -53,7 +63,8 @@
|
||||
pick: function(data, i) {
|
||||
var completion = data.list[i];
|
||||
if (completion.hint) completion.hint(this.cm, data, completion);
|
||||
else this.cm.replaceRange(getText(completion), completion.from||data.from, completion.to||data.to);
|
||||
else this.cm.replaceRange(getText(completion), completion.from || data.from,
|
||||
completion.to || data.to, "complete");
|
||||
CodeMirror.signal(data, "pick", completion);
|
||||
this.close();
|
||||
},
|
||||
@@ -61,7 +72,7 @@
|
||||
showHints: function(data) {
|
||||
if (!data || !data.list.length || !this.active()) return this.close();
|
||||
|
||||
if (this.options.completeSingle != false && data.list.length == 1)
|
||||
if (this.options.completeSingle && data.list.length == 1)
|
||||
this.pick(data, 0);
|
||||
else
|
||||
this.showWidget(data);
|
||||
@@ -72,7 +83,7 @@
|
||||
CodeMirror.signal(data, "shown");
|
||||
|
||||
var debounce = 0, completion = this, finished;
|
||||
var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/;
|
||||
var closeOn = this.options.closeCharacters;
|
||||
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
|
||||
|
||||
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
|
||||
@@ -91,15 +102,17 @@
|
||||
function update() {
|
||||
if (finished) return;
|
||||
CodeMirror.signal(data, "update");
|
||||
if (completion.options.async)
|
||||
completion.getHints(completion.cm, finishUpdate, completion.options);
|
||||
var getHints = completion.options.hint;
|
||||
if (getHints.async)
|
||||
getHints(completion.cm, finishUpdate, completion.options);
|
||||
else
|
||||
finishUpdate(completion.getHints(completion.cm, completion.options));
|
||||
finishUpdate(getHints(completion.cm, completion.options));
|
||||
}
|
||||
function finishUpdate(data_) {
|
||||
data = data_;
|
||||
if (finished) return;
|
||||
if (!data || !data.list.length) return done();
|
||||
if (completion.widget) completion.widget.close();
|
||||
completion.widget = new Widget(completion, data);
|
||||
}
|
||||
|
||||
@@ -124,6 +137,17 @@
|
||||
}
|
||||
this.cm.on("cursorActivity", activity);
|
||||
this.onClose = done;
|
||||
},
|
||||
|
||||
buildOptions: function(options) {
|
||||
var editor = this.cm.options.hintOptions;
|
||||
var out = {};
|
||||
for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
|
||||
if (editor) for (var prop in editor)
|
||||
if (editor[prop] !== undefined) out[prop] = editor[prop];
|
||||
if (options) for (var prop in options)
|
||||
if (options[prop] !== undefined) out[prop] = options[prop];
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,7 +156,7 @@
|
||||
else return completion.text;
|
||||
}
|
||||
|
||||
function buildKeyMap(options, handle) {
|
||||
function buildKeyMap(completion, handle) {
|
||||
var baseMap = {
|
||||
Up: function() {handle.moveFocus(-1);},
|
||||
Down: function() {handle.moveFocus(1);},
|
||||
@@ -144,7 +168,8 @@
|
||||
Tab: handle.pick,
|
||||
Esc: handle.close
|
||||
};
|
||||
var ourMap = options.customKeys ? {} : baseMap;
|
||||
var custom = completion.options.customKeys;
|
||||
var ourMap = custom ? {} : baseMap;
|
||||
function addBinding(key, val) {
|
||||
var bound;
|
||||
if (typeof val != "string")
|
||||
@@ -156,12 +181,13 @@
|
||||
bound = val;
|
||||
ourMap[key] = bound;
|
||||
}
|
||||
if (options.customKeys)
|
||||
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key))
|
||||
addBinding(key, options.customKeys[key]);
|
||||
if (options.extraKeys)
|
||||
for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key))
|
||||
addBinding(key, options.extraKeys[key]);
|
||||
if (custom)
|
||||
for (var key in custom) if (custom.hasOwnProperty(key))
|
||||
addBinding(key, custom[key]);
|
||||
var extra = completion.options.extraKeys;
|
||||
if (extra)
|
||||
for (var key in extra) if (extra.hasOwnProperty(key))
|
||||
addBinding(key, extra[key]);
|
||||
return ourMap;
|
||||
}
|
||||
|
||||
@@ -175,11 +201,11 @@
|
||||
function Widget(completion, data) {
|
||||
this.completion = completion;
|
||||
this.data = data;
|
||||
var widget = this, cm = completion.cm, options = completion.options;
|
||||
var widget = this, cm = completion.cm;
|
||||
|
||||
var hints = this.hints = document.createElement("ul");
|
||||
hints.className = "CodeMirror-hints";
|
||||
this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0;
|
||||
this.selectedHint = data.selectedHint || 0;
|
||||
|
||||
var completions = data.list;
|
||||
for (var i = 0; i < completions.length; ++i) {
|
||||
@@ -192,19 +218,19 @@
|
||||
elt.hintId = i;
|
||||
}
|
||||
|
||||
var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
|
||||
var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
|
||||
var left = pos.left, top = pos.bottom, below = true;
|
||||
hints.style.left = left + "px";
|
||||
hints.style.top = top + "px";
|
||||
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
|
||||
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
|
||||
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
|
||||
(options.container || document.body).appendChild(hints);
|
||||
(completion.options.container || document.body).appendChild(hints);
|
||||
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
|
||||
if (overlapY > 0) {
|
||||
var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top);
|
||||
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
|
||||
if (curTop - height > 0) { // Fits above cursor
|
||||
hints.style.top = (top = curTop - height) + "px";
|
||||
hints.style.top = (top = pos.top - height) + "px";
|
||||
below = false;
|
||||
} else if (height > winH) {
|
||||
hints.style.height = (winH - 5) + "px";
|
||||
@@ -217,7 +243,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var overlapX = box.left - winW;
|
||||
var overlapX = box.right - winW;
|
||||
if (overlapX > 0) {
|
||||
if (box.right - box.left > winW) {
|
||||
hints.style.width = (winW - 5) + "px";
|
||||
@@ -226,7 +252,7 @@
|
||||
hints.style.left = (left = pos.left - overlapX) + "px";
|
||||
}
|
||||
|
||||
cm.addKeyMap(this.keyMap = buildKeyMap(options, {
|
||||
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
|
||||
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
|
||||
setFocus: function(n) { widget.changeActive(n); },
|
||||
menuSize: function() { return widget.screenAmount(); },
|
||||
@@ -236,7 +262,7 @@
|
||||
data: data
|
||||
}));
|
||||
|
||||
if (options.closeOnUnfocus !== false) {
|
||||
if (completion.options.closeOnUnfocus) {
|
||||
var closingOnBlur;
|
||||
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
|
||||
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
|
||||
@@ -262,7 +288,7 @@
|
||||
var t = getHintElement(hints, e.target || e.srcElement);
|
||||
if (t && t.hintId != null) {
|
||||
widget.changeActive(t.hintId);
|
||||
if (options.completeOnSingleClick) widget.pick();
|
||||
if (completion.options.completeOnSingleClick) widget.pick();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -282,7 +308,7 @@
|
||||
this.completion.cm.removeKeyMap(this.keyMap);
|
||||
|
||||
var cm = this.completion.cm;
|
||||
if (this.completion.options.closeOnUnfocus !== false) {
|
||||
if (this.completion.options.closeOnUnfocus) {
|
||||
cm.off("blur", this.onBlur);
|
||||
cm.off("focus", this.onFocus);
|
||||
}
|
||||
@@ -346,4 +372,18 @@
|
||||
});
|
||||
|
||||
CodeMirror.commands.autocomplete = CodeMirror.showHint;
|
||||
|
||||
var defaultOptions = {
|
||||
hint: CodeMirror.hint.auto,
|
||||
completeSingle: true,
|
||||
alignWithWord: true,
|
||||
closeCharacters: /[\s()\[\]{};:>,]/,
|
||||
closeOnUnfocus: true,
|
||||
completeOnSingleClick: false,
|
||||
container: null,
|
||||
customKeys: null,
|
||||
extraKeys: null
|
||||
};
|
||||
|
||||
CodeMirror.defineOption("hintOptions", null);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var tables;
|
||||
var defaultTable;
|
||||
var keywords;
|
||||
var CONS = {
|
||||
QUERY_DIV: ";",
|
||||
ALIAS_KEYWORD: "AS"
|
||||
};
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
function getKeywords(editor) {
|
||||
var mode = editor.doc.modeOption;
|
||||
if (mode === "sql") mode = "text/x-sql";
|
||||
return CodeMirror.resolveMode(mode).keywords;
|
||||
}
|
||||
|
||||
function match(string, word) {
|
||||
var len = string.length;
|
||||
var sub = word.substr(0, len);
|
||||
return string.toUpperCase() === sub.toUpperCase();
|
||||
}
|
||||
|
||||
function addMatches(result, search, wordlist, formatter) {
|
||||
for (var word in wordlist) {
|
||||
if (!wordlist.hasOwnProperty(word)) continue;
|
||||
if (Array.isArray(wordlist)) {
|
||||
word = wordlist[word];
|
||||
}
|
||||
if (match(search, word)) {
|
||||
result.push(formatter(word));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function nameCompletion(cur, token, result, editor) {
|
||||
var useBacktick = (token.string.charAt(0) == "`");
|
||||
var string = token.string.substr(1);
|
||||
var prevToken = editor.getTokenAt(Pos(cur.line, token.start));
|
||||
if (token.string.charAt(0) == "." || prevToken.string == "."){
|
||||
//Suggest colunm names
|
||||
if (prevToken.string == ".") {
|
||||
var prevToken = editor.getTokenAt(Pos(cur.line, token.start - 1));
|
||||
}
|
||||
var table = prevToken.string;
|
||||
//Check if backtick is used in table name. If yes, use it for columns too.
|
||||
var useBacktickTable = false;
|
||||
if (table.match(/`/g)) {
|
||||
useBacktickTable = true;
|
||||
table = table.replace(/`/g, "");
|
||||
}
|
||||
//Check if table is available. If not, find table by Alias
|
||||
if (!tables.hasOwnProperty(table))
|
||||
table = findTableByAlias(table, editor);
|
||||
var columns = tables[table];
|
||||
if (!columns) return;
|
||||
|
||||
if (useBacktick) {
|
||||
addMatches(result, string, columns, function(w) {return "`" + w + "`";});
|
||||
}
|
||||
else if(useBacktickTable) {
|
||||
addMatches(result, string, columns, function(w) {return ".`" + w + "`";});
|
||||
}
|
||||
else {
|
||||
addMatches(result, string, columns, function(w) {return "." + w;});
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Suggest table names or colums in defaultTable
|
||||
while (token.start && string.charAt(0) == ".") {
|
||||
token = editor.getTokenAt(Pos(cur.line, token.start - 1));
|
||||
string = token.string + string;
|
||||
}
|
||||
if (useBacktick) {
|
||||
addMatches(result, string, tables, function(w) {return "`" + w + "`";});
|
||||
addMatches(result, string, defaultTable, function(w) {return "`" + w + "`";});
|
||||
}
|
||||
else {
|
||||
addMatches(result, string, tables, function(w) {return w;});
|
||||
addMatches(result, string, defaultTable, function(w) {return w;});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function eachWord(lineText, f) {
|
||||
if (!lineText) return;
|
||||
var excepted = /[,;]/g;
|
||||
var words = lineText.split(" ");
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
f(words[i]?words[i].replace(excepted, '') : '');
|
||||
}
|
||||
}
|
||||
|
||||
function convertCurToNumber(cur) {
|
||||
// max characters of a line is 999,999.
|
||||
return cur.line + cur.ch / Math.pow(10, 6);
|
||||
}
|
||||
|
||||
function convertNumberToCur(num) {
|
||||
return Pos(Math.floor(num), +num.toString().split('.').pop());
|
||||
}
|
||||
|
||||
function findTableByAlias(alias, editor) {
|
||||
var doc = editor.doc;
|
||||
var fullQuery = doc.getValue();
|
||||
var aliasUpperCase = alias.toUpperCase();
|
||||
var previousWord = "";
|
||||
var table = "";
|
||||
var separator = [];
|
||||
var validRange = {
|
||||
start: Pos(0, 0),
|
||||
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
|
||||
};
|
||||
|
||||
//add separator
|
||||
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
|
||||
while(indexOfSeparator != -1) {
|
||||
separator.push(doc.posFromIndex(indexOfSeparator));
|
||||
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
|
||||
}
|
||||
separator.unshift(Pos(0, 0));
|
||||
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
|
||||
|
||||
//find valid range
|
||||
var prevItem = 0;
|
||||
var current = convertCurToNumber(editor.getCursor());
|
||||
for (var i=0; i< separator.length; i++) {
|
||||
var _v = convertCurToNumber(separator[i]);
|
||||
if (current > prevItem && current <= _v) {
|
||||
validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
|
||||
break;
|
||||
}
|
||||
prevItem = _v;
|
||||
}
|
||||
|
||||
var query = doc.getRange(validRange.start, validRange.end, false);
|
||||
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
var lineText = query[i];
|
||||
eachWord(lineText, function(word) {
|
||||
var wordUpperCase = word.toUpperCase();
|
||||
if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
|
||||
table = previousWord;
|
||||
}
|
||||
if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
|
||||
previousWord = word;
|
||||
}
|
||||
});
|
||||
if (table) break;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
|
||||
tables = (options && options.tables) || {};
|
||||
var defaultTableName = options && options.defaultTable;
|
||||
defaultTable = (defaultTableName && tables[defaultTableName] || []);
|
||||
keywords = keywords || getKeywords(editor);
|
||||
|
||||
var cur = editor.getCursor();
|
||||
var result = [];
|
||||
var token = editor.getTokenAt(cur), start, end, search;
|
||||
if (token.end > cur.ch) {
|
||||
token.end = cur.ch;
|
||||
token.string = token.string.slice(0, cur.ch - token.start);
|
||||
}
|
||||
|
||||
if (token.string.match(/^[.`\w@]\w*$/)) {
|
||||
search = token.string;
|
||||
start = token.start;
|
||||
end = token.end;
|
||||
} else {
|
||||
start = end = cur.ch;
|
||||
search = "";
|
||||
}
|
||||
if (search.charAt(0) == "." || search.charAt(0) == "`") {
|
||||
nameCompletion(cur, token, result, editor);
|
||||
} else {
|
||||
addMatches(result, search, tables, function(w) {return w;});
|
||||
addMatches(result, search, defaultTable, function(w) {return w;});
|
||||
addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
|
||||
}
|
||||
|
||||
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
|
||||
});
|
||||
});
|
||||
+48
-13
@@ -1,3 +1,6 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
@@ -15,30 +18,55 @@
|
||||
var quote = (options && options.quoteChar) || '"';
|
||||
if (!tags) return;
|
||||
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
||||
if (token.end > cur.ch) {
|
||||
token.end = cur.ch;
|
||||
token.string = token.string.slice(0, cur.ch - token.start);
|
||||
}
|
||||
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
||||
if (inner.mode.name != "xml") return;
|
||||
var result = [], replaceToken = false, prefix;
|
||||
var isTag = token.string.charAt(0) == "<";
|
||||
if (!inner.state.tagName || isTag) { // Tag completion
|
||||
if (isTag) {
|
||||
prefix = token.string.slice(1);
|
||||
replaceToken = true;
|
||||
}
|
||||
var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
|
||||
var tagName = tag && /^\w/.test(token.string), tagStart;
|
||||
|
||||
if (tagName) {
|
||||
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
|
||||
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
|
||||
if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
|
||||
} else if (tag && token.string == "<") {
|
||||
tagType = "open";
|
||||
} else if (tag && token.string == "</") {
|
||||
tagType = "close";
|
||||
}
|
||||
|
||||
if (!tag && !inner.state.tagName || tagType) {
|
||||
if (tagName)
|
||||
prefix = token.string;
|
||||
replaceToken = tagType;
|
||||
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
|
||||
var childList = cx ? curTag && curTag.children : tags["!top"];
|
||||
if (childList) {
|
||||
if (childList && tagType != "close") {
|
||||
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0)
|
||||
result.push("<" + childList[i]);
|
||||
} else {
|
||||
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
|
||||
result.push("<" + name);
|
||||
} else if (tagType != "close") {
|
||||
for (var name in tags)
|
||||
if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
|
||||
result.push("<" + name);
|
||||
}
|
||||
if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0))
|
||||
if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0))
|
||||
result.push("</" + cx.tagName + ">");
|
||||
} else {
|
||||
// Attribute completion
|
||||
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
|
||||
if (!attrs) return;
|
||||
var globalAttrs = tags["!attrs"];
|
||||
if (!attrs && !globalAttrs) return;
|
||||
if (!attrs) {
|
||||
attrs = globalAttrs;
|
||||
} else if (globalAttrs) { // Combine tag-local and global attributes
|
||||
var set = {};
|
||||
for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
|
||||
for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
|
||||
attrs = set;
|
||||
}
|
||||
if (token.type == "string" || token.string == "=") { // A value
|
||||
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
|
||||
Pos(cur.line, token.type == "string" ? token.start : token.end));
|
||||
@@ -47,9 +75,16 @@
|
||||
if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
|
||||
if (token.type == "string") {
|
||||
prefix = token.string;
|
||||
var n = 0;
|
||||
if (/['"]/.test(token.string.charAt(0))) {
|
||||
quote = token.string.charAt(0);
|
||||
prefix = token.string.slice(1);
|
||||
n++;
|
||||
}
|
||||
var len = token.string.length;
|
||||
if (/['"]/.test(token.string.charAt(len - 1))) {
|
||||
quote = token.string.charAt(len - 1);
|
||||
prefix = token.string.substr(n, len - 2);
|
||||
}
|
||||
replaceToken = true;
|
||||
}
|
||||
@@ -66,7 +101,7 @@
|
||||
}
|
||||
return {
|
||||
list: result,
|
||||
from: replaceToken ? Pos(cur.line, token.start) : cur,
|
||||
from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
|
||||
to: replaceToken ? Pos(cur.line, token.end) : cur
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user