added Linux arm64 SDK
This commit is contained in:
@@ -1,926 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CodeMirrorCompletionController = class CodeMirrorCompletionController extends WI.Object
|
||||
{
|
||||
constructor(mode, codeMirror, delegate, stopCharactersRegex)
|
||||
{
|
||||
console.assert(Object.values(WI.CodeMirrorCompletionController.Mode).includes(mode), mode);
|
||||
console.assert(codeMirror instanceof CodeMirror, codeMirror);
|
||||
|
||||
super();
|
||||
|
||||
this._mode = mode;
|
||||
this._codeMirror = codeMirror;
|
||||
this._stopCharactersRegex = stopCharactersRegex || null;
|
||||
this._delegate = delegate || null;
|
||||
|
||||
this._startOffset = NaN;
|
||||
this._endOffset = NaN;
|
||||
this._lineNumber = NaN;
|
||||
this._prefix = "";
|
||||
this._noEndingSemicolon = false;
|
||||
this._completions = [];
|
||||
this._extendedCompletionProviders = {};
|
||||
|
||||
this._suggestionsView = new WI.CompletionSuggestionsView(this);
|
||||
|
||||
this._keyMap = {
|
||||
"Up": this._handleUpKey.bind(this),
|
||||
"Down": this._handleDownKey.bind(this),
|
||||
"Right": this._handleRightOrEnterKey.bind(this),
|
||||
"Esc": this._handleEscapeKey.bind(this),
|
||||
"Enter": this._handleRightOrEnterKey.bind(this),
|
||||
"Tab": this._handleTabKey.bind(this),
|
||||
"Cmd-A": this._handleHideKey.bind(this),
|
||||
"Cmd-Z": this._handleHideKey.bind(this),
|
||||
"Shift-Cmd-Z": this._handleHideKey.bind(this),
|
||||
"Cmd-Y": this._handleHideKey.bind(this)
|
||||
};
|
||||
|
||||
this._handleChangeListener = this._handleChange.bind(this);
|
||||
this._handleCursorActivityListener = this._handleCursorActivity.bind(this);
|
||||
this._handleHideActionListener = this._handleHideAction.bind(this);
|
||||
|
||||
this._codeMirror.addKeyMap(this._keyMap);
|
||||
|
||||
this._codeMirror.on("change", this._handleChangeListener);
|
||||
this._codeMirror.on("cursorActivity", this._handleCursorActivityListener);
|
||||
this._codeMirror.on("blur", this._handleHideActionListener);
|
||||
this._codeMirror.on("scroll", this._handleHideActionListener);
|
||||
|
||||
this._updatePromise = null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get mode() { return this._mode; }
|
||||
|
||||
addExtendedCompletionProvider(modeName, provider)
|
||||
{
|
||||
this._extendedCompletionProviders[modeName] = provider;
|
||||
}
|
||||
|
||||
updateCompletions(completions, implicitSuffix)
|
||||
{
|
||||
if (isNaN(this._startOffset) || isNaN(this._endOffset) || isNaN(this._lineNumber))
|
||||
return;
|
||||
|
||||
if (!completions || !completions.length) {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
this._completions = completions;
|
||||
|
||||
if (typeof implicitSuffix === "string")
|
||||
this._implicitSuffix = implicitSuffix;
|
||||
|
||||
var from = {line: this._lineNumber, ch: this._startOffset};
|
||||
var to = {line: this._lineNumber, ch: this._endOffset};
|
||||
|
||||
var firstCharCoords = this._codeMirror.cursorCoords(from);
|
||||
var lastCharCoords = this._codeMirror.cursorCoords(to);
|
||||
var bounds = new WI.Rect(firstCharCoords.left, firstCharCoords.top, lastCharCoords.right - firstCharCoords.left, firstCharCoords.bottom - firstCharCoords.top);
|
||||
|
||||
// Try to restore the previous selected index, otherwise just select the first.
|
||||
var index = this._currentCompletion ? completions.indexOf(this._currentCompletion) : 0;
|
||||
if (index === -1)
|
||||
index = 0;
|
||||
|
||||
if (this._forced || completions.length > 1 || completions[index] !== this._prefix) {
|
||||
// Update and show the suggestion list.
|
||||
this._suggestionsView.update(completions, index);
|
||||
this._suggestionsView.show(bounds);
|
||||
} else if (this._implicitSuffix) {
|
||||
// The prefix and the completion exactly match, but there is an implicit suffix.
|
||||
// Just hide the suggestion list and keep the completion hint for the implicit suffix.
|
||||
this._suggestionsView.hide();
|
||||
} else {
|
||||
// The prefix and the completion exactly match, hide the completions. Return early so
|
||||
// the completion hint isn't updated.
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
this._applyCompletionHint(completions[index]);
|
||||
|
||||
this._resolveUpdatePromise(WI.CodeMirrorCompletionController.UpdatePromise.CompletionsFound);
|
||||
}
|
||||
|
||||
isCompletionChange(change)
|
||||
{
|
||||
return this._ignoreChange || change.origin === WI.CodeMirrorCompletionController.CompletionOrigin || change.origin === WI.CodeMirrorCompletionController.DeleteCompletionOrigin;
|
||||
}
|
||||
|
||||
isShowingCompletions()
|
||||
{
|
||||
return this._suggestionsView.visible || (this._completionHintMarker && this._completionHintMarker.find());
|
||||
}
|
||||
|
||||
isHandlingClickEvent()
|
||||
{
|
||||
return this._suggestionsView.isHandlingClickEvent();
|
||||
}
|
||||
|
||||
commitCurrentCompletion()
|
||||
{
|
||||
this._removeCompletionHint(true, true);
|
||||
|
||||
let replacementText = this._currentReplacementText;
|
||||
if (!replacementText)
|
||||
return;
|
||||
|
||||
let from = {line: this._lineNumber, ch: this._startOffset};
|
||||
let cursor = {line: this._lineNumber, ch: this._endOffset};
|
||||
let to = {line: this._lineNumber, ch: this._startOffset + replacementText.length};
|
||||
|
||||
let lastChar = this._currentCompletion.charAt(this._currentCompletion.length - 1);
|
||||
let isClosing = ")]}".indexOf(lastChar);
|
||||
if (isClosing !== -1)
|
||||
to.ch -= 1 + this._implicitSuffix.length;
|
||||
|
||||
this._codeMirror.replaceRange(replacementText, from, cursor, WI.CodeMirrorCompletionController.CompletionOrigin);
|
||||
|
||||
// Don't call _removeLastChangeFromHistory here to allow the committed completion to be undone.
|
||||
|
||||
this._codeMirror.setCursor(to);
|
||||
|
||||
this.hideCompletions();
|
||||
}
|
||||
|
||||
hideCompletions()
|
||||
{
|
||||
this._suggestionsView.hide();
|
||||
|
||||
this._removeCompletionHint();
|
||||
|
||||
this._startOffset = NaN;
|
||||
this._endOffset = NaN;
|
||||
this._lineNumber = NaN;
|
||||
this._prefix = "";
|
||||
this._completions = [];
|
||||
this._implicitSuffix = "";
|
||||
this._forced = false;
|
||||
|
||||
delete this._currentCompletion;
|
||||
delete this._ignoreNextCursorActivity;
|
||||
|
||||
this._resolveUpdatePromise(WI.CodeMirrorCompletionController.UpdatePromise.NoCompletionsFound);
|
||||
}
|
||||
|
||||
close()
|
||||
{
|
||||
this._codeMirror.removeKeyMap(this._keyMap);
|
||||
|
||||
this._codeMirror.off("change", this._handleChangeListener);
|
||||
this._codeMirror.off("cursorActivity", this._handleCursorActivityListener);
|
||||
this._codeMirror.off("blur", this._handleHideActionListener);
|
||||
this._codeMirror.off("scroll", this._handleHideActionListener);
|
||||
}
|
||||
|
||||
completeAtCurrentPositionIfNeeded(force)
|
||||
{
|
||||
this._resolveUpdatePromise(WI.CodeMirrorCompletionController.UpdatePromise.Canceled);
|
||||
|
||||
var update = this._updatePromise = new WI.WrappedPromise;
|
||||
|
||||
this._completeAtCurrentPosition(force);
|
||||
|
||||
return update.promise;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
completionSuggestionsSelectedCompletion(suggestionsView, completionText)
|
||||
{
|
||||
this._applyCompletionHint(completionText);
|
||||
}
|
||||
|
||||
completionSuggestionsClickedCompletion(suggestionsView, completionText)
|
||||
{
|
||||
// The clicked suggestion causes the editor to loose focus. Restore it so the user can keep typing.
|
||||
this._codeMirror.focus();
|
||||
|
||||
this._applyCompletionHint(completionText);
|
||||
this._commitCompletionHint();
|
||||
}
|
||||
|
||||
set noEndingSemicolon(noEndingSemicolon)
|
||||
{
|
||||
this._noEndingSemicolon = noEndingSemicolon;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_resolveUpdatePromise(message)
|
||||
{
|
||||
if (!this._updatePromise)
|
||||
return;
|
||||
|
||||
this._updatePromise.resolve(message);
|
||||
this._updatePromise = null;
|
||||
}
|
||||
|
||||
get _currentReplacementText()
|
||||
{
|
||||
return (this._currentCompletion ?? "") + (this._implicitSuffix ?? "");
|
||||
}
|
||||
|
||||
_hasPendingCompletion()
|
||||
{
|
||||
return !isNaN(this._startOffset) && !isNaN(this._endOffset) && !isNaN(this._lineNumber);
|
||||
}
|
||||
|
||||
_notifyCompletionsHiddenSoon()
|
||||
{
|
||||
function notify()
|
||||
{
|
||||
if (this._completionHintMarker)
|
||||
return;
|
||||
|
||||
if (this._delegate && typeof this._delegate.completionControllerCompletionsHidden === "function")
|
||||
this._delegate.completionControllerCompletionsHidden(this);
|
||||
}
|
||||
|
||||
if (this._notifyCompletionsHiddenIfNeededTimeout)
|
||||
clearTimeout(this._notifyCompletionsHiddenIfNeededTimeout);
|
||||
this._notifyCompletionsHiddenIfNeededTimeout = setTimeout(notify.bind(this), WI.CodeMirrorCompletionController.CompletionsHiddenDelay);
|
||||
}
|
||||
|
||||
_createCompletionHintMarker(position, text)
|
||||
{
|
||||
var container = document.createElement("span");
|
||||
container.classList.add(WI.CodeMirrorCompletionController.CompletionHintStyleClassName);
|
||||
container.textContent = text;
|
||||
|
||||
container.addEventListener("mousedown", (event) => {
|
||||
event.preventDefault();
|
||||
this._commitCompletionHint();
|
||||
|
||||
// The clicked hint marker causes the editor to loose focus. Restore it so the user can keep typing.
|
||||
setTimeout(() => { this._codeMirror.focus(); }, 0);
|
||||
});
|
||||
|
||||
this._completionHintMarker = this._codeMirror.setUniqueBookmark(position, {widget: container, insertLeft: true});
|
||||
}
|
||||
|
||||
_applyCompletionHint(completionText)
|
||||
{
|
||||
console.assert(completionText);
|
||||
if (!completionText)
|
||||
return;
|
||||
|
||||
function update()
|
||||
{
|
||||
this._currentCompletion = completionText;
|
||||
|
||||
this._removeCompletionHint(true, true);
|
||||
|
||||
var replacementText = this._currentReplacementText;
|
||||
|
||||
var from = {line: this._lineNumber, ch: this._startOffset};
|
||||
var cursor = {line: this._lineNumber, ch: this._endOffset};
|
||||
var currentText = this._codeMirror.getRange(from, cursor);
|
||||
|
||||
this._createCompletionHintMarker(cursor, replacementText.replace(currentText, ""));
|
||||
}
|
||||
|
||||
this._ignoreChange = true;
|
||||
this._ignoreNextCursorActivity = true;
|
||||
|
||||
this._codeMirror.operation(update.bind(this));
|
||||
|
||||
delete this._ignoreChange;
|
||||
}
|
||||
|
||||
_commitCompletionHint()
|
||||
{
|
||||
this._ignoreChange = true;
|
||||
this._ignoreNextCursorActivity = true;
|
||||
|
||||
this._codeMirror.operation(this.commitCurrentCompletion.bind(this));
|
||||
|
||||
delete this._ignoreChange;
|
||||
}
|
||||
|
||||
_removeLastChangeFromHistory()
|
||||
{
|
||||
var history = this._codeMirror.getHistory();
|
||||
|
||||
// We don't expect a undone history. But if there is one clear it. If could lead to undefined behavior.
|
||||
console.assert(!history.undone.length);
|
||||
history.undone = [];
|
||||
|
||||
// Pop the last item from the done history.
|
||||
console.assert(history.done.length);
|
||||
history.done.pop();
|
||||
|
||||
this._codeMirror.setHistory(history);
|
||||
}
|
||||
|
||||
_removeCompletionHint(nonatomic, dontRestorePrefix)
|
||||
{
|
||||
if (!this._completionHintMarker)
|
||||
return;
|
||||
|
||||
this._notifyCompletionsHiddenSoon();
|
||||
|
||||
function clearMarker(marker)
|
||||
{
|
||||
if (!marker)
|
||||
return;
|
||||
|
||||
var range = marker.find();
|
||||
if (range)
|
||||
marker.clear();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function update()
|
||||
{
|
||||
this._completionHintMarker = clearMarker(this._completionHintMarker);
|
||||
|
||||
if (dontRestorePrefix)
|
||||
return;
|
||||
|
||||
console.assert(!isNaN(this._startOffset));
|
||||
console.assert(!isNaN(this._endOffset));
|
||||
console.assert(!isNaN(this._lineNumber));
|
||||
|
||||
var from = {line: this._lineNumber, ch: this._startOffset};
|
||||
var to = {line: this._lineNumber, ch: this._endOffset};
|
||||
|
||||
this._codeMirror.replaceRange(this._prefix, from, to, WI.CodeMirrorCompletionController.DeleteCompletionOrigin);
|
||||
this._removeLastChangeFromHistory();
|
||||
}
|
||||
|
||||
if (nonatomic) {
|
||||
update.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
this._ignoreChange = true;
|
||||
|
||||
this._codeMirror.operation(update.bind(this));
|
||||
|
||||
delete this._ignoreChange;
|
||||
}
|
||||
|
||||
_scanStringForExpression(modeName, string, startOffset, direction, allowMiddleAndEmpty, includeStopCharacter, ignoreInitialUnmatchedOpenBracket, stopCharactersRegex)
|
||||
{
|
||||
console.assert(direction === -1 || direction === 1);
|
||||
|
||||
var stopCharactersRegex = stopCharactersRegex || this._stopCharactersRegex || WI.CodeMirrorCompletionController.DefaultStopCharactersRegexModeMap[modeName] || WI.CodeMirrorCompletionController.GenericStopCharactersRegex;
|
||||
|
||||
function isStopCharacter(character)
|
||||
{
|
||||
return stopCharactersRegex.test(character);
|
||||
}
|
||||
|
||||
function isOpenBracketCharacter(character)
|
||||
{
|
||||
return WI.CodeMirrorCompletionController.OpenBracketCharactersRegex.test(character);
|
||||
}
|
||||
|
||||
function isCloseBracketCharacter(character)
|
||||
{
|
||||
return WI.CodeMirrorCompletionController.CloseBracketCharactersRegex.test(character);
|
||||
}
|
||||
|
||||
function matchingBracketCharacter(character)
|
||||
{
|
||||
return WI.CodeMirrorCompletionController.MatchingBrackets[character];
|
||||
}
|
||||
|
||||
var endOffset = Math.min(startOffset, string.length);
|
||||
|
||||
var endOfLineOrWord = endOffset === string.length || isStopCharacter(string.charAt(endOffset));
|
||||
|
||||
if (!endOfLineOrWord && !allowMiddleAndEmpty)
|
||||
return null;
|
||||
|
||||
var bracketStack = [];
|
||||
var bracketOffsetStack = [];
|
||||
|
||||
var startOffset = endOffset;
|
||||
var firstOffset = endOffset + direction;
|
||||
for (var i = firstOffset; direction > 0 ? i < string.length : i >= 0; i += direction) {
|
||||
var character = string.charAt(i);
|
||||
|
||||
// Ignore stop characters when we are inside brackets.
|
||||
if (isStopCharacter(character) && !bracketStack.length)
|
||||
break;
|
||||
|
||||
if (isCloseBracketCharacter(character)) {
|
||||
bracketStack.push(character);
|
||||
bracketOffsetStack.push(i);
|
||||
} else if (isOpenBracketCharacter(character)) {
|
||||
if ((!ignoreInitialUnmatchedOpenBracket || i !== firstOffset) && (!bracketStack.length || matchingBracketCharacter(character) !== bracketStack.lastValue))
|
||||
break;
|
||||
|
||||
bracketOffsetStack.pop();
|
||||
bracketStack.pop();
|
||||
}
|
||||
|
||||
startOffset = i + (direction > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
if (bracketOffsetStack.length)
|
||||
startOffset = bracketOffsetStack.pop() + 1;
|
||||
|
||||
if (includeStopCharacter && startOffset > 0 && startOffset < string.length)
|
||||
startOffset += direction;
|
||||
|
||||
if (direction > 0) {
|
||||
var tempEndOffset = endOffset;
|
||||
endOffset = startOffset;
|
||||
startOffset = tempEndOffset;
|
||||
}
|
||||
|
||||
return {string: string.substring(startOffset, endOffset), startOffset, endOffset};
|
||||
}
|
||||
|
||||
_completeAtCurrentPosition(force)
|
||||
{
|
||||
if (this._codeMirror.somethingSelected()) {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
this._removeCompletionHint(true, true);
|
||||
|
||||
var cursor = this._codeMirror.getCursor();
|
||||
var token = this._codeMirror.getTokenAt(cursor);
|
||||
|
||||
// Don't try to complete inside comments or strings.
|
||||
if (token.type && /\b(comment|string)\b/.test(token.type)) {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
var mode = this._codeMirror.getMode();
|
||||
var innerMode = CodeMirror.innerMode(mode, token.state).mode;
|
||||
var modeName = innerMode.alternateName || innerMode.name;
|
||||
|
||||
var lineNumber = cursor.line;
|
||||
var lineString = this._codeMirror.getLine(lineNumber);
|
||||
|
||||
var backwardScanResult = this._scanStringForExpression(modeName, lineString, cursor.ch, -1, force);
|
||||
if (!backwardScanResult) {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
var forwardScanResult = this._scanStringForExpression(modeName, lineString, cursor.ch, 1, true, true);
|
||||
var suffix = forwardScanResult.string;
|
||||
|
||||
this._ignoreNextCursorActivity = true;
|
||||
|
||||
this._startOffset = backwardScanResult.startOffset;
|
||||
this._endOffset = backwardScanResult.endOffset;
|
||||
this._lineNumber = lineNumber;
|
||||
this._prefix = backwardScanResult.string;
|
||||
this._completions = [];
|
||||
this._implicitSuffix = "";
|
||||
this._forced = force;
|
||||
|
||||
var baseExpressionStopCharactersRegex = WI.CodeMirrorCompletionController.BaseExpressionStopCharactersRegexModeMap[modeName];
|
||||
if (baseExpressionStopCharactersRegex)
|
||||
var baseScanResult = this._scanStringForExpression(modeName, lineString, this._startOffset, -1, true, false, true, baseExpressionStopCharactersRegex);
|
||||
|
||||
if (!force && !backwardScanResult.string && (!baseScanResult || !baseScanResult.string)) {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultCompletions = [];
|
||||
|
||||
switch (modeName) {
|
||||
case "css":
|
||||
defaultCompletions = this._generateCSSCompletions(token, baseScanResult ? baseScanResult.string : null, suffix);
|
||||
break;
|
||||
case "javascript":
|
||||
defaultCompletions = this._generateJavaScriptCompletions(token, baseScanResult ? baseScanResult.string : null, suffix);
|
||||
break;
|
||||
}
|
||||
|
||||
var extendedCompletionsProvider = this._extendedCompletionProviders[modeName];
|
||||
if (extendedCompletionsProvider) {
|
||||
extendedCompletionsProvider.completionControllerCompletionsNeeded(this, defaultCompletions, baseScanResult ? baseScanResult.string : null, this._prefix, suffix, force);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._delegate && typeof this._delegate.completionControllerCompletionsNeeded === "function")
|
||||
this._delegate.completionControllerCompletionsNeeded(this, this._prefix, defaultCompletions, baseScanResult ? baseScanResult.string : null, suffix, force);
|
||||
else
|
||||
this.updateCompletions(defaultCompletions);
|
||||
}
|
||||
|
||||
_generateCSSCompletions(mainToken, base, suffix)
|
||||
{
|
||||
// We support completion inside CSS block context and functions.
|
||||
if (mainToken.state.state === "media" || mainToken.state.state === "top")
|
||||
return [];
|
||||
|
||||
// Don't complete in the middle of a property name.
|
||||
if (/^[a-z]/i.test(suffix))
|
||||
return [];
|
||||
|
||||
var token = mainToken;
|
||||
var lineNumber = this._lineNumber;
|
||||
|
||||
let getPreviousToken = () => {
|
||||
// Found the beginning of the line. Go to the previous line.
|
||||
if (!token.start) {
|
||||
--lineNumber;
|
||||
|
||||
// No more lines, stop.
|
||||
if (lineNumber < 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._codeMirror.getTokenAt({line: lineNumber, ch: token.start ? token.start : Number.MAX_VALUE});
|
||||
};
|
||||
|
||||
// Inside a function, determine the function name.
|
||||
if (token.state.state === "parens") {
|
||||
// Scan backwards looking for the function paren boundary.
|
||||
while (token && token.state.state === "parens" && token.string !== "(")
|
||||
token = getPreviousToken();
|
||||
|
||||
// The immediately preceding token should have the function name.
|
||||
if (token)
|
||||
token = getPreviousToken();
|
||||
|
||||
// No completions if no function name found.
|
||||
if (!token)
|
||||
return [];
|
||||
|
||||
let functionName = token.string;
|
||||
if (!functionName)
|
||||
return [];
|
||||
|
||||
let functionCompletions = WI.CSSKeywordCompletions.forFunction(functionName).startsWith(this._prefix);
|
||||
|
||||
if (this._delegate && this._delegate.completionControllerCSSFunctionValuesNeeded)
|
||||
functionCompletions = this._delegate.completionControllerCSSFunctionValuesNeeded(this, functionName, functionCompletions);
|
||||
|
||||
return functionCompletions;
|
||||
}
|
||||
|
||||
// Scan backwards looking for the current property.
|
||||
while (token.state.state === "prop") {
|
||||
let previousToken = getPreviousToken();
|
||||
if (!previousToken)
|
||||
break;
|
||||
token = previousToken;
|
||||
}
|
||||
|
||||
// If we have a property token and it's not the main token, then we are working on
|
||||
// the value for that property and should complete allowed values.
|
||||
if (mainToken !== token && token.type && /\bproperty\b/.test(token.type)) {
|
||||
var propertyName = token.string;
|
||||
|
||||
// If there is a suffix and it isn't a semicolon, then we should use a space since
|
||||
// the user is editing in the middle. Likewise if the suffix starts with an open
|
||||
// paren we are changing a function name so don't add a suffix.
|
||||
this._implicitSuffix = " ";
|
||||
if (suffix === ";")
|
||||
this._implicitSuffix = this._noEndingSemicolon ? "" : ";";
|
||||
else if (suffix.startsWith("("))
|
||||
this._implicitSuffix = "";
|
||||
|
||||
// Don't use an implicit suffix if it would be the same as the existing suffix.
|
||||
if (this._implicitSuffix === suffix)
|
||||
this._implicitSuffix = "";
|
||||
|
||||
let completions = WI.CSSKeywordCompletions.forProperty(propertyName).startsWith(this._prefix);
|
||||
|
||||
if (suffix.startsWith("("))
|
||||
completions = completions.map((x) => x.replace(/\(\)$/, ""));
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
this._implicitSuffix = suffix !== ":" ? ": " : "";
|
||||
|
||||
// Complete property names.
|
||||
return WI.cssManager.propertyNameCompletions.startsWith(this._prefix);
|
||||
}
|
||||
|
||||
_generateJavaScriptCompletions(mainToken, base, suffix)
|
||||
{
|
||||
// If there is a base expression then we should not attempt to match any keywords or variables.
|
||||
// Allow only open bracket characters at the end of the base, otherwise leave completions with
|
||||
// a base up to the delegate to figure out.
|
||||
if (base && !/[({[]$/.test(base))
|
||||
return [];
|
||||
|
||||
var matchingWords = [];
|
||||
|
||||
var prefix = this._prefix;
|
||||
|
||||
var localState = mainToken.state.localState ? mainToken.state.localState : mainToken.state;
|
||||
|
||||
var declaringVariable = localState.lexical.type === "vardef";
|
||||
var insideSwitch = localState.lexical.prev ? localState.lexical.prev.info === "switch" : false;
|
||||
var insideBlock = localState.lexical.prev ? localState.lexical.prev.type === "}" : false;
|
||||
var insideParenthesis = localState.lexical.type === ")";
|
||||
var insideBrackets = localState.lexical.type === "]";
|
||||
|
||||
// FIXME: Include module keywords if we know this is a module environment.
|
||||
// var moduleKeywords = ["default", "export", "import"];
|
||||
|
||||
const allKeywords = [
|
||||
"break", "case", "catch", "class", "const", "continue", "debugger", "default",
|
||||
"delete", "do", "else", "extends", "false", "finally", "for", "function",
|
||||
"if", "in", "Infinity", "instanceof", "let", "NaN", "new", "null", "of",
|
||||
"return", "static", "super", "switch", "this", "throw", "true", "try",
|
||||
"typeof", "undefined", "var", "void", "while", "with", "yield"
|
||||
];
|
||||
const valueKeywords = ["false", "Infinity", "NaN", "null", "this", "true", "undefined", "globalThis"];
|
||||
|
||||
const allowedKeywordsInsideBlocks = new Set(allKeywords);
|
||||
const allowedKeywordsWhenDeclaringVariable = new Set(valueKeywords);
|
||||
const allowedKeywordsInsideParenthesis = new Set(valueKeywords.concat(["class", "function"]));
|
||||
const allowedKeywordsInsideBrackets = allowedKeywordsInsideParenthesis;
|
||||
const allowedKeywordsOnlyInsideSwitch = new Set(["case", "default"]);
|
||||
|
||||
function matchKeywords(keywords)
|
||||
{
|
||||
for (let keyword of keywords) {
|
||||
if (!insideSwitch && allowedKeywordsOnlyInsideSwitch.has(keyword))
|
||||
continue;
|
||||
if (insideBlock && !allowedKeywordsInsideBlocks.has(keyword))
|
||||
continue;
|
||||
if (insideBrackets && !allowedKeywordsInsideBrackets.has(keyword))
|
||||
continue;
|
||||
if (insideParenthesis && !allowedKeywordsInsideParenthesis.has(keyword))
|
||||
continue;
|
||||
if (declaringVariable && !allowedKeywordsWhenDeclaringVariable.has(keyword))
|
||||
continue;
|
||||
if (!keyword.startsWith(prefix))
|
||||
continue;
|
||||
matchingWords.push(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
function matchVariables()
|
||||
{
|
||||
function filterVariables(variables)
|
||||
{
|
||||
for (var variable = variables; variable; variable = variable.next) {
|
||||
// Don't match the variable if this token is in a variable declaration.
|
||||
// Otherwise the currently typed text will always match and that isn't useful.
|
||||
if (declaringVariable && variable.name === prefix)
|
||||
continue;
|
||||
|
||||
if (variable.name.startsWith(prefix) && !matchingWords.includes(variable.name))
|
||||
matchingWords.push(variable.name);
|
||||
}
|
||||
}
|
||||
|
||||
var context = localState.context;
|
||||
while (context) {
|
||||
if (context.vars)
|
||||
filterVariables(context.vars);
|
||||
context = context.prev;
|
||||
}
|
||||
|
||||
if (localState.localVars)
|
||||
filterVariables(localState.localVars);
|
||||
if (localState.globalVars)
|
||||
filterVariables(localState.globalVars);
|
||||
}
|
||||
|
||||
switch (suffix.substring(0, 1)) {
|
||||
case "":
|
||||
case " ":
|
||||
matchVariables();
|
||||
matchKeywords(allKeywords);
|
||||
break;
|
||||
|
||||
case ".":
|
||||
case "[":
|
||||
matchVariables();
|
||||
matchKeywords(["false", "Infinity", "NaN", "this", "true"]);
|
||||
break;
|
||||
|
||||
case "(":
|
||||
matchVariables();
|
||||
matchKeywords(["catch", "else", "for", "function", "if", "return", "switch", "throw", "while", "with", "yield"]);
|
||||
break;
|
||||
|
||||
case "{":
|
||||
matchKeywords(["do", "else", "finally", "return", "try", "yield"]);
|
||||
break;
|
||||
|
||||
case ":":
|
||||
if (insideSwitch)
|
||||
matchKeywords(["case", "default"]);
|
||||
break;
|
||||
|
||||
case ";":
|
||||
matchVariables();
|
||||
matchKeywords(valueKeywords);
|
||||
matchKeywords(["break", "continue", "debugger", "return", "void"]);
|
||||
break;
|
||||
}
|
||||
|
||||
return matchingWords;
|
||||
}
|
||||
|
||||
_handleUpKey(codeMirror)
|
||||
{
|
||||
if (!this._hasPendingCompletion())
|
||||
return CodeMirror.Pass;
|
||||
|
||||
if (!this.isShowingCompletions())
|
||||
return;
|
||||
|
||||
this._suggestionsView.selectPrevious();
|
||||
}
|
||||
|
||||
_handleDownKey(codeMirror)
|
||||
{
|
||||
if (!this._hasPendingCompletion())
|
||||
return CodeMirror.Pass;
|
||||
|
||||
if (!this.isShowingCompletions())
|
||||
return;
|
||||
|
||||
this._suggestionsView.selectNext();
|
||||
}
|
||||
|
||||
_handleRightOrEnterKey(codeMirror)
|
||||
{
|
||||
if (!this._hasPendingCompletion())
|
||||
return CodeMirror.Pass;
|
||||
|
||||
if (!this.isShowingCompletions())
|
||||
return;
|
||||
|
||||
this._commitCompletionHint();
|
||||
}
|
||||
|
||||
_handleEscapeKey(codeMirror)
|
||||
{
|
||||
var delegateImplementsShouldAllowEscapeCompletion = this._delegate && typeof this._delegate.completionControllerShouldAllowEscapeCompletion === "function";
|
||||
if (this._hasPendingCompletion())
|
||||
this.hideCompletions();
|
||||
else if (this._codeMirror.getOption("readOnly"))
|
||||
return CodeMirror.Pass;
|
||||
else if (!delegateImplementsShouldAllowEscapeCompletion || this._delegate.completionControllerShouldAllowEscapeCompletion(this))
|
||||
this._completeAtCurrentPosition(true);
|
||||
else
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
_handleTabKey(codeMirror)
|
||||
{
|
||||
if (!this._hasPendingCompletion())
|
||||
return CodeMirror.Pass;
|
||||
|
||||
if (!this.isShowingCompletions())
|
||||
return;
|
||||
|
||||
console.assert(this._completions.length);
|
||||
if (!this._completions.length)
|
||||
return;
|
||||
|
||||
console.assert(this._currentCompletion);
|
||||
if (!this._currentCompletion)
|
||||
return;
|
||||
|
||||
// Commit the current completion if there is only one suggestion.
|
||||
if (this._completions.length === 1) {
|
||||
this._commitCompletionHint();
|
||||
return;
|
||||
}
|
||||
|
||||
var prefixLength = this._prefix.length;
|
||||
|
||||
var commonPrefix = this._completions[0];
|
||||
for (var i = 1; i < this._completions.length; ++i) {
|
||||
var completion = this._completions[i];
|
||||
var lastIndex = Math.min(commonPrefix.length, completion.length);
|
||||
for (var j = prefixLength; j < lastIndex; ++j) {
|
||||
if (commonPrefix[j] !== completion[j]) {
|
||||
commonPrefix = commonPrefix.substr(0, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit the current completion if there is no common prefix that is longer.
|
||||
if (commonPrefix === this._prefix) {
|
||||
this._commitCompletionHint();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the prefix to the common prefix so _applyCompletionHint will insert the
|
||||
// common prefix as commited text. Adjust _endOffset to match the new prefix.
|
||||
this._prefix = commonPrefix;
|
||||
this._endOffset = this._startOffset + commonPrefix.length;
|
||||
|
||||
this._applyCompletionHint(this._currentCompletion);
|
||||
}
|
||||
|
||||
_handleChange(codeMirror, change)
|
||||
{
|
||||
if (this.isCompletionChange(change))
|
||||
return;
|
||||
|
||||
this._ignoreNextCursorActivity = true;
|
||||
|
||||
if (!change.origin || change.origin.charAt(0) !== "+") {
|
||||
this.hideCompletions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only complete on delete if we are showing completions already.
|
||||
if (change.origin === "+delete" && !this._hasPendingCompletion())
|
||||
return;
|
||||
|
||||
this._completeAtCurrentPosition(false);
|
||||
}
|
||||
|
||||
_handleCursorActivity(codeMirror)
|
||||
{
|
||||
if (this._ignoreChange)
|
||||
return;
|
||||
|
||||
if (this._ignoreNextCursorActivity) {
|
||||
delete this._ignoreNextCursorActivity;
|
||||
return;
|
||||
}
|
||||
|
||||
this.hideCompletions();
|
||||
}
|
||||
|
||||
_handleHideKey(codeMirror)
|
||||
{
|
||||
this.hideCompletions();
|
||||
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
_handleHideAction(codeMirror)
|
||||
{
|
||||
// Clicking a suggestion causes the editor to blur. We don't want to hide completions in this case.
|
||||
if (this.isHandlingClickEvent())
|
||||
return;
|
||||
|
||||
this.hideCompletions();
|
||||
}
|
||||
};
|
||||
|
||||
WI.CodeMirrorCompletionController.Mode = {
|
||||
Basic: "basic",
|
||||
EventBreakpoint: "event-breakpoint",
|
||||
ExceptionBreakpoint: "exception-breakpoint",
|
||||
FullConsoleCommandLineAPI: "full-console-command-line-api",
|
||||
PausedConsoleCommandLineAPI: "paused-console-command-line-api",
|
||||
};
|
||||
|
||||
WI.CodeMirrorCompletionController.UpdatePromise = {
|
||||
Canceled: "code-mirror-completion-controller-canceled",
|
||||
CompletionsFound: "code-mirror-completion-controller-completions-found",
|
||||
NoCompletionsFound: "code-mirror-completion-controller-no-completions-found"
|
||||
};
|
||||
|
||||
WI.CodeMirrorCompletionController.GenericStopCharactersRegex = /[\s=:;,]/;
|
||||
WI.CodeMirrorCompletionController.DefaultStopCharactersRegexModeMap = {"css": /[\s:;,{}()]/, "javascript": /[\s=:;,!+\-*/%&|^~?<>.{}()[\]]/};
|
||||
WI.CodeMirrorCompletionController.BaseExpressionStopCharactersRegexModeMap = {"javascript": /[\s=:;,!+\-*/%&|^~?<>]/};
|
||||
WI.CodeMirrorCompletionController.OpenBracketCharactersRegex = /[({[]/;
|
||||
WI.CodeMirrorCompletionController.CloseBracketCharactersRegex = /[)}\]]/;
|
||||
WI.CodeMirrorCompletionController.MatchingBrackets = {"{": "}", "(": ")", "[": "]", "}": "{", ")": "(", "]": "["};
|
||||
WI.CodeMirrorCompletionController.CompletionHintStyleClassName = "completion-hint";
|
||||
WI.CodeMirrorCompletionController.CompletionsHiddenDelay = 250;
|
||||
WI.CodeMirrorCompletionController.CompletionTypingDelay = 250;
|
||||
WI.CodeMirrorCompletionController.CompletionOrigin = "+completion";
|
||||
WI.CodeMirrorCompletionController.DeleteCompletionOrigin = "+delete-completion";
|
||||
Reference in New Issue
Block a user