Path: blob/main/extensions/copilot/src/platform/parser/node/docGenParsing.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type { SyntaxNode } from 'web-tree-sitter';6import { Node, TreeSitterOffsetRange } from './nodes';7import { _parse } from './parserWithCaching';8import { _getNodeMatchingSelection } from './selectionParsing';9import { WASMLanguage } from './treeSitterLanguages';10import { extractIdentifier, isDocumentableNode } from './util';111213export type NodeToDocumentContext = {1415/** is undefined when we couldn't determine the identifier */16nodeIdentifier: string | undefined;1718nodeToDocument: Node;1920/**21* 'expanding' - selection was expanded to a wrapping node22* 'matchingSelection' - node was picked by observing range overlap with the selection23*/24nodeSelectionBy: 'expanding' | 'matchingSelection';25};2627/**28* Starting from the smallest AST node that wraps `selection` and climbs up the AST until it sees a "documentable" node.29* See {@link isDocumentableNode} for definition of a "documentable" node.30*31* @param language The language ID of the source code.32* @param source The source code to parse.33* @param selection The range to document.34* @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.35* Returns undefined if the language ID is not supported.36*/37export async function _getNodeToDocument(38language: WASMLanguage,39source: string,40selection: TreeSitterOffsetRange41): Promise<NodeToDocumentContext> {4243const treeRef = await _parse(language, source);4445try {4647// if selection is non-empty, try identify a documentable AST node that most matches the selection48// otherwise, try to find the smallest documentable AST node that wraps the selection4950const isSelectionEmpty = selection.startIndex === selection.endIndex;5152const selectionMatchedNode = isSelectionEmpty ? undefined : _getNodeMatchingSelection(treeRef.tree, selection, language);5354if (selectionMatchedNode) {55const nodeIdentifier = extractIdentifier(selectionMatchedNode, language);56return {57nodeIdentifier,58nodeToDocument: Node.ofSyntaxNode(selectionMatchedNode),59nodeSelectionBy: 'matchingSelection'60};61}6263const nodeContainingCursor = treeRef.tree.rootNode.descendantForIndex(selection.startIndex, selection.endIndex);6465let nodeToDocument: SyntaxNode = nodeContainingCursor;66let nNodesClimbedUp = 0;6768// ascend the parse tree until we find a declaration/definition (documentable) node or reach the root node69while (!isDocumentableNode(nodeToDocument, language) && nodeToDocument.parent !== null) {70nodeToDocument = nodeToDocument.parent;71++nNodesClimbedUp;72}7374const nodeIdentifier = extractIdentifier(nodeToDocument, language);75return {76nodeIdentifier,77nodeToDocument: Node.ofSyntaxNode(nodeToDocument),78nodeSelectionBy: 'expanding',79};80} finally {81treeRef.dispose();82}83}8485export async function _getDocumentableNodeIfOnIdentifier(86language: WASMLanguage,87source: string,88range: TreeSitterOffsetRange89): Promise<{ identifier: string; nodeRange?: TreeSitterOffsetRange } | undefined> {90const treeRef = await _parse(language, source);91try {9293const smallestNodeContainingRange = treeRef.tree.rootNode.descendantForIndex(range.startIndex, range.endIndex);9495if (smallestNodeContainingRange.type.match(/identifier/) &&96(smallestNodeContainingRange.parent === null || isDocumentableNode(smallestNodeContainingRange.parent, language))97) {98const parent = smallestNodeContainingRange.parent;99100const parentNodeRange = parent === null101? undefined102: { startIndex: parent.startIndex, endIndex: parent.endIndex };103104return {105identifier: smallestNodeContainingRange.text,106nodeRange: parentNodeRange107};108}109} finally {110treeRef.dispose();111}112}113114115