Bug 1945467 - [devtools] No longer using getScopes for inline previews in CM6 r=ochameau
Highlights of this patch - Added `getBindingScopeReferencesFinder` returns a finder function which would be used to get the location and meta details for any binding reference with the scope of the immediate function containing the specified location - Use the platform scopes to determine the scope levels with `getScopeLevels` Differential Revision: https://phabricator.services.mozilla.com/D237564
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
getSelectedScope,
|
getSelectedScope,
|
||||||
} from "../../selectors/index";
|
} from "../../selectors/index";
|
||||||
import { features } from "../../utils/prefs";
|
import { features } from "../../utils/prefs";
|
||||||
|
import { getEditor } from "../../utils/editor/index";
|
||||||
import { validateSelectedFrame } from "../../utils/context";
|
import { validateSelectedFrame } from "../../utils/context";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,8 +25,8 @@ export function generateInlinePreview(selectedFrame) {
|
|||||||
if (getSelectedFrameInlinePreviews(getState())) {
|
if (getSelectedFrameInlinePreviews(getState())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const scope = getSelectedScope(getState());
|
|
||||||
|
|
||||||
|
const scope = getSelectedScope(getState());
|
||||||
if (!scope || !scope.bindings) {
|
if (!scope || !scope.bindings) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -52,6 +53,8 @@ export function generateInlinePreview(selectedFrame) {
|
|||||||
previews[line].push(preview);
|
previews[line].push(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateSelectedFrame(getState(), selectedFrame);
|
||||||
|
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: "ADD_INLINE_PREVIEW",
|
type: "ADD_INLINE_PREVIEW",
|
||||||
selectedFrame,
|
selectedFrame,
|
||||||
@@ -63,7 +66,7 @@ export function generateInlinePreview(selectedFrame) {
|
|||||||
* Creates all the previews
|
* Creates all the previews
|
||||||
*
|
*
|
||||||
* @param {Object} selectedFrame
|
* @param {Object} selectedFrame
|
||||||
* @param {Object} scope - Scopes from the platform
|
* @param {Object} scope - Scope from the platform
|
||||||
* @param {Object} thunkArgs
|
* @param {Object} thunkArgs
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
@@ -81,46 +84,79 @@ async function getPreviews(selectedFrame, scope, thunkArgs) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalAstScopes = await parserWorker.getScopes(selectedLocation);
|
|
||||||
if (!originalAstScopes) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bailout if we resumed or moved to another frame while computing the scope
|
|
||||||
validateSelectedFrame(getState(), selectedFrame);
|
|
||||||
|
|
||||||
const allPreviews = [];
|
const allPreviews = [];
|
||||||
let level = 0;
|
if (features.codemirrorNext) {
|
||||||
while (scope && scope.bindings) {
|
// Get all the bindings for all scopes up until and including the first function scope.
|
||||||
// All the bindings from the platform environment
|
let allBindings = {};
|
||||||
const bindings = getScopeBindings(scope);
|
while (scope && scope.bindings) {
|
||||||
|
const bindings = getScopeBindings(scope);
|
||||||
|
allBindings = { ...allBindings, ...bindings };
|
||||||
|
if (scope.type === "function") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scope = scope.parent;
|
||||||
|
}
|
||||||
|
const editor = getEditor(features.codemirrorNext);
|
||||||
|
const references = await editor.getBindingReferences(
|
||||||
|
selectedLocation,
|
||||||
|
Object.keys(allBindings)
|
||||||
|
);
|
||||||
|
|
||||||
// Generate the previews for all the bindings
|
validateSelectedFrame(getState(), selectedFrame);
|
||||||
const allPreviewBindingsComplete = Object.keys(bindings).map(async name => {
|
|
||||||
// Get previews for this binding
|
for (const name in references) {
|
||||||
const previews = await generatePreviewsForBinding(
|
const previews = await generatePreviewsForBinding(
|
||||||
originalAstScopes[level]?.bindings[name],
|
references[name],
|
||||||
selectedLocation.line,
|
selectedLocation.line,
|
||||||
name,
|
name,
|
||||||
bindings[name].value,
|
allBindings[name].value,
|
||||||
client,
|
client,
|
||||||
selectedFrame.thread
|
selectedFrame.thread
|
||||||
);
|
);
|
||||||
|
|
||||||
allPreviews.push(...previews);
|
allPreviews.push(...previews);
|
||||||
});
|
|
||||||
await Promise.all(allPreviewBindingsComplete);
|
|
||||||
|
|
||||||
// Bailout if we resumed or moved to another frame while fetching the values from the backend
|
|
||||||
validateSelectedFrame(getState(), selectedFrame);
|
|
||||||
|
|
||||||
// We need to display all variables in for all block scopes up until
|
|
||||||
// and including the first function scope.
|
|
||||||
if (scope.type === "function") {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
level++;
|
} else {
|
||||||
scope = scope.parent;
|
const originalAstScopes = await parserWorker.getScopes(selectedLocation);
|
||||||
|
if (!originalAstScopes) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bailout if we resumed or moved to another frame while computing the scope
|
||||||
|
validateSelectedFrame(getState(), selectedFrame);
|
||||||
|
let level = 0;
|
||||||
|
while (scope && scope.bindings) {
|
||||||
|
// All the bindings from the platform environment
|
||||||
|
const bindings = getScopeBindings(scope);
|
||||||
|
|
||||||
|
// Generate the previews for all the bindings
|
||||||
|
const allPreviewBindingsComplete = Object.keys(bindings).map(
|
||||||
|
async name => {
|
||||||
|
// Get previews for this binding
|
||||||
|
const previews = await generatePreviewsForBinding(
|
||||||
|
originalAstScopes[level]?.bindings[name],
|
||||||
|
selectedLocation.line,
|
||||||
|
name,
|
||||||
|
bindings[name].value,
|
||||||
|
client,
|
||||||
|
selectedFrame.thread
|
||||||
|
);
|
||||||
|
|
||||||
|
allPreviews.push(...previews);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await Promise.all(allPreviewBindingsComplete);
|
||||||
|
|
||||||
|
// Bailout if we resumed or moved to another frame while fetching the values from the backend
|
||||||
|
validateSelectedFrame(getState(), selectedFrame);
|
||||||
|
|
||||||
|
// We need to display all variables in for all block scopes up until
|
||||||
|
// and including the first function scope.
|
||||||
|
if (scope.type === "function") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
level++;
|
||||||
|
scope = scope.parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return allPreviews;
|
return allPreviews;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1047,7 +1047,8 @@ class Editor extends EventEmitter {
|
|||||||
// investigate further Bug 1890895.
|
// investigate further Bug 1890895.
|
||||||
event.target.ownerGlobal.setTimeout(() => {
|
event.target.ownerGlobal.setTimeout(() => {
|
||||||
const view = editor.viewState;
|
const view = editor.viewState;
|
||||||
const cursorPos = this.#posToLineColumn(
|
const cursorPos = lezerUtils.positionToLocation(
|
||||||
|
view.state.doc,
|
||||||
view.state.selection.main.head
|
view.state.selection.main.head
|
||||||
);
|
);
|
||||||
handler(event, view, cursorPos.line, cursorPos.column);
|
handler(event, view, cursorPos.line, cursorPos.column);
|
||||||
@@ -1868,12 +1869,16 @@ class Editor extends EventEmitter {
|
|||||||
const {
|
const {
|
||||||
codemirrorLanguage: { syntaxTree },
|
codemirrorLanguage: { syntaxTree },
|
||||||
} = this.#CodeMirror6;
|
} = this.#CodeMirror6;
|
||||||
const lineObject = cm.state.doc.line(line);
|
|
||||||
const pos = lineObject.from + column;
|
const token = lezerUtils.getTreeNodeAtLocation(
|
||||||
const token = syntaxTree(cm.state).resolve(pos, 1);
|
cm.state.doc,
|
||||||
|
syntaxTree(cm.state),
|
||||||
|
{ line, column }
|
||||||
|
);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startColumn: column,
|
startColumn: column,
|
||||||
endColumn: token.to - token.from,
|
endColumn: token.to - token.from,
|
||||||
@@ -2150,7 +2155,10 @@ class Editor extends EventEmitter {
|
|||||||
handle: {
|
handle: {
|
||||||
markedSpans: markedSpans
|
markedSpans: markedSpans
|
||||||
? markedSpans.map(span => {
|
? markedSpans.map(span => {
|
||||||
const { column } = this.#posToLineColumn(cm.posAtDOM(span));
|
const { column } = lezerUtils.positionToLocation(
|
||||||
|
cm.state.doc,
|
||||||
|
cm.posAtDOM(span)
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
marker: { className: span.className },
|
marker: { className: span.className },
|
||||||
from: column,
|
from: column,
|
||||||
@@ -2198,8 +2206,8 @@ class Editor extends EventEmitter {
|
|||||||
name,
|
name,
|
||||||
klass: lezerUtils.getFunctionClass(cm.state.doc, syntaxNode),
|
klass: lezerUtils.getFunctionClass(cm.state.doc, syntaxNode),
|
||||||
location: {
|
location: {
|
||||||
start: this.#posToLineColumn(node.from),
|
start: lezerUtils.positionToLocation(cm.state.doc, node.from),
|
||||||
end: this.#posToLineColumn(node.to),
|
end: lezerUtils.positionToLocation(cm.state.doc, node.to),
|
||||||
},
|
},
|
||||||
parameterNames: lezerUtils.getFunctionParameterNames(
|
parameterNames: lezerUtils.getFunctionParameterNames(
|
||||||
cm.state.doc,
|
cm.state.doc,
|
||||||
@@ -2236,8 +2244,8 @@ class Editor extends EventEmitter {
|
|||||||
classVarDefNode.to
|
classVarDefNode.to
|
||||||
),
|
),
|
||||||
location: {
|
location: {
|
||||||
start: this.#posToLineColumn(node.from),
|
start: lezerUtils.positionToLocation(cm.state.doc, node.from),
|
||||||
end: this.#posToLineColumn(node.to),
|
end: lezerUtils.positionToLocation(cm.state.doc, node.to),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -2290,8 +2298,14 @@ class Editor extends EventEmitter {
|
|||||||
doc = cm.state.toText(sourceContent);
|
doc = cm.state.toText(sourceContent);
|
||||||
tree = lezerUtils.getTree(javascriptLanguage, sourceId, sourceContent);
|
tree = lezerUtils.getTree(javascriptLanguage, sourceId, sourceContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = lezerUtils.getTreeNodeAtLocation(doc, tree, location);
|
const token = lezerUtils.getTreeNodeAtLocation(doc, tree, location);
|
||||||
return lezerUtils.getEnclosingFunctionName(doc, token);
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enclosingScope = lezerUtils.getEnclosingFunction(doc, token);
|
||||||
|
return enclosingScope ? enclosingScope.funcName : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2321,8 +2335,8 @@ class Editor extends EventEmitter {
|
|||||||
computed: false,
|
computed: false,
|
||||||
expression: cm.state.doc.sliceString(node.from, node.to),
|
expression: cm.state.doc.sliceString(node.from, node.to),
|
||||||
location: {
|
location: {
|
||||||
start: this.#posToLineColumn(node.from),
|
start: lezerUtils.positionToLocation(cm.state.doc, node.from),
|
||||||
end: this.#posToLineColumn(node.to),
|
end: lezerUtils.positionToLocation(cm.state.doc, node.to),
|
||||||
},
|
},
|
||||||
from: node.from,
|
from: node.from,
|
||||||
to: node.to,
|
to: node.to,
|
||||||
@@ -2435,6 +2449,73 @@ class Editor extends EventEmitter {
|
|||||||
return sourceLines.filter(i => i != undefined);
|
return sourceLines.filter(i => i != undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the location and meta details of all the references
|
||||||
|
* (within the scope of the immediate enclosing function of the specified location)
|
||||||
|
* which are related to the bindings specified.
|
||||||
|
*
|
||||||
|
* @param {Object} location
|
||||||
|
* @param {Array<String>} bindings - list of binding names
|
||||||
|
* @returns {Function}
|
||||||
|
**/
|
||||||
|
async getBindingReferences(location, bindings) {
|
||||||
|
const cm = editors.get(this);
|
||||||
|
const {
|
||||||
|
codemirrorLanguage: { syntaxTree },
|
||||||
|
} = this.#CodeMirror6;
|
||||||
|
|
||||||
|
const token = lezerUtils.getTreeNodeAtLocation(
|
||||||
|
cm.state.doc,
|
||||||
|
syntaxTree(cm.state),
|
||||||
|
location
|
||||||
|
);
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enclosingScope = lezerUtils.getEnclosingFunction(
|
||||||
|
cm.state.doc,
|
||||||
|
token,
|
||||||
|
{ includeAnonymousFunctions: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!enclosingScope) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bindingReferences = {};
|
||||||
|
// This should find location and meta information for the binding name specified.
|
||||||
|
await lezerUtils.walkCursor(enclosingScope.node.cursor(), {
|
||||||
|
filterSet: lezerUtils.nodeTypeSets.bindingReferences,
|
||||||
|
enterVisitor: node => {
|
||||||
|
const bindingName = cm.state.doc.sliceString(node.from, node.to);
|
||||||
|
if (!bindings.includes(bindingName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ref = {
|
||||||
|
start: lezerUtils.positionToLocation(cm.state.doc, node.from),
|
||||||
|
end: lezerUtils.positionToLocation(cm.state.doc, node.to),
|
||||||
|
};
|
||||||
|
const syntaxNode = node.node;
|
||||||
|
// Previews for member expressions are built of the meta property which is
|
||||||
|
// reference of the child property and so on. e.g a.b.c
|
||||||
|
if (syntaxNode.parent.name == lezerUtils.nodeTypes.MemberExpression) {
|
||||||
|
ref.meta = lezerUtils.getMetaBindings(
|
||||||
|
cm.state.doc,
|
||||||
|
syntaxNode.parent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bindingReferences[bindingName]) {
|
||||||
|
bindingReferences[bindingName] = { refs: [] };
|
||||||
|
}
|
||||||
|
bindingReferences[bindingName].refs.push(ref);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return bindingReferences;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces whatever is in the text area with the contents of
|
* Replaces whatever is in the text area with the contents of
|
||||||
* the 'value' argument.
|
* the 'value' argument.
|
||||||
@@ -3396,27 +3477,6 @@ class Editor extends EventEmitter {
|
|||||||
return inXView && inYView;
|
return inXView && inYView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the line and column values the map to the codemirror offset specified.
|
|
||||||
* Used only for CM6
|
|
||||||
* @param {Number} pos - Codemirror offset
|
|
||||||
* @returns {Object} - Line column related to the position
|
|
||||||
*/
|
|
||||||
#posToLineColumn(pos) {
|
|
||||||
const cm = editors.get(this);
|
|
||||||
if (pos == null) {
|
|
||||||
return {
|
|
||||||
line: null,
|
|
||||||
column: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const line = cm.state.doc.lineAt(pos);
|
|
||||||
return {
|
|
||||||
line: line.number,
|
|
||||||
column: pos - line.from,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts line/col to CM6 offset position
|
* Converts line/col to CM6 offset position
|
||||||
* @param {Number} line - The line in the source
|
* @param {Number} line - The line in the source
|
||||||
@@ -3617,7 +3677,10 @@ class Editor extends EventEmitter {
|
|||||||
return { text: "", line: -1, column: -1 };
|
return { text: "", line: -1, column: -1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursorPosition = this.#posToLineColumn(cursor.to);
|
const cursorPosition = lezerUtils.positionToLocation(
|
||||||
|
cm.state.doc,
|
||||||
|
cursor.to
|
||||||
|
);
|
||||||
// The lines in CM6 are 1 based while CM5 is 0 based
|
// The lines in CM6 are 1 based while CM5 is 0 based
|
||||||
return {
|
return {
|
||||||
text: cursor.match[0],
|
text: cursor.match[0],
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ const nodeTypeSets = {
|
|||||||
numberAndProperty: new Set([nodeTypes.PropertyDefinition, nodeTypes.Number]),
|
numberAndProperty: new Set([nodeTypes.PropertyDefinition, nodeTypes.Number]),
|
||||||
memberExpression: new Set([nodeTypes.MemberExpression]),
|
memberExpression: new Set([nodeTypes.MemberExpression]),
|
||||||
classes: new Set([nodeTypes.ClassDeclaration, nodeTypes.ClassExpression]),
|
classes: new Set([nodeTypes.ClassDeclaration, nodeTypes.ClassExpression]),
|
||||||
|
bindingReferences: new Set([
|
||||||
|
nodeTypes.VariableDefinition,
|
||||||
|
nodeTypes.VariableName,
|
||||||
|
]),
|
||||||
|
expressionProperty: new Set([nodeTypes.PropertyName]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const ast = new Map();
|
const ast = new Map();
|
||||||
@@ -133,24 +138,41 @@ function clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of the function which immediately encloses the node (representing a location)
|
* Gets the node and the function name which immediately encloses the node (representing a location)
|
||||||
*
|
*
|
||||||
* @param {Object} doc - The codemirror document used to retrive the part of content
|
* @param {Object} doc - The codemirror document used to retrive the part of content
|
||||||
* @param {Object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
|
* @param {Object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
|
||||||
|
* @params {Object} options
|
||||||
|
* options.includeAnonymousFunctions - if true, allow matching anonymous functions
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function getEnclosingFunctionName(doc, node) {
|
function getEnclosingFunction(
|
||||||
|
doc,
|
||||||
|
node,
|
||||||
|
options = { includeAnonymousFunctions: false }
|
||||||
|
) {
|
||||||
let parentNode = node.parent;
|
let parentNode = node.parent;
|
||||||
while (parentNode !== null) {
|
while (parentNode !== null) {
|
||||||
if (nodeTypeSets.functionsVarDecl.has(parentNode.name)) {
|
if (nodeTypeSets.functionsVarDecl.has(parentNode.name)) {
|
||||||
|
// For anonymous functions, we use variable declarations, but we only care about variable declarations which are part of function expressions
|
||||||
|
if (
|
||||||
|
parentNode.name == nodeTypes.VariableDeclaration &&
|
||||||
|
!hasChildNodeOfType(parentNode.node, nodeTypeSets.functionExpressions)
|
||||||
|
) {
|
||||||
|
parentNode = parentNode.parent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const funcName = getFunctionName(doc, parentNode);
|
const funcName = getFunctionName(doc, parentNode);
|
||||||
if (funcName) {
|
if (funcName || options.includeAnonymousFunctions) {
|
||||||
return funcName;
|
return {
|
||||||
|
node: parentNode,
|
||||||
|
funcName,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentNode = parentNode.parent;
|
parentNode = parentNode.parent;
|
||||||
}
|
}
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,9 +184,36 @@ function getEnclosingFunctionName(doc, node) {
|
|||||||
* @returns {Object} node - https://lezer.codemirror.net/docs/ref/#common.SyntaxNodeRef
|
* @returns {Object} node - https://lezer.codemirror.net/docs/ref/#common.SyntaxNodeRef
|
||||||
*/
|
*/
|
||||||
function getTreeNodeAtLocation(doc, tree, location) {
|
function getTreeNodeAtLocation(doc, tree, location) {
|
||||||
const line = doc.line(location.line);
|
try {
|
||||||
const pos = line.from + location.column;
|
const line = doc.line(location.line);
|
||||||
return tree.resolve(pos, 1);
|
const pos = line.from + location.column;
|
||||||
|
return tree.resolve(pos, 1);
|
||||||
|
} catch (e) {
|
||||||
|
// if the line is not found in the document doc.line() will throw
|
||||||
|
console.warn(e.message);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts Codemirror position to valid source location. Used only for CM6
|
||||||
|
*
|
||||||
|
* @param {Object} doc - The Codemirror document used to retrive the part of content
|
||||||
|
* @param {Number} pos - Codemirror offset
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function positionToLocation(doc, pos) {
|
||||||
|
if (pos == null) {
|
||||||
|
return {
|
||||||
|
line: null,
|
||||||
|
column: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const line = doc.lineAt(pos);
|
||||||
|
return {
|
||||||
|
line: line.number,
|
||||||
|
column: pos - line.from,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -349,6 +398,28 @@ function getFunctionClass(doc, node) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the meta data for member expression nodes
|
||||||
|
*
|
||||||
|
* @param {Object} doc - The codemirror document used to retrieve the part of content
|
||||||
|
* @param {Object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getMetaBindings(doc, node) {
|
||||||
|
if (!node || node.name !== nodeTypes.MemberExpression) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memExpr = doc.sliceString(node.from, node.to).split(".");
|
||||||
|
return {
|
||||||
|
type: "member",
|
||||||
|
start: positionToLocation(doc, node.from),
|
||||||
|
end: positionToLocation(doc, node.to),
|
||||||
|
property: memExpr.at(-1),
|
||||||
|
parent: getMetaBindings(doc, node.parent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk the syntax tree of the langauge provided
|
* Walk the syntax tree of the langauge provided
|
||||||
*
|
*
|
||||||
@@ -380,15 +451,34 @@ async function walkTree(view, language, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enables walking a specific part of the syntax tree using the cursor
|
||||||
|
* provided by the node (which is the parent)
|
||||||
|
* @param {Object} cursor - https://lezer.codemirror.net/docs/ref/#common.TreeCursor
|
||||||
|
* @param {Object} options
|
||||||
|
* {Function} options.enterVisitor - A function that is called when a node is entered
|
||||||
|
* {Set} options.filterSet - A set of node types which should be visited, all others should be ignored
|
||||||
|
*/
|
||||||
|
async function walkCursor(cursor, options) {
|
||||||
|
await cursor.iterate(node => {
|
||||||
|
if (options.filterSet?.has(node.name)) {
|
||||||
|
options.enterVisitor(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getFunctionName,
|
getFunctionName,
|
||||||
getFunctionParameterNames,
|
getFunctionParameterNames,
|
||||||
getFunctionClass,
|
getFunctionClass,
|
||||||
getEnclosingFunctionName,
|
getEnclosingFunction,
|
||||||
getTreeNodeAtLocation,
|
getTreeNodeAtLocation,
|
||||||
|
getMetaBindings,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
nodeTypeSets,
|
nodeTypeSets,
|
||||||
walkTree,
|
walkTree,
|
||||||
getTree,
|
getTree,
|
||||||
clear,
|
clear,
|
||||||
|
walkCursor,
|
||||||
|
positionToLocation,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function containsLocation(parentLocation, childLocation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getInnerLocations(locations, position) {
|
function getInnerLocations(locations, position) {
|
||||||
// First, find the function which directly contains the specified position (line / column)
|
// First, find the function which directly contains the specified position (line / column)
|
||||||
let parentIndex;
|
let parentIndex;
|
||||||
for (let i = locations.length - 1; i >= 0; i--) {
|
for (let i = locations.length - 1; i >= 0; i--) {
|
||||||
if (containsPosition(locations[i], position)) {
|
if (containsPosition(locations[i], position)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user