Path: blob/main/extensions/copilot/src/platform/parser/node/nodes.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 { Point, SyntaxNode } from 'web-tree-sitter';6import { BugIndicatingError } from '../../../util/vs/base/common/errors';7import { Range, Uri } from '../../../vscodeTypes';89export interface TreeSitterOffsetRange {10startIndex: number;11endIndex: number;12}1314export interface TreeSitterExpressionInfo extends TreeSitterOffsetRange {15version?: number;16identifier: string;17text: string;18}1920export interface TreeSitterExpressionLocationInfo extends TreeSitterOffsetRange {21text: string;22version?: number;23uri?: Uri;24range?: Range;25identifier?: string;26}2728export interface TreeSitterPoint {29row: number;30column: number;31}3233export interface TreeSitterPointRange {34startPosition: TreeSitterPoint;35endPosition: TreeSitterPoint;36}3738/** Util functions to deal with `TreeSitterOffsetRange` type */39export const TreeSitterOffsetRange = {40/** check if `container` contains `containee` (non-strict, ie [0, 3] contains [0, 3] */41doesContain: (container: TreeSitterOffsetRange, containee: TreeSitterOffsetRange): boolean => container.startIndex <= containee.startIndex && containee.endIndex <= container.endIndex,4243ofSyntaxNode: (n: SyntaxNode): TreeSitterOffsetRange => ({ startIndex: n.startIndex, endIndex: n.endIndex }),4445/** sort by `node.startIndex`, break ties by `node.endIndex` (so that nodes with same start index are sorted in descending order) */46compare: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): number => a.startIndex - b.startIndex || b.endIndex - a.endIndex,47isEqual: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): boolean => TreeSitterOffsetRange.compare(a, b) === 0,4849doIntersect: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange) => {50const start = Math.max(a.startIndex, b.startIndex);51const end = Math.min(a.endIndex, b.endIndex);52return start < end;53},5455len: (n: TreeSitterOffsetRange) => n.endIndex - n.startIndex,5657/** Given offset ranges [a0, a1] and [b0, b1], returns overlap size */58intersectionSize: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): number => {59const start = Math.max(a.startIndex, b.startIndex);60const end = Math.min(a.endIndex, b.endIndex);61return Math.max(end - start, 0);62},6364/** Check the given object extends TreeSitterOffsetRange */65isTreeSitterOffsetRange(obj: any): obj is TreeSitterOffsetRange {66return typeof obj.startIndex === 'number' && typeof obj.endIndex === 'number';67},68};6970export const TreeSitterPoint = {7172isEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {73return n.row === other.row && n.column === other.column;74},7576isBefore(n: TreeSitterPoint, other: TreeSitterPoint): boolean {77if (n.row < other.row || (n.row === other.row && n.column < other.column)) {78return true;79}80return false;81},8283isAfter(n: TreeSitterPoint, other: TreeSitterPoint): boolean {84return TreeSitterPoint.isBefore(other, n);85},8687isBeforeOrEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {88const isBefore = TreeSitterPoint.isBefore(n, other);89const isEqual = TreeSitterPoint.isEqual(n, other);90if (isBefore || isEqual) {91return true;92}93return false;94},9596equals(n: TreeSitterPoint, other: TreeSitterPoint): boolean {97return n.column === other.column && n.row === other.row;98},99100isAfterOrEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {101return TreeSitterPoint.isBeforeOrEqual(other, n);102},103104ofPoint: (n: Point): TreeSitterPoint => ({105row: n.row,106column: n.column107}),108};109110export const TreeSitterPointRange = {111112/** check if `container` contains `containee` (non-strict) */113doesContain: (container: TreeSitterPointRange, containee: TreeSitterPointRange): boolean => {114return TreeSitterPoint.isBeforeOrEqual(container.startPosition, containee.startPosition) && TreeSitterPoint.isAfterOrEqual(container.endPosition, containee.endPosition);115},116117equals: (a: TreeSitterPointRange, b: TreeSitterPointRange): boolean => {118return TreeSitterPoint.equals(a.startPosition, b.startPosition) && TreeSitterPoint.equals(a.endPosition, b.endPosition);119},120121ofSyntaxNode: (n: SyntaxNode): TreeSitterPointRange => ({122startPosition: n.startPosition,123endPosition: n.endPosition124}),125};126127export interface Node extends TreeSitterOffsetRange {128type: string;129}130131export const Node = {132ofSyntaxNode: (n: SyntaxNode) => ({133type: n.type,134startIndex: n.startIndex,135endIndex: n.endIndex,136}),137};138139export interface TreeSitterChunkHeaderInfo extends TreeSitterOffsetRange {140text: string;141range: TreeSitterPointRange;142}143144export const TreeSitterChunkHeaderInfo = {145ofSyntaxNode: (n: SyntaxNode): TreeSitterChunkHeaderInfo => ({146range: TreeSitterPointRange.ofSyntaxNode(n),147startIndex: n.startIndex,148text: n.text,149endIndex: n.endIndex,150}),151};152153/**154* Represents a node in the overlay tree.155*/156export class OverlayNode {157constructor(158public readonly startIndex: number,159public readonly endIndex: number,160/**161* @example `class_declaration`162*/163public kind: string, // TODO@ulugbekna: come up with more generic kinds so that these aren't per-language, then use enum?164public readonly children: OverlayNode[],165) {166if (startIndex > endIndex) {167throw new BugIndicatingError('startIndex must be less than endIndex');168}169let minStartIndex = startIndex;170for (const child of children) {171if (child.startIndex < minStartIndex) {172throw new BugIndicatingError('Invalid child startIndex');173}174if (child.endIndex > endIndex) {175throw new BugIndicatingError('Invalid child endIndex');176}177minStartIndex = Math.max(child.endIndex, minStartIndex);178}179}180181toString() {182const printedNodes: string[] = [];183function toString(node: OverlayNode, indent = '') {184printedNodes.push(`${indent}${node.kind} [${node.startIndex}, ${node.endIndex}]`);185node.children.forEach(child => toString(child, indent + ' '));186}187toString(this);188return printedNodes.join('\n');189}190}191192193