Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/parser/node/docGenParsing.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type { SyntaxNode } from 'web-tree-sitter';
7
import { Node, TreeSitterOffsetRange } from './nodes';
8
import { _parse } from './parserWithCaching';
9
import { _getNodeMatchingSelection } from './selectionParsing';
10
import { WASMLanguage } from './treeSitterLanguages';
11
import { extractIdentifier, isDocumentableNode } from './util';
12
13
14
export type NodeToDocumentContext = {
15
16
/** is undefined when we couldn't determine the identifier */
17
nodeIdentifier: string | undefined;
18
19
nodeToDocument: Node;
20
21
/**
22
* 'expanding' - selection was expanded to a wrapping node
23
* 'matchingSelection' - node was picked by observing range overlap with the selection
24
*/
25
nodeSelectionBy: 'expanding' | 'matchingSelection';
26
};
27
28
/**
29
* Starting from the smallest AST node that wraps `selection` and climbs up the AST until it sees a "documentable" node.
30
* See {@link isDocumentableNode} for definition of a "documentable" node.
31
*
32
* @param language The language ID of the source code.
33
* @param source The source code to parse.
34
* @param selection The range to document.
35
* @returns An object containing the smallest node containing the selection range, its parent node, the node to document, and the number of nodes climbed up to reach the documentable node.
36
* Returns undefined if the language ID is not supported.
37
*/
38
export async function _getNodeToDocument(
39
language: WASMLanguage,
40
source: string,
41
selection: TreeSitterOffsetRange
42
): Promise<NodeToDocumentContext> {
43
44
const treeRef = await _parse(language, source);
45
46
try {
47
48
// if selection is non-empty, try identify a documentable AST node that most matches the selection
49
// otherwise, try to find the smallest documentable AST node that wraps the selection
50
51
const isSelectionEmpty = selection.startIndex === selection.endIndex;
52
53
const selectionMatchedNode = isSelectionEmpty ? undefined : _getNodeMatchingSelection(treeRef.tree, selection, language);
54
55
if (selectionMatchedNode) {
56
const nodeIdentifier = extractIdentifier(selectionMatchedNode, language);
57
return {
58
nodeIdentifier,
59
nodeToDocument: Node.ofSyntaxNode(selectionMatchedNode),
60
nodeSelectionBy: 'matchingSelection'
61
};
62
}
63
64
const nodeContainingCursor = treeRef.tree.rootNode.descendantForIndex(selection.startIndex, selection.endIndex);
65
66
let nodeToDocument: SyntaxNode = nodeContainingCursor;
67
let nNodesClimbedUp = 0;
68
69
// ascend the parse tree until we find a declaration/definition (documentable) node or reach the root node
70
while (!isDocumentableNode(nodeToDocument, language) && nodeToDocument.parent !== null) {
71
nodeToDocument = nodeToDocument.parent;
72
++nNodesClimbedUp;
73
}
74
75
const nodeIdentifier = extractIdentifier(nodeToDocument, language);
76
return {
77
nodeIdentifier,
78
nodeToDocument: Node.ofSyntaxNode(nodeToDocument),
79
nodeSelectionBy: 'expanding',
80
};
81
} finally {
82
treeRef.dispose();
83
}
84
}
85
86
export async function _getDocumentableNodeIfOnIdentifier(
87
language: WASMLanguage,
88
source: string,
89
range: TreeSitterOffsetRange
90
): Promise<{ identifier: string; nodeRange?: TreeSitterOffsetRange } | undefined> {
91
const treeRef = await _parse(language, source);
92
try {
93
94
const smallestNodeContainingRange = treeRef.tree.rootNode.descendantForIndex(range.startIndex, range.endIndex);
95
96
if (smallestNodeContainingRange.type.match(/identifier/) &&
97
(smallestNodeContainingRange.parent === null || isDocumentableNode(smallestNodeContainingRange.parent, language))
98
) {
99
const parent = smallestNodeContainingRange.parent;
100
101
const parentNodeRange = parent === null
102
? undefined
103
: { startIndex: parent.startIndex, endIndex: parent.endIndex };
104
105
return {
106
identifier: smallestNodeContainingRange.text,
107
nodeRange: parentNodeRange
108
};
109
}
110
} finally {
111
treeRef.dispose();
112
}
113
}
114
115