Skip to content
Permalink
bd74eddf10
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
661 lines (597 sloc) 18.4 KB
// @ts-nocheck
// Because this code is originally javascript code.
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// declare global: DOMRect
"use strict";
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) {
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);
};
CodeMirror.defineExtension("showHint", function (options) {
options = parseOptions(this, this.getCursor("start"), options);
var selections = this.listSelections();
if (selections.length > 1) return;
// By default, don't allow completion when something is selected.
// A hint function can have a `supportsSelection` property to
// indicate that it can handle selections.
if (this.somethingSelected()) {
if (!options.hint.supportsSelection) return;
// Don't try with cross-line selections
for (var i = 0; i < selections.length; i++)
if (selections[i].head.line != selections[i].anchor.line) return;
}
if (this.state.completionActive) this.state.completionActive.close();
var completion = (this.state.completionActive = new Completion(
this,
options
));
if (!completion.options.hint) return;
CodeMirror.signal(this, "startCompletion", this);
completion.update(true);
});
CodeMirror.defineExtension("closeHint", function () {
if (this.state.completionActive) this.state.completionActive.close();
});
function Completion(cm, options) {
this.cm = cm;
this.options = options;
this.widget = null;
this.debounce = 0;
this.tick = 0;
this.startPos = this.cm.getCursor("start");
this.startLen =
this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
if (this.options.updateOnCursorActivity) {
var self = this;
cm.on(
"cursorActivity",
(this.activityFunc = function () {
self.cursorActivity();
})
);
}
}
var requestAnimationFrame =
window.requestAnimationFrame ||
function (fn) {
return setTimeout(fn, 1000 / 60);
};
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
Completion.prototype = {
close: function () {
if (!this.active()) return;
this.cm.state.completionActive = null;
this.tick = null;
if (this.options.updateOnCursorActivity) {
this.cm.off("cursorActivity", this.activityFunc);
}
if (this.widget && this.data) CodeMirror.signal(this.data, "close");
if (this.widget) this.widget.close();
CodeMirror.signal(this.cm, "endCompletion", this.cm);
},
active: function () {
return this.cm.state.completionActive == this;
},
pick: function (data, i) {
var completion = data.list[i],
self = this;
this.cm.operation(function () {
if (completion.hint) completion.hint(self.cm, data, completion);
else
self.cm.replaceRange(
getText(completion),
completion.from || data.from,
completion.to || data.to,
"complete"
);
CodeMirror.signal(data, "pick", completion);
self.cm.scrollIntoView();
});
if (this.options.closeOnPick) {
this.close();
}
},
cursorActivity: function () {
if (this.debounce) {
cancelAnimationFrame(this.debounce);
this.debounce = 0;
}
var identStart = this.startPos;
if (this.data) {
identStart = this.data.from;
}
var pos = this.cm.getCursor(),
line = this.cm.getLine(pos.line);
if (
pos.line != this.startPos.line ||
line.length - pos.ch != this.startLen - this.startPos.ch ||
pos.ch < identStart.ch ||
this.cm.somethingSelected() ||
!pos.ch ||
this.options.closeCharacters.test(line.charAt(pos.ch - 1))
) {
this.close();
} else {
var self = this;
this.debounce = requestAnimationFrame(function () {
self.update();
});
if (this.widget) this.widget.disable();
}
},
update: function (first) {
if (this.tick == null) return;
var self = this,
myTick = ++this.tick;
fetchHints(this.options.hint, this.cm, this.options, function (data) {
if (self.tick == myTick) self.finishUpdate(data, first);
});
},
finishUpdate: function (data, first) {
if (this.data) CodeMirror.signal(this.data, "update");
var picked =
(this.widget && this.widget.picked) ||
(first && this.options.completeSingle);
if (this.widget) this.widget.close();
this.data = data;
if (data && data.list.length) {
if (picked && data.list.length == 1) {
this.pick(data, 0);
} else {
this.widget = new Widget(this, data);
CodeMirror.signal(data, "shown");
}
}
},
};
function parseOptions(cm, pos, options) {
var editor = 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];
if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos);
return out;
}
function getText(completion) {
if (typeof completion == "string") return completion;
else return completion.text;
}
function buildKeyMap(completion, handle) {
var baseMap = {
Up: function () {
handle.moveFocus(-1);
},
Down: function () {
handle.moveFocus(1);
},
PageUp: function () {
handle.moveFocus(-handle.menuSize() + 1, true);
},
PageDown: function () {
handle.moveFocus(handle.menuSize() - 1, true);
},
Home: function () {
handle.setFocus(0);
},
End: function () {
handle.setFocus(handle.length - 1);
},
Enter: handle.pick,
Tab: handle.pick,
Esc: handle.close,
};
var mac = /Mac/.test(navigator.platform);
if (mac) {
baseMap["Ctrl-P"] = function () {
handle.moveFocus(-1);
};
baseMap["Ctrl-N"] = function () {
handle.moveFocus(1);
};
}
var custom = completion.options.customKeys;
var ourMap = custom ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
bound = function (cm) {
return val(cm, handle);
};
// This mechanism is deprecated
else if (baseMap.hasOwnProperty(val)) bound = baseMap[val];
else bound = val;
ourMap[key] = bound;
}
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;
}
function getHintElement(hintsElement, el) {
while (el && el != hintsElement) {
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement)
return el;
el = el.parentNode;
}
}
function Widget(completion, data) {
this.completion = completion;
this.data = data;
this.picked = false;
var widget = this,
cm = completion.cm;
var ownerDocument = cm.getInputField().ownerDocument;
var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
var hints = (this.hints = ownerDocument.createElement("ul"));
var theme = completion.cm.options.theme;
hints.className = "CodeMirror-hints " + theme;
this.selectedHint = data.selectedHint || 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(ownerDocument.createElement("li")),
cur = completions[i];
var className =
HINT_ELEMENT_CLASS +
(i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
if (cur.className != null) className = cur.className + " " + className;
elt.className = className;
if (cur.render) cur.render(elt, data, cur);
else
elt.appendChild(
ownerDocument.createTextNode(cur.displayText || getText(cur))
);
elt.hintId = i;
}
var container = completion.options.container || ownerDocument.body;
var pos = cm.cursorCoords(
completion.options.alignWithWord ? data.from : null
);
var left = pos.left,
top = pos.bottom,
below = true;
var offsetLeft = 0,
offsetTop = 0;
if (container !== ownerDocument.body) {
// We offset the cursor position because left and top are relative to the offsetParent's top left corner.
var isContainerPositioned =
["absolute", "relative", "fixed"].indexOf(
parentWindow.getComputedStyle(container).position
) !== -1;
var offsetParent = isContainerPositioned
? container
: container.offsetParent;
var offsetParentPosition = offsetParent.getBoundingClientRect();
var bodyPosition = ownerDocument.body.getBoundingClientRect();
offsetLeft =
offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft;
offsetTop =
offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop;
}
hints.style.left = left - offsetLeft + "px";
hints.style.top = top - offsetTop + "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 =
parentWindow.innerWidth ||
Math.max(
ownerDocument.body.offsetWidth,
ownerDocument.documentElement.offsetWidth
);
var winH =
parentWindow.innerHeight ||
Math.max(
ownerDocument.body.offsetHeight,
ownerDocument.documentElement.offsetHeight
);
container.appendChild(hints);
var box = completion.options.moveOnOverlap
? hints.getBoundingClientRect()
: new DOMRect();
var scrolls = completion.options.paddingForScrollbar
? hints.scrollHeight > hints.clientHeight + 1
: false;
// Compute in the timeout to avoid reflow on init
var startScroll;
setTimeout(function () {
startScroll = cm.getScrollInfo();
});
var overlapY = box.bottom - winH;
if (overlapY > 0) {
var height = box.bottom - box.top,
curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) {
// Fits above cursor
hints.style.top = (top = pos.top - height - offsetTop) + "px";
below = false;
} else if (height > winH) {
hints.style.height = winH - 5 + "px";
hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
var cursor = cm.getCursor();
if (data.from.ch != cursor.ch) {
pos = cm.cursorCoords(cursor);
hints.style.left = (left = pos.left - offsetLeft) + "px";
box = hints.getBoundingClientRect();
}
}
}
var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = winW - 5 + "px";
overlapX -= box.right - box.left - winW;
}
hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
}
if (scrolls)
for (var node = hints.firstChild; node; node = node.nextSibling)
node.style.paddingRight = cm.display.nativeBarWidth + "px";
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();
},
length: completions.length,
close: function () {
completion.close();
},
pick: function () {
widget.pick();
},
data: data,
}))
);
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);
})
);
}
cm.on(
"scroll",
(this.onScroll = function () {
var curScroll = cm.getScrollInfo(),
editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top;
var point =
newTop -
(parentWindow.pageYOffset ||
(ownerDocument.documentElement || ownerDocument.body).scrollTop);
if (!below) point += hints.offsetHeight;
if (point <= editor.top || point >= editor.bottom)
return completion.close();
hints.style.top = newTop + "px";
hints.style.left = left + startScroll.left - curScroll.left + "px";
})
);
CodeMirror.on(hints, "dblclick", function (e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
widget.changeActive(t.hintId);
widget.pick();
}
});
CodeMirror.on(hints, "click", function (e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
widget.changeActive(t.hintId);
if (completion.options.completeOnSingleClick) widget.pick();
}
});
CodeMirror.on(hints, "mousedown", function () {
setTimeout(function () {
cm.focus();
}, 20);
});
// The first hint doesn't need to be scrolled to on init
var selectedHintRange = this.getSelectedHintRange();
if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) {
this.scrollToActive();
}
CodeMirror.signal(
data,
"select",
completions[this.selectedHint],
hints.childNodes[this.selectedHint]
);
return true;
}
Widget.prototype = {
close: function () {
if (this.completion.widget != this) return;
this.completion.widget = null;
this.hints.parentNode.removeChild(this.hints);
this.completion.cm.removeKeyMap(this.keyMap);
var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
cm.off("scroll", this.onScroll);
},
disable: function () {
this.completion.cm.removeKeyMap(this.keyMap);
var widget = this;
this.keyMap = {
Enter: function () {
widget.picked = true;
},
};
this.completion.cm.addKeyMap(this.keyMap);
},
pick: function () {
this.completion.pick(this.data, this.selectedHint);
},
changeActive: function (i, avoidWrap) {
if (i >= this.data.list.length)
i = avoidWrap ? this.data.list.length - 1 : 0;
else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1;
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
if (node)
node.className = node.className.replace(
" " + ACTIVE_HINT_ELEMENT_CLASS,
""
);
node = this.hints.childNodes[(this.selectedHint = i)];
node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
this.scrollToActive();
CodeMirror.signal(
this.data,
"select",
this.data.list[this.selectedHint],
node
);
},
scrollToActive: function () {
var selectedHintRange = this.getSelectedHintRange();
var node1 = this.hints.childNodes[selectedHintRange.from];
var node2 = this.hints.childNodes[selectedHintRange.to];
var firstNode = this.hints.firstChild;
if (node1.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
else if (
node2.offsetTop + node2.offsetHeight >
this.hints.scrollTop + this.hints.clientHeight
)
this.hints.scrollTop =
node2.offsetTop +
node2.offsetHeight -
this.hints.clientHeight +
firstNode.offsetTop;
},
screenAmount: function () {
return (
Math.floor(
this.hints.clientHeight / this.hints.firstChild.offsetHeight
) || 1
);
},
getSelectedHintRange: function () {
var margin = this.completion.options.scrollMargin || 0;
return {
from: Math.max(0, this.selectedHint - margin),
to: Math.min(this.data.list.length - 1, this.selectedHint + margin),
};
},
};
function applicableHelpers(cm, helpers) {
if (!cm.somethingSelected()) return helpers;
var result = [];
for (var i = 0; i < helpers.length; i++)
if (helpers[i].supportsSelection) result.push(helpers[i]);
return result;
}
function fetchHints(hint, cm, options, callback) {
if (hint.async) {
hint(cm, callback, options);
} else {
var result = hint(cm, options);
if (result && result.then) result.then(callback);
else callback(result);
}
}
function resolveAutoHints(cm, pos) {
var helpers = cm.getHelpers(pos, "hint"),
words;
if (helpers.length) {
var resolved = function (cm, callback, options) {
var app = applicableHelpers(cm, helpers);
function run(i) {
if (i == app.length) return callback(null);
fetchHints(app[i], cm, options, function (result) {
if (result && result.list.length > 0) callback(result);
else run(i + 1);
});
}
run(0);
};
resolved.async = true;
resolved.supportsSelection = true;
return resolved;
} else if ((words = cm.getHelper(cm.getCursor(), "hintWords"))) {
return function (cm) {
return CodeMirror.hint.fromList(cm, { words: words });
};
} else if (CodeMirror.hint.anyword) {
return function (cm, options) {
return CodeMirror.hint.anyword(cm, options);
};
} else {
return function () {};
}
}
CodeMirror.registerHelper("hint", "auto", {
resolve: resolveAutoHints,
});
CodeMirror.registerHelper("hint", "fromList", function (cm, options) {
var cur = cm.getCursor(),
token = cm.getTokenAt(cur);
var term,
from = CodeMirror.Pos(cur.line, token.start),
to = cur;
if (
token.start < cur.ch &&
/\w/.test(token.string.charAt(cur.ch - token.start - 1))
) {
term = token.string.substr(0, cur.ch - token.start);
} else {
term = "";
from = cur;
}
var found = [];
for (var i = 0; i < options.words.length; i++) {
var word = options.words[i];
if (word.slice(0, term.length) == term) found.push(word);
}
if (found.length) return { list: found, from: from, to: to };
});
CodeMirror.commands.autocomplete = CodeMirror.showHint;
var defaultOptions = {
hint: CodeMirror.hint.auto,
completeSingle: true,
alignWithWord: true,
closeCharacters: /[\s()\[\]{};:>,]/,
closeOnPick: true,
closeOnUnfocus: true,
updateOnCursorActivity: true,
completeOnSingleClick: true,
container: null,
customKeys: null,
extraKeys: null,
paddingForScrollbar: true,
moveOnOverlap: true,
};
CodeMirror.defineOption("hintOptions", null);