Path: blob/main/extensions/copilot/src/platform/parser/node/selectionParsing.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 { SyntaxNode, Tree } from 'web-tree-sitter';6import { max } from '../../../util/common/arrays';7import { TreeSitterOffsetRange } from './nodes';8import { WASMLanguage } from './treeSitterLanguages';9import { isDocumentableNode } from './util';1011/**12* This function is used to find the most relevant node to document in a parse tree.13* It traverses the parse tree and keeps track of documentable nodes that could be used to generate documentation.14* The relevance of a node is determined by its intersection with a given range (containerRange).15* The function rewards nodes for which the overlap size constitutes a large part of the node and penalizes nodes for non-overlapping range.16*17* @remarks Exported for testing purposes only.18*19* @param parseTree - The parse tree to search for the most relevant node.20* @param containerRange - The range to intersect with the nodes of the parse tree.21* @param language - The language identifier used to determine if a node is documentable.22* @returns The most relevant node to document or undefined if no such node is found.23*/24export function _getNodeMatchingSelection(parseTree: Tree, containerRange: TreeSitterOffsetRange, language: WASMLanguage, match: (node: SyntaxNode, language: WASMLanguage) => RegExpMatchArray | null = isDocumentableNode): SyntaxNode | undefined {2526// nodes to explore27let frontier = [parseTree.rootNode];2829// keeps documentable nodes that could be used to generate documentation for30const documentableNodes: [SyntaxNode, /* weight (higher better) */ number][] = [];3132while (true) {33// nodes that intersect with `containerRange`34const candidates = frontier35.map((node): [SyntaxNode, number] => [node, TreeSitterOffsetRange.intersectionSize(node, containerRange)])36.filter(([_, s]) => s > 0)37.sort(([_, s0], [__, s1]) => s1 - s0);3839if (candidates.length === 0) {40return documentableNodes.length === 041? undefined42: max(documentableNodes, ([_, s0], [__, s1]) => s0 - s1)![0];43} else {44const reweighedCandidates = candidates45.map(([n, overlapSize]): [SyntaxNode, number] => {46const nLen = TreeSitterOffsetRange.len(n);47const nonOverlappingSize = Math.abs(TreeSitterOffsetRange.len(containerRange) - overlapSize);48// reward overlap size but penalize for non-overlapping range49const penalizedWeigth = overlapSize - nonOverlappingSize;50const normalizedPenalizedWeight = penalizedWeigth / nLen;51return [n, normalizedPenalizedWeight];52});5354documentableNodes.push(...reweighedCandidates.filter(([node, _]) => match(node, language)));5556frontier = [];57frontier.push(...reweighedCandidates.flatMap(([n, s]) => n.children));58}59}60}616263