diff --git a/server/src/cache/cache.ts b/server/src/cache/cache.ts new file mode 100644 index 00000000..0874730b --- /dev/null +++ b/server/src/cache/cache.ts @@ -0,0 +1,26 @@ +export class Cache { + private readonly data: Map + + public constructor() { + this.data = new Map() + } + + public cached(key: TKey, cb: () => TValue): TValue { + const cached = this.data.get(key) + if (cached !== undefined) { + return cached + } + + const value = cb() + this.data.set(key, value) + return value + } + + public clear(): void { + this.data.clear() + } + + public get size(): number { + return this.data.size + } +} diff --git a/server/src/completion/CompletionItemAdditionalInformation.ts b/server/src/completion/CompletionItemAdditionalInformation.ts new file mode 100644 index 00000000..b37f9f1f --- /dev/null +++ b/server/src/completion/CompletionItemAdditionalInformation.ts @@ -0,0 +1,8 @@ +import {File} from "@server/psi/File" + +export interface CompletionItemAdditionalInformation { + readonly name: string | undefined + readonly file: File | undefined + readonly elementFile: File | undefined + readonly language: "tolk" | "func" | undefined +} diff --git a/server/src/completion/CompletionProvider.ts b/server/src/completion/CompletionProvider.ts new file mode 100644 index 00000000..e7e17361 --- /dev/null +++ b/server/src/completion/CompletionProvider.ts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2025 TON Studio +import {CompletionResult} from "@server/completion/WeightedCompletionItem" + +export interface CompletionProvider { + isAvailable(ctx: Ctx): boolean + + addCompletion(ctx: Ctx, result: CompletionResult): void +} + +export interface AsyncCompletionProvider { + isAvailable(ctx: Ctx): boolean + + addCompletion(ctx: Ctx, result: CompletionResult): Promise +} diff --git a/server/src/languages/tolk/completion/WeightedCompletionItem.ts b/server/src/completion/WeightedCompletionItem.ts similarity index 100% rename from server/src/languages/tolk/completion/WeightedCompletionItem.ts rename to server/src/completion/WeightedCompletionItem.ts diff --git a/server/src/files.ts b/server/src/files.ts index 08904b2c..9e309e30 100644 --- a/server/src/files.ts +++ b/server/src/files.ts @@ -153,9 +153,6 @@ export const isTlbFile = ( event?: lsp.TextDocumentChangeEvent, ): boolean => event?.document.languageId === "tlb" || uri.endsWith(".tlb") -// export function filePathToUri(filePath: string): string { -// return pathToFileURL(filePath).href -// } export const filePathToUri = (filePath: string): string => { const url = pathToFileURL(filePath).toString() return url.replace(/c:/g, "c%3A").replace(/d:/g, "d%3A") diff --git a/server/src/foldings/index.ts b/server/src/foldings/index.ts new file mode 100644 index 00000000..3551829c --- /dev/null +++ b/server/src/foldings/index.ts @@ -0,0 +1,13 @@ +import type {Point} from "web-tree-sitter" +import type * as lsp from "vscode-languageserver" +import {FoldingRangeKind} from "vscode-languageserver-types" + +export function genericFolding(start: Point, end: Point): lsp.FoldingRange { + return { + kind: FoldingRangeKind.Region, + startLine: start.row, + endLine: end.row - 1, + startCharacter: end.column, + endCharacter: end.column, + } +} diff --git a/server/src/func-indexing-root.ts b/server/src/indexing/indexing.ts similarity index 63% rename from server/src/func-indexing-root.ts rename to server/src/indexing/indexing.ts index 4d37bbf9..aaa57ad2 100644 --- a/server/src/func-indexing-root.ts +++ b/server/src/indexing/indexing.ts @@ -1,25 +1,23 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import * as path from "node:path" -import {glob} from "glob" -import {index} from "@server/languages/func/indexes" import {fileURLToPath} from "node:url" -import {filePathToUri, findFuncFile} from "@server/files" +import {glob} from "glob" +import * as path from "node:path" +import {filePathToUri} from "@server/files" -export enum FuncIndexingRootKind { +export enum IndexingRootKind { Stdlib = "stdlib", Workspace = "workspace", } -export class FuncIndexingRoot { - public constructor( +export abstract class IndexingRoot { + protected constructor( public root: string, - public kind: FuncIndexingRootKind, + public extensions: string[], + public kind: IndexingRootKind, ) {} public async index(): Promise { const ignore = - this.kind === FuncIndexingRootKind.Stdlib + this.kind === IndexingRootKind.Stdlib ? [] : [ ".git/**", @@ -30,7 +28,11 @@ export class FuncIndexingRoot { ] const rootDir = fileURLToPath(this.root) - const files = await glob("**/*.{fc,func}", { + + const globPattern = + this.extensions.length === 1 ? this.extensions[0] : `{${this.extensions.join(",")}}` + + const files = await glob(`**/*.${globPattern}`, { cwd: rootDir, ignore: ignore, }) @@ -41,8 +43,9 @@ export class FuncIndexingRoot { console.info("Indexing:", filePath) const absPath = path.join(rootDir, filePath) const uri = filePathToUri(absPath) - const file = await findFuncFile(uri) - index.addFile(uri, file, false) + await this.onFile(uri) } } + + protected abstract onFile(uri: string): Promise } diff --git a/server/src/languages/fift/foldings/collect.ts b/server/src/languages/fift/foldings/collect.ts index 82210e63..947eba55 100644 --- a/server/src/languages/fift/foldings/collect.ts +++ b/server/src/languages/fift/foldings/collect.ts @@ -1,24 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio -import {FoldingRange, FoldingRangeKind} from "vscode-languageserver-types" +import {FoldingRange} from "vscode-languageserver-types" import {RecursiveVisitor} from "@server/visitor/visitor" -import type {Point} from "web-tree-sitter" -import type * as lsp from "vscode-languageserver" import {FiftFile} from "@server/languages/fift/psi/FiftFile" +import {genericFolding} from "@server/foldings" export function provideFiftFoldingRanges(file: FiftFile): FoldingRange[] { const result: FoldingRange[] = [] - const genericFolding = (start: Point, end: Point): lsp.FoldingRange => { - return { - kind: FoldingRangeKind.Region, - startLine: start.row, - endLine: end.row - 1, - startCharacter: end.column, - endCharacter: end.column, - } - } - RecursiveVisitor.visit(file.rootNode, (n): boolean => { if ( n.type === "program" || diff --git a/server/src/languages/fift/semantic-tokens/index.ts b/server/src/languages/fift/semantic-tokens/index.ts index 7ecdf1fd..dcb91235 100644 --- a/server/src/languages/fift/semantic-tokens/index.ts +++ b/server/src/languages/fift/semantic-tokens/index.ts @@ -2,12 +2,10 @@ // Copyright © 2025 TON Studio import type {SemanticTokens} from "vscode-languageserver" import {RecursiveVisitor} from "@server/visitor/visitor" -import {SemanticTokensBuilder} from "vscode-languageserver/lib/common/semanticTokens" import {SemanticTokenTypes} from "vscode-languageserver-protocol" -import type {Node as SyntaxNode} from "web-tree-sitter" -import * as lsp from "vscode-languageserver" import {FiftReference} from "@server/languages/fift/psi/FiftReference" import {FiftFile} from "@server/languages/fift/psi/FiftFile" +import {Tokens} from "@server/semantic/tokens" export function provideFiftSemanticTokens( file: FiftFile, @@ -15,21 +13,9 @@ export function provideFiftSemanticTokens( enabled: boolean }, ): SemanticTokens | null { - if (!settings.enabled) { - return null - } - - const builder = new SemanticTokensBuilder() + if (!settings.enabled) return null - function pushToken(n: SyntaxNode, tokenType: lsp.SemanticTokenTypes): void { - builder.push( - n.startPosition.row, - n.startPosition.column, - n.endPosition.column - n.startPosition.column, - Object.keys(lsp.SemanticTokenTypes).indexOf(tokenType.toString()), - 0, - ) - } + const tokens = new Tokens() RecursiveVisitor.visit(file.rootNode, (node): boolean => { if ( @@ -41,7 +27,7 @@ export function provideFiftSemanticTokens( ) { const nameNode = node.childForFieldName("name") if (nameNode) { - pushToken(nameNode, SemanticTokenTypes.function) + tokens.node(nameNode, SemanticTokenTypes.function) } } @@ -52,12 +38,15 @@ export function provideFiftSemanticTokens( ) { const def = FiftReference.resolve(node, file) if (def) { - pushToken(node, SemanticTokenTypes.function) + tokens.node(node, SemanticTokenTypes.function) } } return true }) - return builder.build() + return { + resultId: Date.now().toString(), + data: tokens.result(), + } } diff --git a/server/src/languages/func/cache.ts b/server/src/languages/func/cache.ts index 8f7fe7fa..c5dcb0d6 100644 --- a/server/src/languages/func/cache.ts +++ b/server/src/languages/func/cache.ts @@ -1,40 +1,10 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core import type {NamedNode} from "@server/languages/func/psi/FuncNode" +import {Cache} from "@server/cache/cache" -export class Cache { - private readonly data: Map - - public constructor() { - this.data = new Map() - } - - public cached(key: TKey, cb: () => TValue): TValue { - const cached = this.data.get(key) - if (cached !== undefined) { - return cached - } - - const value = cb() - this.data.set(key, value) - return value - } - - public clear(): void { - this.data.clear() - } - - public get size(): number { - return this.data.size - } -} - -export class CacheManager { - public readonly resolveCache: Cache - - public constructor() { - this.resolveCache = new Cache() - } +export class FuncCache { + public readonly resolveCache: Cache = new Cache() public clear(): void { console.info(`Clearing caches (resolve: ${this.resolveCache.size})`) @@ -42,4 +12,4 @@ export class CacheManager { } } -export const FUNC_CACHE = new CacheManager() +export const FUNC_CACHE = new FuncCache() diff --git a/server/src/languages/func/completion/CompletionProvider.ts b/server/src/languages/func/completion/CompletionProvider.ts deleted file mode 100644 index 300bc9cd..00000000 --- a/server/src/languages/func/completion/CompletionProvider.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {CompletionContext} from "./CompletionContext" -import {CompletionResult} from "@server/languages/func/completion/WeightedCompletionItem" - -export interface CompletionProvider { - isAvailable(ctx: CompletionContext): boolean - addCompletion(ctx: CompletionContext, result: CompletionResult): void -} - -export interface AsyncCompletionProvider { - isAvailable(ctx: CompletionContext): boolean - addCompletion(ctx: CompletionContext, result: CompletionResult): Promise -} diff --git a/server/src/languages/func/completion/ReferenceCompletionProcessor.ts b/server/src/languages/func/completion/ReferenceCompletionProcessor.ts index cd7fe9c5..c64d1219 100644 --- a/server/src/languages/func/completion/ReferenceCompletionProcessor.ts +++ b/server/src/languages/func/completion/ReferenceCompletionProcessor.ts @@ -12,19 +12,9 @@ import { TypeParameter, } from "@server/languages/func/psi/Decls" import {CompletionContext} from "./CompletionContext" -import { - CompletionWeight, - WeightedCompletionItem, -} from "@server/languages/func/completion/WeightedCompletionItem" -import {FuncFile} from "@server/languages/func/psi/FuncFile" +import {CompletionWeight, WeightedCompletionItem} from "@server/completion/WeightedCompletionItem" import {ResolveState} from "@server/psi/ResolveState" - -export interface CompletionItemAdditionalInformation { - readonly name: string | undefined - readonly file: FuncFile | undefined - readonly elementFile: FuncFile | undefined - readonly language: "tolk" | "func" | undefined -} +import {CompletionItemAdditionalInformation} from "@server/completion/CompletionItemAdditionalInformation" export class ReferenceCompletionProcessor implements ScopeProcessor { public constructor(private readonly ctx: CompletionContext) {} diff --git a/server/src/languages/func/completion/WeightedCompletionItem.ts b/server/src/languages/func/completion/WeightedCompletionItem.ts deleted file mode 100644 index ed63a1e2..00000000 --- a/server/src/languages/func/completion/WeightedCompletionItem.ts +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {CompletionItem} from "vscode-languageserver-types" - -export enum CompletionWeight { - CONTEXT_ELEMENT = 0, - VARIABLE = 50, - PARAM = 60, - KEYWORD = 80, - FUNCTION = 90, - SNIPPET = 95, - CONSTANT = 100, - GLOBAL_VARIABLE = 105, - LOWEST = 500, -} - -export function contextWeight(weight: CompletionWeight, match: boolean): CompletionWeight { - if (match) return weight - return weight + 500 -} - -// eslint-disable-next-line functional/type-declaration-immutability -export type WeightedCompletionItem = CompletionItem & { - readonly weight?: CompletionWeight -} - -export class CompletionResult { - public elements: WeightedCompletionItem[] = [] - - public add(...element: WeightedCompletionItem[]): void { - this.elements.push(...element) - } - - public sorted(): CompletionItem[] { - if (this.elements.length === 0) return [] - - const sorted = this.elements.sort((a, b) => { - if (a.weight === undefined || b.weight === undefined) return 0 - return a.weight - b.weight - }) - - let groupIndex = 0 - let lastWeight = sorted[0].weight ?? 0 - - for (const item of sorted) { - const weight = item.weight as number - if (lastWeight !== weight) { - groupIndex++ - lastWeight = weight - } - - item.sortText = groupIndex.toString() - } - - return sorted - } -} diff --git a/server/src/languages/func/completion/index.ts b/server/src/languages/func/completion/index.ts index 98c56baa..4a60aadc 100644 --- a/server/src/languages/func/completion/index.ts +++ b/server/src/languages/func/completion/index.ts @@ -9,11 +9,11 @@ import {asParserPoint} from "@server/utils/position" import {NamedNode} from "@server/languages/func/psi/FuncNode" import {Reference} from "@server/languages/func/psi/Reference" import {CompletionContext} from "@server/languages/func/completion/CompletionContext" -import {CompletionResult} from "@server/languages/func/completion/WeightedCompletionItem" +import {CompletionResult} from "@server/completion/WeightedCompletionItem" import type { AsyncCompletionProvider, CompletionProvider, -} from "@server/languages/func/completion/CompletionProvider" +} from "@server/completion/CompletionProvider" import {ReferenceCompletionProvider} from "@server/languages/func/completion/providers/ReferenceCompletionProvider" import {ImportPathCompletionProvider} from "@server/languages/func/completion/providers/ImportPathCompletionProvider" import {TopLevelCompletionProvider} from "@server/languages/func/completion/providers/TopLevelCompletionProvider" @@ -83,7 +83,7 @@ export async function provideFuncCompletion( ) const result = new CompletionResult() - const providers: CompletionProvider[] = [ + const providers: CompletionProvider[] = [ new ReferenceCompletionProvider(ref), new TopLevelCompletionProvider(), ] @@ -93,7 +93,9 @@ export async function provideFuncCompletion( provider.addCompletion(ctx, result) } - const asyncProviders: AsyncCompletionProvider[] = [new ImportPathCompletionProvider()] + const asyncProviders: AsyncCompletionProvider[] = [ + new ImportPathCompletionProvider(), + ] for (const provider of asyncProviders) { if (!provider.isAvailable(ctx)) continue diff --git a/server/src/languages/func/completion/providers/ImportPathCompletionProvider.ts b/server/src/languages/func/completion/providers/ImportPathCompletionProvider.ts index cad73cec..0f2259f7 100644 --- a/server/src/languages/func/completion/providers/ImportPathCompletionProvider.ts +++ b/server/src/languages/func/completion/providers/ImportPathCompletionProvider.ts @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import {AsyncCompletionProvider} from "@server/languages/func/completion/CompletionProvider" +import {AsyncCompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/func/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/func/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import * as path from "node:path" import {globalVFS} from "@server/vfs/global" import {listDirs, listFiles} from "@server/vfs/vfs" @@ -14,7 +11,7 @@ import {filePathToUri} from "@server/files" import {FuncFile} from "@server/languages/func/psi/FuncFile" import {trimSuffix} from "@server/utils/strings" -export class ImportPathCompletionProvider implements AsyncCompletionProvider { +export class ImportPathCompletionProvider implements AsyncCompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.insideImport } diff --git a/server/src/languages/func/completion/providers/ReferenceCompletionProvider.ts b/server/src/languages/func/completion/providers/ReferenceCompletionProvider.ts index 0718fcd6..73d0e3fb 100644 --- a/server/src/languages/func/completion/providers/ReferenceCompletionProvider.ts +++ b/server/src/languages/func/completion/providers/ReferenceCompletionProvider.ts @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/func/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/func/completion/CompletionContext" import {Reference} from "@server/languages/func/psi/Reference" import {ReferenceCompletionProcessor} from "@server/languages/func/completion/ReferenceCompletionProcessor" -import type {CompletionResult} from "@server/languages/func/completion/WeightedCompletionItem" +import type {CompletionResult} from "@server/completion/WeightedCompletionItem" import {ResolveState} from "@server/psi/ResolveState" -export class ReferenceCompletionProvider implements CompletionProvider { +export class ReferenceCompletionProvider implements CompletionProvider { public constructor(private readonly ref: Reference) {} public isAvailable(ctx: CompletionContext): boolean { diff --git a/server/src/languages/func/completion/providers/TopLevelCompletionProvider.ts b/server/src/languages/func/completion/providers/TopLevelCompletionProvider.ts index 88f08020..0c42e19c 100644 --- a/server/src/languages/func/completion/providers/TopLevelCompletionProvider.ts +++ b/server/src/languages/func/completion/providers/TopLevelCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" -import type {CompletionProvider} from "@server/languages/func/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/func/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/func/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class TopLevelCompletionProvider implements CompletionProvider { +export class TopLevelCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.topLevel } diff --git a/server/src/languages/func/documentation/documentation.ts b/server/src/languages/func/documentation/documentation.ts index 9ba81971..1e04c173 100644 --- a/server/src/languages/func/documentation/documentation.ts +++ b/server/src/languages/func/documentation/documentation.ts @@ -5,9 +5,6 @@ import {Constant, Func, GlobalVariable, TypeParameter} from "@server/languages/f import {parentOfType} from "@server/psi/utils" import type {Node as SyntaxNode} from "web-tree-sitter" -const CODE_FENCE = "```" -const DOC_TMPL = `${CODE_FENCE}func\n{signature}\n${CODE_FENCE}\n{documentation}\n` - /** * Returns the documentation for the given symbol in Markdown format, or null * if there is no documentation for the element. @@ -95,5 +92,7 @@ export function generateFuncDocFor(node: NamedNode, _place: SyntaxNode): string } function defaultResult(signature: string, documentation: string = ""): string { + const CODE_FENCE = "```" + const DOC_TMPL = `${CODE_FENCE}func\n{signature}\n${CODE_FENCE}\n{documentation}\n` return DOC_TMPL.replace("{signature}", signature).replace("{documentation}", documentation) } diff --git a/server/src/languages/func/documentation/index.ts b/server/src/languages/func/documentation/index.ts index 364284dd..deeae913 100644 --- a/server/src/languages/func/documentation/index.ts +++ b/server/src/languages/func/documentation/index.ts @@ -19,19 +19,7 @@ export function provideFuncDocumentation(hoverNode: SyntaxNode, file: FuncFile): } const res = Reference.resolve(NamedNode.create(hoverNode, file)) - if (res === null) { - if (process.env["TON_LS_DEV"] !== "true") { - return null - } - - return { - range: asLspRange(hoverNode), - contents: { - kind: "plaintext", - value: hoverNode.type, - }, - } - } + if (res === null) return null const doc = docs.generateFuncDocFor(res, hoverNode) if (doc === null) return null diff --git a/server/src/languages/func/foldings/index.ts b/server/src/languages/func/foldings/index.ts index c4b95d3e..f3df823f 100644 --- a/server/src/languages/func/foldings/index.ts +++ b/server/src/languages/func/foldings/index.ts @@ -1,24 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import {FoldingRange, FoldingRangeKind} from "vscode-languageserver-types" +import {FoldingRange} from "vscode-languageserver-types" import {RecursiveVisitor} from "@server/visitor/visitor" -import type {Point} from "web-tree-sitter" -import type * as lsp from "vscode-languageserver" import {FuncFile} from "@server/languages/func/psi/FuncFile" +import {genericFolding} from "@server/foldings" export function provideFuncFoldingRanges(file: FuncFile): FoldingRange[] { const result: FoldingRange[] = [] - const genericFolding = (start: Point, end: Point): lsp.FoldingRange => { - return { - kind: FoldingRangeKind.Region, - startLine: start.row, - endLine: end.row - 1, - startCharacter: end.column, - endCharacter: end.column, - } - } - RecursiveVisitor.visit(file.rootNode, (n): boolean => { if (n.type === "block_statement") { const openBrace = n.firstChild diff --git a/server/src/languages/func/indexing-root.ts b/server/src/languages/func/indexing-root.ts new file mode 100644 index 00000000..e6470b8c --- /dev/null +++ b/server/src/languages/func/indexing-root.ts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2025 TON Studio +import {index} from "@server/languages/func/indexes" +import {findFuncFile} from "@server/files" +import {IndexingRoot, IndexingRootKind} from "@server/indexing/indexing" + +export class FuncIndexingRoot extends IndexingRoot { + public constructor(root: string, kind: IndexingRootKind) { + super(root, ["fc", "func"], kind) + } + + protected override async onFile(uri: string): Promise { + const file = await findFuncFile(uri) + index.addFile(uri, file, false) + } +} diff --git a/server/src/languages/func/psi/Referent.ts b/server/src/languages/func/psi/Referent.ts index e721a4b0..528e0627 100644 --- a/server/src/languages/func/psi/Referent.ts +++ b/server/src/languages/func/psi/Referent.ts @@ -6,148 +6,27 @@ import {NamedNode, FuncNode} from "./FuncNode" import {Reference} from "./Reference" import type {FuncFile} from "./FuncFile" import {parentOfType} from "@server/psi/utils" +import {BaseReferent, GlobalSearchScope, LocalSearchScope} from "@server/references/referent" +import {File} from "@server/psi/File" import {FUNC_PARSED_FILES_CACHE} from "@server/files" -/** - * Describes a scope that contains all possible uses of a certain symbol. - */ -export interface SearchScope { - toString(): string -} - -/** - * Describes the scope described by some AST node, the search for usages will be - * performed only within this node. - * - * For example, the scope for a local variable will be the block in which it is defined. - */ -export class LocalSearchScope implements SearchScope { - public constructor(public node: SyntaxNode) {} - - public toString(): string { - return `LocalSearchScope:\n${this.node.text}` - } -} - -/** - * Describes a scope consisting of one or more files. - * - * For example, the scope of a global function from the standard library is all project files. - */ -export class GlobalSearchScope implements SearchScope { - public static allFiles(): GlobalSearchScope { +class FuncGlobalSearchScope extends GlobalSearchScope { + public static allFiles(): GlobalSearchScope { const files = [...FUNC_PARSED_FILES_CACHE.values()] return new GlobalSearchScope(files) } - - public constructor(public files: FuncFile[]) {} - - public toString(): string { - return `GlobalSearchScope:\n${this.files.map(f => `- ${f.uri}`).join("\n")}` - } } -export interface FindReferenceOptions { - /** - * if true, the first element of the result contains the definition - */ - readonly includeDefinition?: boolean - /** - * if true, don't include `self` as usages (for rename) - */ - readonly includeSelf?: boolean - /** - * if true, only references from the same files listed - */ - readonly sameFileOnly?: boolean - /** - * search stops after `limit` number of references are found - */ - readonly limit?: number -} - -/** - * Referent encapsulates the logic for finding all references to a definition. - * - * The search logic is simple, each symbol has a certain scope in which it can be used. - * If it is a local variable, then the block in which it is defined, if a parameter, then - * the function in which it is defined. If it is a global function, then all project files. - * - * When the scope is defined, it is enough to go through all the nodes from it and find those - * that refer to the searched element. - * For optimization, we do not try to resolve each identifier, we resolve only those that have - * the same name as the searched element (and a bit of logic for processing `self`). - * - * Searching for uses of global symbols can be improved, now we use all files from the index, - * but following the Tolk imports logic, we can reduce the search scope. For example, when searching - * for uses of a global function defined within the project, there is no point in searching - * for its uses within the standard library. - * These optimizations and improvements are the object of further work. - */ -export class Referent { - private readonly resolved: NamedNode | null = null - private readonly file: FuncFile +export class Referent extends BaseReferent { + public readonly resolved: NamedNode | null = null public constructor(node: SyntaxNode, file: FuncFile) { - this.file = file + super(file) const element = new NamedNode(node, file) this.resolved = Reference.resolve(element) } - /** - * Returns a list of nodes that reference the definition. - */ - public findReferences({ - includeDefinition = false, - sameFileOnly = false, - limit = Infinity, - }: FindReferenceOptions): FuncNode[] { - const resolved = this.resolved - if (!resolved) return [] - - const useScope = this.useScope() - if (!useScope) return [] - - const result: FuncNode[] = [] - if (includeDefinition && (!sameFileOnly || resolved.file.uri === this.file.uri)) { - const nameNode = resolved.nameNode() - if (nameNode) { - result.push(nameNode) - } - } - - this.searchInScope(useScope, sameFileOnly, result, limit) - return result - } - - private searchInScope( - scope: SearchScope, - sameFileOnly: boolean, - result: FuncNode[], - limit: number, - ): void { - if (!this.resolved) return - - if (scope instanceof LocalSearchScope) { - this.traverseTree(this.resolved.file, scope.node, result, limit) - } - - if (scope instanceof GlobalSearchScope) { - if (sameFileOnly) { - this.traverseTree(this.file, this.file.rootNode, result, limit) - return - } - - for (const file of scope.files) { - this.traverseTree(file, file.rootNode, result, limit) - if (result.length === limit) { - break - } - } - } - } - - private traverseTree( + public override traverseTree( file: FuncFile, node: SyntaxNode, result: FuncNode[], @@ -214,7 +93,7 @@ export class Referent { * Outside this node, no usages are assumed to exist. For example, a variable * can be used only in outer block statement where it is defined. */ - public useScope(): SearchScope | null { + public override useScope(): LocalSearchScope | GlobalSearchScope | null { if (!this.resolved) return null const node = this.resolved.node @@ -254,7 +133,7 @@ export class Referent { node.type === "function_declaration" || node.type === "constant_declaration" ) { - return GlobalSearchScope.allFiles() + return FuncGlobalSearchScope.allFiles() } if (node.type === "type_parameter") { @@ -264,7 +143,7 @@ export class Referent { return null } - private static localSearchScope(node: SyntaxNode | null): SearchScope | null { + private static localSearchScope(node: SyntaxNode | null): LocalSearchScope | null { if (!node) return null return new LocalSearchScope(node) } diff --git a/server/src/languages/func/rename/index.ts b/server/src/languages/func/rename/index.ts index 0a0d151c..7bafa04d 100644 --- a/server/src/languages/func/rename/index.ts +++ b/server/src/languages/func/rename/index.ts @@ -15,7 +15,6 @@ export function provideFuncRename(params: lsp.RenameParams, file: FuncFile): Wor const result = new Referent(renameNode, file).findReferences({ includeDefinition: true, sameFileOnly: false, - includeSelf: false, }) if (result.length === 0) return null diff --git a/server/src/languages/tlb/cache.ts b/server/src/languages/tlb/cache.ts index 901fdb4e..bdaafc05 100644 --- a/server/src/languages/tlb/cache.ts +++ b/server/src/languages/tlb/cache.ts @@ -1,40 +1,10 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio import {NamedNode} from "@server/languages/tlb/psi/TlbNode" +import {Cache} from "@server/cache/cache" -export class Cache { - private readonly data: Map - - public constructor() { - this.data = new Map() - } - - public cached(key: TKey, cb: () => TValue): TValue { - const cached = this.data.get(key) - if (cached !== undefined) { - return cached - } - - const value = cb() - this.data.set(key, value) - return value - } - - public clear(): void { - this.data.clear() - } - - public get size(): number { - return this.data.size - } -} - -export class CacheManager { - public readonly resolveCache: Cache - - public constructor() { - this.resolveCache = new Cache() - } +export class TlbCache { + public readonly resolveCache: Cache = new Cache() public clear(): void { console.info(`Clearing caches (resolve: ${this.resolveCache.size})`) @@ -42,4 +12,4 @@ export class CacheManager { } } -export const TLB_CACHE = new CacheManager() +export const TLB_CACHE = new TlbCache() diff --git a/server/src/languages/tlb/completion/CompletionProvider.ts b/server/src/languages/tlb/completion/CompletionProvider.ts deleted file mode 100644 index 6fde1b57..00000000 --- a/server/src/languages/tlb/completion/CompletionProvider.ts +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {CompletionContext} from "./CompletionContext" -import {CompletionResult} from "@server/languages/tolk/completion/WeightedCompletionItem" - -export interface CompletionProvider { - isAvailable(ctx: CompletionContext): boolean - addCompletion(ctx: CompletionContext, result: CompletionResult): void -} diff --git a/server/src/languages/tlb/completion/ReferenceCompletionProcessor.ts b/server/src/languages/tlb/completion/ReferenceCompletionProcessor.ts index 0f12f8d3..edd16718 100644 --- a/server/src/languages/tlb/completion/ReferenceCompletionProcessor.ts +++ b/server/src/languages/tlb/completion/ReferenceCompletionProcessor.ts @@ -2,11 +2,11 @@ // Copyright © 2025 TON Studio import {DeclarationNode, NamedFieldNode, NamedNode, ParameterNode, TlbNode} from "../psi/TlbNode" -import {ScopeProcessor} from "../psi/TlbReference" +import {ScopeProcessor} from "../psi/Reference" import {CompletionContext} from "@server/languages/tlb/completion/CompletionContext" import {CompletionItem, CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import {ResolveState} from "@server/psi/ResolveState" -import {WeightedCompletionItem} from "@server/languages/tlb/completion/WeightedCompletionItem" +import {WeightedCompletionItem} from "@server/completion/WeightedCompletionItem" export class ReferenceCompletionProcessor implements ScopeProcessor { public constructor(private readonly ctx: CompletionContext) {} diff --git a/server/src/languages/tlb/completion/WeightedCompletionItem.ts b/server/src/languages/tlb/completion/WeightedCompletionItem.ts deleted file mode 100644 index c5d563ec..00000000 --- a/server/src/languages/tlb/completion/WeightedCompletionItem.ts +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {CompletionItem} from "vscode-languageserver-types" - -export enum CompletionWeight { - CONTEXT_ELEMENT = 0, - KEYWORD = 80, - LOWEST = 500, -} - -// eslint-disable-next-line functional/type-declaration-immutability -export type WeightedCompletionItem = CompletionItem & { - readonly weight?: CompletionWeight -} - -export class CompletionResult { - public elements: WeightedCompletionItem[] = [] - - public add(...element: WeightedCompletionItem[]): void { - this.elements.push(...element) - } - - public sorted(): CompletionItem[] { - if (this.elements.length === 0) return [] - - const sorted = this.elements.sort((a, b) => { - if (a.weight === undefined || b.weight === undefined) return 0 - return a.weight - b.weight - }) - - let groupIndex = 0 - let lastWeight = sorted[0].weight ?? 0 - - sorted.forEach(item => { - const weight = item.weight as number - if (lastWeight !== weight) { - groupIndex++ - lastWeight = weight - } - - item.sortText = groupIndex.toString() - }) - - return sorted - } -} diff --git a/server/src/languages/tlb/completion/index.ts b/server/src/languages/tlb/completion/index.ts index 738f7f39..f8c48442 100644 --- a/server/src/languages/tlb/completion/index.ts +++ b/server/src/languages/tlb/completion/index.ts @@ -2,13 +2,13 @@ import * as lsp from "vscode-languageserver" import {getOffsetFromPosition} from "@server/document-store" import {asParserPoint} from "@server/utils/position" import {getDocumentSettings} from "@server/settings/settings" -import {CompletionResult} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult} from "@server/completion/WeightedCompletionItem" import {TlbFile} from "@server/languages/tlb/psi/TlbFile" import {createTlbParser} from "@server/parser" import {NamedNode} from "@server/languages/tlb/psi/TlbNode" -import {TlbReference} from "@server/languages/tlb/psi/TlbReference" +import {Reference} from "@server/languages/tlb/psi/Reference" import {CompletionContext} from "@server/languages/tlb/completion/CompletionContext" -import {CompletionProvider} from "@server/languages/tlb/completion/CompletionProvider" +import {CompletionProvider} from "@server/completion/CompletionProvider" import {ReferenceCompletionProvider} from "@server/languages/tlb/completion/providers/ReferenceCompletionProvider" import {BuiltinTypesCompletionProvider} from "@server/languages/tlb/completion/providers/BuiltinTypesCompletionProvider" @@ -43,7 +43,7 @@ export async function provideTlbCompletion( const newFile = new TlbFile(uri, tree, newContent) const element = new NamedNode(cursorNode, newFile) - const ref = new TlbReference(element, newFile) + const ref = new Reference(element, newFile) const ctx = new CompletionContext( element, @@ -53,7 +53,7 @@ export async function provideTlbCompletion( ) const result = new CompletionResult() - const providers: CompletionProvider[] = [ + const providers: CompletionProvider[] = [ new ReferenceCompletionProvider(ref), new BuiltinTypesCompletionProvider(), ] diff --git a/server/src/languages/tlb/completion/providers/BuiltinTypesCompletionProvider.ts b/server/src/languages/tlb/completion/providers/BuiltinTypesCompletionProvider.ts index deb7930c..dd1c3075 100644 --- a/server/src/languages/tlb/completion/providers/BuiltinTypesCompletionProvider.ts +++ b/server/src/languages/tlb/completion/providers/BuiltinTypesCompletionProvider.ts @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio -import {CompletionProvider} from "@server/languages/tlb/completion/CompletionProvider" +import {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionContext} from "@server/languages/tlb/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tlb/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import {CompletionItemKind} from "vscode-languageserver-types" export const BUILTIN_TYPES: Map = new Map([ @@ -44,7 +41,7 @@ export const BUILTIN_TYPES: Map = new Map([ ["Type", "Built-in TL-B type representing the type of types"], ]) -export class BuiltinTypesCompletionProvider implements CompletionProvider { +export class BuiltinTypesCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.isType } diff --git a/server/src/languages/tlb/completion/providers/ReferenceCompletionProvider.ts b/server/src/languages/tlb/completion/providers/ReferenceCompletionProvider.ts index 601f4bd3..ce915d5d 100644 --- a/server/src/languages/tlb/completion/providers/ReferenceCompletionProvider.ts +++ b/server/src/languages/tlb/completion/providers/ReferenceCompletionProvider.ts @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio import {ResolveState} from "@server/psi/ResolveState" -import {CompletionProvider} from "../CompletionProvider" +import {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionContext} from "@server/languages/tlb/completion/CompletionContext" -import {CompletionResult} from "@server/languages/tlb/completion/WeightedCompletionItem" +import {CompletionResult} from "@server/completion/WeightedCompletionItem" import {ReferenceCompletionProcessor} from "@server/languages/tlb/completion/ReferenceCompletionProcessor" -import {TlbReference} from "@server/languages/tlb/psi/TlbReference" +import {Reference} from "@server/languages/tlb/psi/Reference" -export class ReferenceCompletionProvider implements CompletionProvider { - public constructor(private readonly ref: TlbReference) {} +export class ReferenceCompletionProvider implements CompletionProvider { + public constructor(private readonly ref: Reference) {} public isAvailable(_ctx: CompletionContext): boolean { return true diff --git a/server/src/languages/tlb/documentation/index.ts b/server/src/languages/tlb/documentation/index.ts index 3c7f29b6..da7cd205 100644 --- a/server/src/languages/tlb/documentation/index.ts +++ b/server/src/languages/tlb/documentation/index.ts @@ -3,7 +3,7 @@ import type {Node as SyntaxNode} from "web-tree-sitter" import {TlbFile} from "@server/languages/tlb/psi/TlbFile" import {asLspRange} from "@server/utils/position" import {DeclarationNode, NamedNode} from "@server/languages/tlb/psi/TlbNode" -import {TlbReference} from "@server/languages/tlb/psi/TlbReference" +import {Reference} from "@server/languages/tlb/psi/Reference" import {BUILTIN_TYPES} from "@server/languages/tlb/completion/providers/BuiltinTypesCompletionProvider" import {generateDeclarationDoc} from "@server/languages/tlb/documentation/documentation" @@ -31,7 +31,7 @@ export function provideTlbDocumentation(hoverNode: SyntaxNode, file: TlbFile): l return null } - const results = TlbReference.multiResolve(new NamedNode(hoverNode, file)) + const results = Reference.multiResolve(new NamedNode(hoverNode, file)) if (results.length === 0) { const typeDoc = generateTypeDoc(text) if (typeDoc) { diff --git a/server/src/languages/tlb/find-definitions/index.ts b/server/src/languages/tlb/find-definitions/index.ts index 15fd2aee..2bfea210 100644 --- a/server/src/languages/tlb/find-definitions/index.ts +++ b/server/src/languages/tlb/find-definitions/index.ts @@ -1,6 +1,6 @@ import type {Node as SyntaxNode} from "web-tree-sitter" import * as lsp from "vscode-languageserver" -import {TlbReference} from "@server/languages/tlb/psi/TlbReference" +import {Reference} from "@server/languages/tlb/psi/Reference" import {asLspRange} from "@server/utils/position" import {TlbFile} from "@server/languages/tlb/psi/TlbFile" import {NamedNode} from "@server/languages/tlb/psi/TlbNode" @@ -11,7 +11,7 @@ export function provideTlbDefinition( ): lsp.Location[] | lsp.LocationLink[] { if (node.type !== "identifier" && node.type !== "type_identifier") return [] - const targets = TlbReference.multiResolve(new NamedNode(node, file)) + const targets = Reference.multiResolve(new NamedNode(node, file)) if (targets.length === 0) return [] return targets.map(target => { diff --git a/server/src/languages/tlb/psi/TlbReference.ts b/server/src/languages/tlb/psi/Reference.ts similarity index 93% rename from server/src/languages/tlb/psi/TlbReference.ts rename to server/src/languages/tlb/psi/Reference.ts index 5365a3c8..d35ae2cf 100644 --- a/server/src/languages/tlb/psi/TlbReference.ts +++ b/server/src/languages/tlb/psi/Reference.ts @@ -8,18 +8,18 @@ import {ResolveState} from "@server/psi/ResolveState" import {parentOfType} from "@server/psi/utils" import {TLB_CACHE} from "@server/languages/tlb/cache" -export class TlbReference { +export class Reference { private readonly element: TlbNode private readonly file: TlbFile public static resolve(node: TlbNode | null): NamedNode | null { if (node === null) return null - return new TlbReference(node, node.file).resolve() + return new Reference(node, node.file).resolve() } public static multiResolve(node: TlbNode | null): NamedNode[] { if (node === null) return [] - return new TlbReference(node, node.file).multiResolve() + return new Reference(node, node.file).multiResolve() } public constructor(element: TlbNode, file: TlbFile) { @@ -39,10 +39,7 @@ export class TlbReference { public resolveImpl(): NamedNode[] { const result: NamedNode[] = [] const state = new ResolveState() - this.processResolveVariants( - TlbReference.createResolveProcessor(result, this.element), - state, - ) + this.processResolveVariants(Reference.createResolveProcessor(result, this.element), state) if (result.length === 0) return [] return result } diff --git a/server/src/languages/tlb/psi/Referent.ts b/server/src/languages/tlb/psi/Referent.ts new file mode 100644 index 00000000..a7258c68 --- /dev/null +++ b/server/src/languages/tlb/psi/Referent.ts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2025 TON Studio +import type {Node as SyntaxNode} from "web-tree-sitter" +import {RecursiveVisitor} from "@server/visitor/visitor" +import {NamedNode, TlbNode} from "./TlbNode" +import {Reference} from "./Reference" +import type {TlbFile} from "./TlbFile" +import {BaseReferent, GlobalSearchScope, LocalSearchScope} from "@server/references/referent" +import {File} from "@server/psi/File" + +export class Referent extends BaseReferent { + public readonly resolved: NamedNode | null = null + + public constructor(node: SyntaxNode, file: TlbFile) { + super(file) + const element = new NamedNode(node, file) + this.resolved = Reference.resolve(element) + } + + public override traverseTree( + file: TlbFile, + node: SyntaxNode, + result: TlbNode[], + limit: number, + ): void { + const resolved = this.resolved + if (!resolved) return + + // The algorithm for finding TlbReferences is simple: + // we traverse the node that contains all the uses and resolve + // each identifier with the same name as searched symbol. + // If that identifier refers to the definition we are looking for, + // we add it to the list. + RecursiveVisitor.visit(node, (node): boolean | "stop" => { + // fast path, skip non-identifiers + if (node.type !== "identifier" && node.type !== "type_identifier") { + return true + } + + // fast path, identifier name doesn't equal to definition name + const nodeName = node.text + if (nodeName !== resolved.name()) { + return true + } + + const parent = node.parent + if (parent === null) return true + + if (parent.type === "combinator") { + return true + } + + const targets = Reference.multiResolve(new NamedNode(node, file)) + if (targets.length === 0) return true + + for (const res of targets) { + const identifier = res.nameIdentifier() + if (!identifier) continue + + if ( + res.node.type === resolved.node.type && + res.file.uri === resolved.file.uri && + res.node.startPosition.row === resolved.node.startPosition.row && + (identifier.text === resolved.name() || identifier.text === "self") + ) { + // found new TlbReference + result.push(new TlbNode(node, file)) + if (result.length === limit) { + return "stop" // end iteration} + } + } + } + return true + }) + } + + public override useScope(): LocalSearchScope | GlobalSearchScope | null { + if (!this.resolved) return null + return new GlobalSearchScope([this.file]) + } +} diff --git a/server/src/languages/tlb/psi/TlbReferent.ts b/server/src/languages/tlb/psi/TlbReferent.ts deleted file mode 100644 index 601f39eb..00000000 --- a/server/src/languages/tlb/psi/TlbReferent.ts +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import type {Node as SyntaxNode} from "web-tree-sitter" -import {RecursiveVisitor} from "@server/visitor/visitor" -import {NamedNode, TlbNode} from "./TlbNode" -import {TlbReference} from "./TlbReference" -import type {TlbFile} from "./TlbFile" - -/** - * Describes a scope that contains all possible uses of a certain symbol. - */ -export interface SearchScope { - toString(): string -} - -/** - * Describes the scope described by some AST node; the search for usages will be - * performed only within this node. - * - * For example, the scope for a local variable will be the block in which it is defined. - */ -export class LocalSearchScope implements SearchScope { - public constructor(public node: SyntaxNode) {} - - public toString(): string { - return `LocalSearchScope:\n${this.node.text}` - } -} - -/** - * Describes a scope consisting of one or more files. - * - * For example, the scope of a global function from the standard library is all project files. - */ -export class GlobalSearchScope implements SearchScope { - public constructor(public files: TlbFile[]) {} - - public toString(): string { - return `GlobalSearchScope:\n${this.files.map(f => `- ${f.uri}`).join("\n")}` - } -} - -export interface FindTlbReferenceOptions { - /** - * if true, the first element of the result contains the definition - */ - readonly includeDefinition?: boolean - /** - * if true, don't include `self` as usages (for rename) - */ - readonly includeSelf?: boolean - /** - * if true, only TlbReferences from the same files listed - */ - readonly sameFileOnly?: boolean - /** - * search stops after `limit` number of TlbReferences are found - */ - readonly limit?: number -} - -/** - * Referent encapsulates the logic for finding all TlbReferences to a definition. - */ -export class TlbReferent { - private readonly resolved: NamedNode | null = null - private readonly file: TlbFile - - public constructor(node: SyntaxNode, file: TlbFile) { - this.file = file - const element = new NamedNode(node, file) - this.resolved = TlbReference.resolve(element) - } - - /** - * Returns a list of nodes that reference the definition. - */ - public findReferences({ - includeDefinition = false, - sameFileOnly = false, - limit = Infinity, - }: FindTlbReferenceOptions): TlbNode[] { - const resolved = this.resolved - if (!resolved) return [] - - const useScope = this.useScope() - if (!useScope) return [] - - const result: TlbNode[] = [] - if (includeDefinition && (!sameFileOnly || resolved.file.uri === this.file.uri)) { - const nameNode = resolved.nameNode() - if (nameNode) { - result.push(nameNode) - } - } - - this.searchInScope(useScope, sameFileOnly, result, limit) - return result - } - - private searchInScope( - scope: SearchScope, - sameFileOnly: boolean, - result: TlbNode[], - limit: number, - ): void { - if (!this.resolved) return - - if (scope instanceof LocalSearchScope) { - this.traverseTree(this.resolved.file, scope.node, result, limit) - } - - if (scope instanceof GlobalSearchScope) { - if (sameFileOnly) { - this.traverseTree(this.file, this.file.rootNode, result, limit) - return - } - - for (const file of scope.files) { - this.traverseTree(file, file.rootNode, result, limit) - if (result.length === limit) { - break - } - } - } - } - - private traverseTree(file: TlbFile, node: SyntaxNode, result: TlbNode[], limit: number): void { - const resolved = this.resolved - if (!resolved) return - - // The algorithm for finding TlbReferences is simple: - // we traverse the node that contains all the uses and resolve - // each identifier with the same name as searched symbol. - // If that identifier refers to the definition we are looking for, - // we add it to the list. - RecursiveVisitor.visit(node, (node): boolean | "stop" => { - // fast path, skip non-identifiers - if (node.type !== "identifier" && node.type !== "type_identifier") { - return true - } - - // fast path, identifier name doesn't equal to definition name - const nodeName = node.text - if (nodeName !== resolved.name()) { - return true - } - - const parent = node.parent - if (parent === null) return true - - if (parent.type === "combinator") { - return true - } - - const targets = TlbReference.multiResolve(new NamedNode(node, file)) - if (targets.length === 0) return true - - for (const res of targets) { - const identifier = res.nameIdentifier() - if (!identifier) continue - - if ( - res.node.type === resolved.node.type && - res.file.uri === resolved.file.uri && - res.node.startPosition.row === resolved.node.startPosition.row && - (identifier.text === resolved.name() || identifier.text === "self") - ) { - // found new TlbReference - result.push(new TlbNode(node, file)) - if (result.length === limit) { - return "stop" // end iteration} - } - } - } - return true - }) - } - - /** - * Returns the effective node in which all possible usages are expected. - * Outside this node, no usages are assumed to exist. For example, a variable - * can be used only in an outer block statement where it is defined. - */ - public useScope(): SearchScope | null { - if (!this.resolved) return null - - return new GlobalSearchScope([this.file]) - } -} diff --git a/server/src/languages/tlb/references/index.ts b/server/src/languages/tlb/references/index.ts index 8c1d551f..2f02bb1d 100644 --- a/server/src/languages/tlb/references/index.ts +++ b/server/src/languages/tlb/references/index.ts @@ -1,7 +1,7 @@ import type {Node as SyntaxNode} from "web-tree-sitter" import * as lsp from "vscode-languageserver" import {asLspRange} from "@server/utils/position" -import {TlbReferent} from "@server/languages/tlb/psi/TlbReferent" +import {Referent} from "@server/languages/tlb/psi/Referent" import {TlbFile} from "@server/languages/tlb/psi/TlbFile" export function provideTlbReferences( @@ -12,7 +12,7 @@ export function provideTlbReferences( return [] } - const result = new TlbReferent(referenceNode, file).findReferences({ + const result = new Referent(referenceNode, file).findReferences({ includeDefinition: false, }) if (result.length === 0) return null diff --git a/server/src/languages/tlb/semantic-tokens/index.ts b/server/src/languages/tlb/semantic-tokens/index.ts index 30908ff4..05d54b26 100644 --- a/server/src/languages/tlb/semantic-tokens/index.ts +++ b/server/src/languages/tlb/semantic-tokens/index.ts @@ -1,26 +1,15 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio -import {SemanticTokens, SemanticTokensBuilder} from "vscode-languageserver" +import {SemanticTokens} from "vscode-languageserver" import {SemanticTokenTypes} from "vscode-languageserver-protocol" import {RecursiveVisitor} from "@server/visitor/visitor" -import type {Node as SyntaxNode} from "web-tree-sitter" -import * as lsp from "vscode-languageserver" -import {TlbReference} from "@server/languages/tlb/psi/TlbReference" +import {Reference} from "@server/languages/tlb/psi/Reference" import {TlbFile} from "@server/languages/tlb/psi/TlbFile" import {NamedNode} from "@server/languages/tlb/psi/TlbNode" +import {Tokens} from "@server/semantic/tokens" export function provideTlbSemanticTokens(file: TlbFile): SemanticTokens { - const builder = new SemanticTokensBuilder() - - function pushToken(n: SyntaxNode, tokenType: lsp.SemanticTokenTypes): void { - builder.push( - n.startPosition.row, - n.startPosition.column, - n.endPosition.column - n.startPosition.column, - Object.keys(lsp.SemanticTokenTypes).indexOf(tokenType), - 0, - ) - } + const tokens = new Tokens() RecursiveVisitor.visit(file.rootNode, (node): boolean => { switch (node.type) { @@ -32,16 +21,16 @@ export function provideTlbSemanticTokens(file: TlbFile): SemanticTokens { const parent = node.parent if (parent?.type === "constructor_tag") break - pushToken(node, SemanticTokenTypes.macro) + tokens.node(node, SemanticTokenTypes.macro) break } case "identifier": { - const resolved = TlbReference.resolve(new NamedNode(node, file)) + const resolved = Reference.resolve(new NamedNode(node, file)) if (resolved) { const insideTypeParameter = resolved.parentOfType("type_parameter") !== undefined if (insideTypeParameter) { - pushToken(node, SemanticTokenTypes.typeParameter) + tokens.node(node, SemanticTokenTypes.typeParameter) break } } @@ -49,7 +38,7 @@ export function provideTlbSemanticTokens(file: TlbFile): SemanticTokens { } case "type_identifier": { if (isBuiltinType(node.text)) { - pushToken(node, SemanticTokenTypes.macro) + tokens.node(node, SemanticTokenTypes.macro) break } @@ -57,43 +46,43 @@ export function provideTlbSemanticTokens(file: TlbFile): SemanticTokens { if (!parent) break if (parent.type === "combinator" || parent.type === "combinator_expr") { - pushToken(node, SemanticTokenTypes.class) + tokens.node(node, SemanticTokenTypes.class) break } - const resolved = TlbReference.resolve(new NamedNode(node, file)) + const resolved = Reference.resolve(new NamedNode(node, file)) if (resolved) { if (resolved.node.parent?.type === "field_named") { - pushToken(node, SemanticTokenTypes.variable) + tokens.node(node, SemanticTokenTypes.variable) break } const insideTypeParameter = resolved.parentOfType("type_parameter") !== undefined if (insideTypeParameter) { - pushToken(node, SemanticTokenTypes.typeParameter) + tokens.node(node, SemanticTokenTypes.typeParameter) break } } - pushToken(node, SemanticTokenTypes.type) + tokens.node(node, SemanticTokenTypes.type) break } case "type_parameter": { - const name = TlbReference.findTypeParameterNode(node) + const name = Reference.findTypeParameterNode(node) if (!name) break - pushToken(name, SemanticTokenTypes.typeParameter) + tokens.node(name, SemanticTokenTypes.typeParameter) break } case "field_named": { const identifier = node.firstNamedChild if (!identifier) break - pushToken(identifier, SemanticTokenTypes.property) + tokens.node(identifier, SemanticTokenTypes.property) break } case "constructor_": { const identifier = node.firstNamedChild if (identifier && identifier.type === "identifier") { - pushToken(identifier, SemanticTokenTypes.type) + tokens.node(identifier, SemanticTokenTypes.type) } break } @@ -107,7 +96,10 @@ export function provideTlbSemanticTokens(file: TlbFile): SemanticTokens { return true }) - return builder.build() + return { + resultId: Date.now().toString(), + data: tokens.result(), + } } function isBuiltinType(name: string): boolean { diff --git a/server/src/languages/tolk/cache.ts b/server/src/languages/tolk/cache.ts index 2460f11b..64b1d48f 100644 --- a/server/src/languages/tolk/cache.ts +++ b/server/src/languages/tolk/cache.ts @@ -4,46 +4,13 @@ import type {NamedNode} from "@server/languages/tolk/psi/TolkNode" import type {Ty} from "@server/languages/tolk/types/ty" import {TolkFile} from "@server/languages/tolk/psi/TolkFile" import {InferenceResult} from "@server/languages/tolk/type-inference" +import {Cache} from "@server/cache/cache" -export class Cache { - private readonly data: Map - - public constructor() { - this.data = new Map() - } - - public cached(key: TKey, cb: () => TValue): TValue { - const cached = this.data.get(key) - if (cached !== undefined) { - return cached - } - - const value = cb() - this.data.set(key, value) - return value - } - - public clear(): void { - this.data.clear() - } - - public get size(): number { - return this.data.size - } -} - -export class CacheManager { - public readonly typeCache: Cache - public readonly resolveCache: Cache - public readonly funcTypeCache: Cache - public readonly importedFiles: Cache - - public constructor() { - this.typeCache = new Cache() - this.resolveCache = new Cache() - this.funcTypeCache = new Cache() - this.importedFiles = new Cache() - } +export class TolkCache { + public readonly typeCache: Cache = new Cache() + public readonly resolveCache: Cache = new Cache() + public readonly funcTypeCache: Cache = new Cache() + public readonly importedFiles: Cache = new Cache() public clear(): void { console.info( @@ -56,4 +23,4 @@ export class CacheManager { } } -export const TOLK_CACHE = new CacheManager() +export const TOLK_CACHE = new TolkCache() diff --git a/server/src/languages/tolk/completion/CompletionProvider.ts b/server/src/languages/tolk/completion/CompletionProvider.ts deleted file mode 100644 index 85a7e4ce..00000000 --- a/server/src/languages/tolk/completion/CompletionProvider.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {CompletionContext} from "./CompletionContext" -import {CompletionResult} from "@server/languages/tolk/completion/WeightedCompletionItem" - -export interface CompletionProvider { - isAvailable(ctx: CompletionContext): boolean - addCompletion(ctx: CompletionContext, result: CompletionResult): void -} - -export interface AsyncCompletionProvider { - isAvailable(ctx: CompletionContext): boolean - addCompletion(ctx: CompletionContext, result: CompletionResult): Promise -} diff --git a/server/src/languages/tolk/completion/ReferenceCompletionProcessor.ts b/server/src/languages/tolk/completion/ReferenceCompletionProcessor.ts index 9bef1229..fd47e627 100644 --- a/server/src/languages/tolk/completion/ReferenceCompletionProcessor.ts +++ b/server/src/languages/tolk/completion/ReferenceCompletionProcessor.ts @@ -17,20 +17,10 @@ import { TypeParameter, } from "@server/languages/tolk/psi/Decls" import {CompletionContext} from "./CompletionContext" -import { - CompletionWeight, - WeightedCompletionItem, -} from "@server/languages/tolk/completion/WeightedCompletionItem" -import {TolkFile} from "@server/languages/tolk/psi/TolkFile" +import {CompletionWeight, WeightedCompletionItem} from "@server/completion/WeightedCompletionItem" import {ResolveState} from "@server/psi/ResolveState" import {TypeInferer} from "@server/languages/tolk/TypeInferer" - -export interface CompletionItemAdditionalInformation { - readonly name: string | undefined - readonly file: TolkFile | undefined - readonly elementFile: TolkFile | undefined - readonly language: "tolk" | "func" | undefined -} +import {CompletionItemAdditionalInformation} from "@server/completion/CompletionItemAdditionalInformation" export class ReferenceCompletionProcessor implements ScopeProcessor { public constructor(private readonly ctx: CompletionContext) {} diff --git a/server/src/languages/tolk/completion/index.ts b/server/src/languages/tolk/completion/index.ts index e77f6bd0..42d17558 100644 --- a/server/src/languages/tolk/completion/index.ts +++ b/server/src/languages/tolk/completion/index.ts @@ -9,16 +9,15 @@ import {asParserPoint} from "@server/utils/position" import {NamedNode} from "@server/languages/tolk/psi/TolkNode" import {Reference} from "@server/languages/tolk/psi/Reference" import {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import {CompletionResult} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult} from "@server/completion/WeightedCompletionItem" import type { AsyncCompletionProvider, CompletionProvider, -} from "@server/languages/tolk/completion/CompletionProvider" +} from "@server/completion/CompletionProvider" import {TopLevelCompletionProvider} from "@server/languages/tolk/completion/providers/TopLevelCompletionProvider" import {SnippetsCompletionProvider} from "@server/languages/tolk/completion/providers/SnippetsCompletionProvider" import {KeywordsCompletionProvider} from "@server/languages/tolk/completion/providers/KeywordsCompletionProvider" import {ReferenceCompletionProvider} from "@server/languages/tolk/completion/providers/ReferenceCompletionProvider" -import {CompletionItemAdditionalInformation} from "@server/languages/tolk/completion/ReferenceCompletionProcessor" import {findTolkFile} from "@server/files" import {index} from "@server/languages/tolk/indexes" import {FileDiff} from "@server/utils/FileDiff" @@ -31,6 +30,7 @@ import {IndexAccessCompletionProvider} from "@server/languages/tolk/completion/p import {VariableSizeTypeCompletionProvider} from "@server/languages/tolk/completion/providers/VariableSizeTypeCompletionProvider" import {ExpressionSnippetsCompletionProvider} from "@server/languages/tolk/completion/providers/ExpressionSnippetsCompletionProvider" import {MatchArmsCompletionProvider} from "@server/languages/tolk/completion/providers/MatchArmsCompletionProvider" +import {CompletionItemAdditionalInformation} from "@server/completion/CompletionItemAdditionalInformation" export async function provideTolkCompletion( file: TolkFile, @@ -97,7 +97,7 @@ export async function provideTolkCompletion( ) const result = new CompletionResult() - const providers: CompletionProvider[] = [ + const providers: CompletionProvider[] = [ new TopLevelCompletionProvider(), new SnippetsCompletionProvider(), new ExpressionSnippetsCompletionProvider(), @@ -117,7 +117,9 @@ export async function provideTolkCompletion( provider.addCompletion(ctx, result) } - const asyncProviders: AsyncCompletionProvider[] = [new ImportPathCompletionProvider()] + const asyncProviders: AsyncCompletionProvider[] = [ + new ImportPathCompletionProvider(), + ] for (const provider of asyncProviders) { if (!provider.isAvailable(ctx)) continue diff --git a/server/src/languages/tolk/completion/providers/AnnotationsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/AnnotationsCompletionProvider.ts index 034b9036..b92712ad 100644 --- a/server/src/languages/tolk/completion/providers/AnnotationsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/AnnotationsCompletionProvider.ts @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import {parentOfType} from "@server/psi/utils" -export class AnnotationsCompletionProvider implements CompletionProvider { +export class AnnotationsCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.isAnnotationName } diff --git a/server/src/languages/tolk/completion/providers/EntryPointsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/EntryPointsCompletionProvider.ts index 63b187cd..e98bbde7 100644 --- a/server/src/languages/tolk/completion/providers/EntryPointsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/EntryPointsCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class EntryPointsCompletionProvider implements CompletionProvider { +export class EntryPointsCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.topLevel } diff --git a/server/src/languages/tolk/completion/providers/ExpressionSnippetsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/ExpressionSnippetsCompletionProvider.ts index 9eff3d57..c80c8015 100644 --- a/server/src/languages/tolk/completion/providers/ExpressionSnippetsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/ExpressionSnippetsCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class ExpressionSnippetsCompletionProvider implements CompletionProvider { +export class ExpressionSnippetsCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.expression() } diff --git a/server/src/languages/tolk/completion/providers/ImportPathCompletionProvider.ts b/server/src/languages/tolk/completion/providers/ImportPathCompletionProvider.ts index 445bccbc..ad2b2e84 100644 --- a/server/src/languages/tolk/completion/providers/ImportPathCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/ImportPathCompletionProvider.ts @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import {AsyncCompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import {AsyncCompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import * as path from "node:path" import {globalVFS} from "@server/vfs/global" import {listDirs, listFiles} from "@server/vfs/vfs" @@ -15,7 +12,7 @@ import {TolkFile} from "@server/languages/tolk/psi/TolkFile" import {trimSuffix} from "@server/utils/strings" import {projectTolkStdlibPath} from "@server/languages/tolk/toolchain/toolchain" -export class ImportPathCompletionProvider implements AsyncCompletionProvider { +export class ImportPathCompletionProvider implements AsyncCompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.insideImport } diff --git a/server/src/languages/tolk/completion/providers/IndexAccessCompletionProvider.ts b/server/src/languages/tolk/completion/providers/IndexAccessCompletionProvider.ts index 4be64a8a..055dd70d 100644 --- a/server/src/languages/tolk/completion/providers/IndexAccessCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/IndexAccessCompletionProvider.ts @@ -1,16 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import {typeOf} from "@server/languages/tolk/type-inference" import {TensorTy, TupleTy} from "@server/languages/tolk/types/ty" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" -export class IndexAccessCompletionProvider implements CompletionProvider { +export class IndexAccessCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.afterDot } diff --git a/server/src/languages/tolk/completion/providers/KeywordsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/KeywordsCompletionProvider.ts index dbbccc15..ab781e7b 100644 --- a/server/src/languages/tolk/completion/providers/KeywordsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/KeywordsCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class KeywordsCompletionProvider implements CompletionProvider { +export class KeywordsCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.expression() && !ctx.inNameOfFieldInit } diff --git a/server/src/languages/tolk/completion/providers/MatchArmsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/MatchArmsCompletionProvider.ts index ddd9bf0f..01da3d70 100644 --- a/server/src/languages/tolk/completion/providers/MatchArmsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/MatchArmsCompletionProvider.ts @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import {parentOfType} from "@server/psi/utils" import {inferenceOf} from "@server/languages/tolk/type-inference" import {UnionTy} from "@server/languages/tolk/types/ty" @@ -14,7 +11,7 @@ import {ResolveState} from "@server/psi/ResolveState" import {ReferenceCompletionProcessor} from "@server/languages/tolk/completion/ReferenceCompletionProcessor" import {Reference} from "@server/languages/tolk/psi/Reference" -export class MatchArmsCompletionProvider implements CompletionProvider { +export class MatchArmsCompletionProvider implements CompletionProvider { public constructor(private readonly ref: Reference) {} public isAvailable(ctx: CompletionContext): boolean { diff --git a/server/src/languages/tolk/completion/providers/ReferenceCompletionProvider.ts b/server/src/languages/tolk/completion/providers/ReferenceCompletionProvider.ts index 3f086ca8..8fcff3f2 100644 --- a/server/src/languages/tolk/completion/providers/ReferenceCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/ReferenceCompletionProvider.ts @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" import {Reference, ScopeProcessor} from "@server/languages/tolk/psi/Reference" import {ReferenceCompletionProcessor} from "@server/languages/tolk/completion/ReferenceCompletionProcessor" import {NamedNode, TolkNode} from "@server/languages/tolk/psi/TolkNode" -import type {CompletionResult} from "@server/languages/tolk/completion/WeightedCompletionItem" +import type {CompletionResult} from "@server/completion/WeightedCompletionItem" import {ResolveState} from "@server/psi/ResolveState" import {FieldsOwnerTy} from "@server/languages/tolk/types/ty" import {typeOf} from "@server/languages/tolk/type-inference" @@ -15,7 +15,7 @@ enum CompletionKind { ALL = "ALL", } -export class ReferenceCompletionProvider implements CompletionProvider { +export class ReferenceCompletionProvider implements CompletionProvider { public constructor(private readonly ref: Reference) {} public isAvailable(ctx: CompletionContext): boolean { diff --git a/server/src/languages/tolk/completion/providers/ReturnCompletionProvider.ts b/server/src/languages/tolk/completion/providers/ReturnCompletionProvider.ts index a16aac44..5e8091f7 100644 --- a/server/src/languages/tolk/completion/providers/ReturnCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/ReturnCompletionProvider.ts @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" import {Func} from "@server/languages/tolk/psi/Decls" import {UnionTy, NullTy, BoolTy, IntTy, VoidTy} from "@server/languages/tolk/types/ty" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" import {functionTypeOf} from "@server/languages/tolk/type-inference" -export class ReturnCompletionProvider implements CompletionProvider { +export class ReturnCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.isStatement } diff --git a/server/src/languages/tolk/completion/providers/SnippetsCompletionProvider.ts b/server/src/languages/tolk/completion/providers/SnippetsCompletionProvider.ts index 3e338061..0aa6a4ea 100644 --- a/server/src/languages/tolk/completion/providers/SnippetsCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/SnippetsCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class SnippetsCompletionProvider implements CompletionProvider { +export class SnippetsCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.isStatement && !ctx.topLevel && !ctx.afterDot } diff --git a/server/src/languages/tolk/completion/providers/ThrowAssertCompletionProvider.ts b/server/src/languages/tolk/completion/providers/ThrowAssertCompletionProvider.ts index 90a120db..c2e604a6 100644 --- a/server/src/languages/tolk/completion/providers/ThrowAssertCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/ThrowAssertCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class ThrowAssertCompletionProvider implements CompletionProvider { +export class ThrowAssertCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.isStatement } diff --git a/server/src/languages/tolk/completion/providers/TopLevelCompletionProvider.ts b/server/src/languages/tolk/completion/providers/TopLevelCompletionProvider.ts index 095de992..268be5cd 100644 --- a/server/src/languages/tolk/completion/providers/TopLevelCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/TopLevelCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Studio import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class TopLevelCompletionProvider implements CompletionProvider { +export class TopLevelCompletionProvider implements CompletionProvider { public isAvailable(ctx: CompletionContext): boolean { return ctx.topLevel } diff --git a/server/src/languages/tolk/completion/providers/VariableSizeTypeCompletionProvider.ts b/server/src/languages/tolk/completion/providers/VariableSizeTypeCompletionProvider.ts index c7e6fa0f..73af3c9a 100644 --- a/server/src/languages/tolk/completion/providers/VariableSizeTypeCompletionProvider.ts +++ b/server/src/languages/tolk/completion/providers/VariableSizeTypeCompletionProvider.ts @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import type {CompletionProvider} from "@server/languages/tolk/completion/CompletionProvider" +import type {CompletionProvider} from "@server/completion/CompletionProvider" import {CompletionItemKind, InsertTextFormat} from "vscode-languageserver-types" import type {CompletionContext} from "@server/languages/tolk/completion/CompletionContext" -import { - CompletionResult, - CompletionWeight, -} from "@server/languages/tolk/completion/WeightedCompletionItem" +import {CompletionResult, CompletionWeight} from "@server/completion/WeightedCompletionItem" -export class VariableSizeTypeCompletionProvider implements CompletionProvider { +export class VariableSizeTypeCompletionProvider implements CompletionProvider { private readonly types: string[] = [ "uint8", "uint16", diff --git a/server/src/languages/tolk/documentation/documentation.ts b/server/src/languages/tolk/documentation/documentation.ts index ed2b261c..adfa42e9 100644 --- a/server/src/languages/tolk/documentation/documentation.ts +++ b/server/src/languages/tolk/documentation/documentation.ts @@ -20,9 +20,6 @@ import {TypeInferer} from "@server/languages/tolk/TypeInferer" import {functionTypeOf, typeOf} from "@server/languages/tolk/type-inference" import {UnionTy} from "@server/languages/tolk/types/ty" -const CODE_FENCE = "```" -const DOC_TMPL = `${CODE_FENCE}tolk\n{signature}\n${CODE_FENCE}\n{documentation}\n` - /** * Returns the documentation for the given symbol in Markdown format, or null * if there is no documentation for the element. @@ -196,7 +193,7 @@ export function generateTolkDocFor(node: NamedNode, place: SyntaxNode): string | return defaultResult(`${kind} ${name}: ${type} ${valuePresentation}`) } - // TODO: better support for tensor/ tuple variables + // TODO: better support for tensor/tuple variables return defaultResult(owner.text) } case "identifier": { @@ -236,5 +233,7 @@ export function generateTolkDocFor(node: NamedNode, place: SyntaxNode): string | } function defaultResult(signature: string, documentation: string = ""): string { + const CODE_FENCE = "```" + const DOC_TMPL = `${CODE_FENCE}tolk\n{signature}\n${CODE_FENCE}\n{documentation}\n` return DOC_TMPL.replace("{signature}", signature).replace("{documentation}", documentation) } diff --git a/server/src/languages/tolk/documentation/index.ts b/server/src/languages/tolk/documentation/index.ts index 8e30f403..910022c6 100644 --- a/server/src/languages/tolk/documentation/index.ts +++ b/server/src/languages/tolk/documentation/index.ts @@ -51,19 +51,7 @@ export function provideTolkDocumentation(hoverNode: SyntaxNode, file: TolkFile): } const res = Reference.resolve(NamedNode.create(hoverNode, file)) - if (res === null) { - if (process.env["TON_LS_DEV"] !== "true") { - return null - } - - return { - range: asLspRange(hoverNode), - contents: { - kind: "plaintext", - value: hoverNode.type, - }, - } - } + if (res === null) return null const doc = docs.generateTolkDocFor(res, hoverNode) if (doc === null) return null diff --git a/server/src/languages/tolk/foldings/index.ts b/server/src/languages/tolk/foldings/index.ts index e9f28f0f..839448a5 100644 --- a/server/src/languages/tolk/foldings/index.ts +++ b/server/src/languages/tolk/foldings/index.ts @@ -1,24 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright © 2025 TON Core -import {FoldingRange, FoldingRangeKind} from "vscode-languageserver-types" +import {FoldingRange} from "vscode-languageserver-types" import {RecursiveVisitor} from "@server/visitor/visitor" import type {TolkFile} from "@server/languages/tolk/psi/TolkFile" -import type {Point} from "web-tree-sitter" -import type * as lsp from "vscode-languageserver" +import {genericFolding} from "@server/foldings" export function provideTolkFoldingRanges(file: TolkFile): FoldingRange[] { const result: FoldingRange[] = [] - const genericFolding = (start: Point, end: Point): lsp.FoldingRange => { - return { - kind: FoldingRangeKind.Region, - startLine: start.row, - endLine: end.row - 1, - startCharacter: end.column, - endCharacter: end.column, - } - } - RecursiveVisitor.visit(file.rootNode, (n): boolean => { if ( n.type === "block_statement" || diff --git a/server/src/languages/tolk/highlighting/index.ts b/server/src/languages/tolk/highlighting/index.ts index 17c4712f..812d354e 100644 --- a/server/src/languages/tolk/highlighting/index.ts +++ b/server/src/languages/tolk/highlighting/index.ts @@ -18,7 +18,6 @@ export function provideTolkDocumentHighlight( const result = new Referent(highlightNode, file).findReferences({ includeDefinition: true, sameFileOnly: true, - includeSelf: true, }) if (result.length === 0) return null diff --git a/server/src/languages/tolk/indexing-root.ts b/server/src/languages/tolk/indexing-root.ts new file mode 100644 index 00000000..4f663b7d --- /dev/null +++ b/server/src/languages/tolk/indexing-root.ts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2025 TON Studio +import {index} from "@server/languages/tolk/indexes" +import {findTolkFile} from "@server/files" +import {IndexingRoot, IndexingRootKind} from "@server/indexing/indexing" + +export class TolkIndexingRoot extends IndexingRoot { + public constructor(root: string, kind: IndexingRootKind) { + super(root, ["tolk"], kind) + } + + protected override async onFile(uri: string): Promise { + const file = await findTolkFile(uri) + index.addFile(uri, file, false) + } +} diff --git a/server/src/languages/tolk/intentions/index.ts b/server/src/languages/tolk/intentions/index.ts index 72b9aeb4..b0792430 100644 --- a/server/src/languages/tolk/intentions/index.ts +++ b/server/src/languages/tolk/intentions/index.ts @@ -1,6 +1,6 @@ import * as lsp from "vscode-languageserver" import {findTolkFile, TOLK_PARSED_FILES_CACHE} from "@server/files" -import {GlobalSearchScope, LocalSearchScope, Referent} from "@server/languages/tolk/psi/Referent" +import {Referent} from "@server/languages/tolk/psi/Referent" import {File} from "@server/psi/File" import type {Node as SyntaxNode} from "web-tree-sitter" import {asParserPoint} from "@server/utils/position" @@ -21,6 +21,7 @@ import {NamedNode} from "@server/languages/tolk/psi/TolkNode" import {Reference} from "@server/languages/tolk/psi/Reference" import {parentOfType} from "@server/psi/utils" import {inferenceOf} from "@server/languages/tolk/type-inference" +import {LocalSearchScope} from "@server/references/referent" export const TOLK_INTENTIONS: Intention[] = [ new AddImport(), @@ -50,14 +51,12 @@ export async function provideExecuteTolkCommand( if (!scope) return "Scope not found" if (scope instanceof LocalSearchScope) return scope.toString() - if (scope instanceof GlobalSearchScope) { - if (scope.files.length > 10) { - return "GlobalSearchScope{...}" - } - return `GlobalSearchScope{${scope.files.map(it => it.name + ".tolk").join(", ")}}` + if (scope.files.length > 10) { + return "GlobalSearchScope{...}" } - return "Unknown" + + return `GlobalSearchScope{${scope.files.map(it => it.name + ".tolk").join(", ")}}` } if (params.command === "tolk.getUnresolvedIdentifiers") { diff --git a/server/src/languages/tolk/psi/Referent.ts b/server/src/languages/tolk/psi/Referent.ts index 7073722b..5c44ae51 100644 --- a/server/src/languages/tolk/psi/Referent.ts +++ b/server/src/languages/tolk/psi/Referent.ts @@ -7,40 +7,16 @@ import {Reference} from "./Reference" import type {TolkFile} from "./TolkFile" import {parentOfType} from "@server/psi/utils" import {TOLK_PARSED_FILES_CACHE} from "@server/files" +import {BaseReferent, GlobalSearchScope, LocalSearchScope} from "@server/references/referent" +import {File} from "@server/psi/File" -/** - * Describes a scope that contains all possible uses of a certain symbol. - */ -export interface SearchScope { - toString(): string -} - -/** - * Describes the scope described by some AST node, the search for usages will be - * performed only within this node. - * - * For example, the scope for a local variable will be the block in which it is defined. - */ -export class LocalSearchScope implements SearchScope { - public constructor(public node: SyntaxNode) {} - - public toString(): string { - return `LocalSearchScope:\n${this.node.text}` - } -} - -/** - * Describes a scope consisting of one or more files. - * - * For example, the scope of a global function from the standard library is all project files. - */ -export class GlobalSearchScope implements SearchScope { - public static allFiles(): GlobalSearchScope { +class TolkGlobalSearchScope extends GlobalSearchScope { + public static allFiles(): GlobalSearchScope { const files = [...TOLK_PARSED_FILES_CACHE.values()] return new GlobalSearchScope(files) } - public static importedFiles(file: TolkFile): GlobalSearchScope { + public static importedFiles(file: TolkFile): GlobalSearchScope { if (file.fromStdlib || file.fromStubs) { // common.tolk implicitly included everywhere return this.allFiles() @@ -48,120 +24,20 @@ export class GlobalSearchScope implements SearchScope { return new GlobalSearchScope([file, ...file.importedBy()]) } - - public constructor(public files: TolkFile[]) {} - - public toString(): string { - return `GlobalSearchScope:\n${this.files.map(f => `- ${f.uri}`).join("\n")}` - } } -export interface FindReferenceOptions { - /** - * if true, the first element of the result contains the definition - */ - readonly includeDefinition?: boolean - /** - * if true, don't include `self` as usages (for rename) - */ - readonly includeSelf?: boolean - /** - * if true, only references from the same files listed - */ - readonly sameFileOnly?: boolean - /** - * search stops after `limit` number of references are found - */ - readonly limit?: number -} - -/** - * Referent encapsulates the logic for finding all references to a definition. - * - * The search logic is simple, each symbol has a certain scope in which it can be used. - * If it is a local variable, then the block in which it is defined, if a parameter, then - * the function in which it is defined. If it is a global function, then all project files. - * - * When the scope is defined, it is enough to go through all the nodes from it and find those - * that refer to the searched element. - * For optimization, we do not try to resolve each identifier, we resolve only those that have - * the same name as the searched element (and a bit of logic for processing `self`). - * - * Searching for uses of global symbols can be improved, now we use all files from the index, - * but following the Tolk imports logic, we can reduce the search scope. For example, when searching - * for uses of a global function defined within the project, there is no point in searching - * for its uses within the standard library. - * These optimizations and improvements are the object of further work. - */ -export class Referent { - private readonly resolved: NamedNode | null = null - private readonly file: TolkFile +export class Referent extends BaseReferent { + public readonly resolved: NamedNode | null = null public constructor(node: SyntaxNode, file: TolkFile) { - this.file = file + super(file) const element = new NamedNode(node, file) this.resolved = Reference.resolve(element) } - /** - * Returns a list of nodes that reference the definition. - */ - public findReferences({ - includeDefinition = false, - includeSelf = true, - sameFileOnly = false, - limit = Infinity, - }: FindReferenceOptions): TolkNode[] { - const resolved = this.resolved - if (!resolved) return [] - - const useScope = this.useScope() - if (!useScope) return [] - - const result: TolkNode[] = [] - if (includeDefinition && (!sameFileOnly || resolved.file.uri === this.file.uri)) { - const nameNode = resolved.nameNode() - if (nameNode) { - result.push(nameNode) - } - } - - this.searchInScope(useScope, sameFileOnly, includeSelf, result, limit) - return result - } - - private searchInScope( - scope: SearchScope, - sameFileOnly: boolean, - includeSelf: boolean, - result: TolkNode[], - limit: number, - ): void { - if (!this.resolved) return - - if (scope instanceof LocalSearchScope) { - this.traverseTree(this.resolved.file, scope.node, includeSelf, result, limit) - } - - if (scope instanceof GlobalSearchScope) { - if (sameFileOnly) { - this.traverseTree(this.file, this.file.rootNode, includeSelf, result, limit) - return - } - - for (const file of scope.files) { - this.traverseTree(file, file.rootNode, includeSelf, result, limit) - if (result.length === limit) { - break - } - } - } - } - - private traverseTree( + public override traverseTree( file: TolkFile, node: SyntaxNode, - includeSelf: boolean, result: TolkNode[], limit: number, ): void { @@ -181,10 +57,9 @@ export class Referent { // fast path, identifier name doesn't equal to definition name // self can refer to enclosing method const nodeName = node.text - if (nodeName !== resolved.name(false) && nodeName !== "self") { + if (nodeName !== resolved.name(false)) { return true } - if (nodeName === "self" && !includeSelf) return true const parent = node.parent if (parent === null) return true @@ -258,7 +133,7 @@ export class Referent { * Outside this node, no usages are assumed to exist. For example, a variable * can be used only in outer block statement where it is defined. */ - public useScope(): SearchScope | null { + public override useScope(): LocalSearchScope | GlobalSearchScope | null { if (!this.resolved) return null const node = this.resolved.node @@ -306,11 +181,11 @@ export class Referent { node.type === "struct_declaration" || node.type === "type_alias_declaration" ) { - return GlobalSearchScope.importedFiles(this.resolved.file) + return TolkGlobalSearchScope.importedFiles(this.resolved.file) } if (node.type === "struct_field_declaration") { - return GlobalSearchScope.importedFiles(this.resolved.file) + return TolkGlobalSearchScope.importedFiles(this.resolved.file) } if (node.type === "type_identifier" && parent.type === "instantiationT_list") { @@ -342,7 +217,7 @@ export class Referent { return null } - private static localSearchScope(node: SyntaxNode | null): SearchScope | null { + private static localSearchScope(node: SyntaxNode | null): LocalSearchScope | null { if (!node) return null return new LocalSearchScope(node) } diff --git a/server/src/languages/tolk/rename/index.ts b/server/src/languages/tolk/rename/index.ts index 24e56ddd..6d83ab62 100644 --- a/server/src/languages/tolk/rename/index.ts +++ b/server/src/languages/tolk/rename/index.ts @@ -16,7 +16,6 @@ export function provideTolkRename(params: lsp.RenameParams, file: TolkFile): Wor const result = new Referent(renameNode, file).findReferences({ includeDefinition: true, sameFileOnly: false, - includeSelf: false, }) if (result.length === 0) return null diff --git a/server/src/references/referent.ts b/server/src/references/referent.ts new file mode 100644 index 00000000..c52e27bb --- /dev/null +++ b/server/src/references/referent.ts @@ -0,0 +1,145 @@ +import type {Node as SyntaxNode} from "web-tree-sitter" +import {File} from "@server/psi/File" + +/** + * Describes a scope that contains all possible uses of a certain symbol. + */ +export interface SearchScope { + toString(): string +} + +/** + * Describes the scope described by some AST node; the search for usages will be + * performed only within this node. + * + * For example, the scope for a local variable will be the block in which it is defined. + */ +export class LocalSearchScope implements SearchScope { + public constructor(public node: SyntaxNode) {} + + public toString(): string { + return `LocalSearchScope:\n${this.node.text}` + } +} + +/** + * Describes a scope consisting of one or more files. + * + * For example, the scope of a global function from the standard library is all project files. + */ +export class GlobalSearchScope implements SearchScope { + public constructor(public files: TFile[]) {} + + public toString(): string { + return `GlobalSearchScope:\n${this.files.map(f => `- ${f.uri}`).join("\n")}` + } +} + +export interface FindReferencesOptions { + /** + * if true, the first element of the result contains the definition + */ + readonly includeDefinition?: boolean + /** + * if true, only references from the same files listed + */ + readonly sameFileOnly?: boolean + /** + * search stops after `limit` number of references are found + */ + readonly limit?: number +} + +export abstract class NamedNode { + public abstract file: File + + public abstract nameIdentifier(): SyntaxNode | null + + public abstract nameNode(): NamedNode | null + + public abstract name(): string +} + +/** + * Referent encapsulates the logic for finding all references to a definition. + * + * The search logic is simple, each symbol has a certain scope in which it can be used. + * If it is a local variable, then the block in which it is defined, if a parameter, then + * the function in which it is defined. If it is a global function, then all project files. + * + * When the scope is defined, it is enough to go through all the nodes from it and find those + * that refer to the searched element. + * For optimization, we do not try to resolve each identifier, we resolve only those that have + * the same name as the searched element (and a bit of logic for processing `self`). + */ +export abstract class BaseReferent { + public abstract readonly resolved: Node | null + public readonly file: File + + protected constructor(file: File) { + this.file = file + } + + /** + * Returns a list of nodes that reference the definition. + */ + public findReferences({ + includeDefinition = false, + sameFileOnly = false, + limit = Infinity, + }: FindReferencesOptions): Node[] { + const resolved = this.resolved + if (!resolved) return [] + + const useScope = this.useScope() + if (!useScope) return [] + + const result: Node[] = [] + if (includeDefinition && (!sameFileOnly || resolved.file.uri === this.file.uri)) { + const nameNode = resolved.nameNode() + if (nameNode) { + // @ts-expect-error idk + result.push(nameNode) + } + } + + this.searchInScope(useScope, sameFileOnly, result, limit) + return result + } + + private searchInScope( + scope: LocalSearchScope | GlobalSearchScope, + sameFileOnly: boolean, + result: Node[], + limit: number, + ): void { + if (!this.resolved) return + + if (scope instanceof LocalSearchScope) { + this.traverseTree(this.resolved.file, scope.node, result, limit) + } + + if (scope instanceof GlobalSearchScope) { + if (sameFileOnly) { + this.traverseTree(this.file, this.file.rootNode, result, limit) + return + } + + for (const file of scope.files) { + this.traverseTree(file, file.rootNode, result, limit) + if (result.length === limit) { + break + } + } + } + } + + public abstract traverseTree(file: File, node: SyntaxNode, result: Node[], limit: number): void + + /** + * Returns the effective node in which all possible usages are expected. + * Outside this node, no usages are assumed to exist. For example, a variable + * can be used only in an outer block statement where it is defined. + */ + public abstract useScope(): LocalSearchScope | GlobalSearchScope | null +} diff --git a/server/src/server.ts b/server/src/server.ts index ad852b3d..68caab76 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -61,7 +61,7 @@ import {provideTlbCompletion} from "@server/languages/tlb/completion" import {TLB_CACHE} from "@server/languages/tlb/cache" import {provideTlbReferences} from "@server/languages/tlb/references" import {TextDocument} from "vscode-languageserver-textdocument" -import {TolkIndexingRoot, TolkIndexingRootKind} from "@server/tolk-indexing-root" +import {TolkIndexingRoot} from "@server/languages/tolk/indexing-root" import {TOLK_CACHE} from "@server/languages/tolk/cache" import {provideTolkSemanticTokens} from "@server/languages/tolk/semantic-tokens" import { @@ -99,7 +99,6 @@ import { onTolkFileRenamed, processTolkFileRenaming, } from "@server/languages/tolk/rename/file-renaming" -import {FuncIndexingRoot, FuncIndexingRootKind} from "@server/func-indexing-root" import {provideFuncDefinition} from "@server/languages/func/find-definitions" import {provideFuncSemanticTokens} from "@server/languages/func/semantic-tokens" import {provideFuncCompletion} from "@server/languages/func/completion" @@ -117,6 +116,8 @@ import { onFuncFileRenamed, processFuncFileRenaming, } from "@server/languages/func/rename/file-renaming" +import {IndexingRootKind} from "@server/indexing/indexing" +import {FuncIndexingRoot} from "@server/languages/func/indexing-root" /** * Whenever LS is initialized. @@ -318,7 +319,7 @@ async function initialize(): Promise { const stdlibUri = filePathToUri(stdlibPath) tolkIndex.withStdlibRoot(new TolkIndexRoot("stdlib", stdlibUri)) - const stdlibRoot = new TolkIndexingRoot(stdlibUri, TolkIndexingRootKind.Stdlib) + const stdlibRoot = new TolkIndexingRoot(stdlibUri, IndexingRootKind.Stdlib) await stdlibRoot.index() } @@ -333,20 +334,20 @@ async function initialize(): Promise { console.info(`Using Tolk Stubs from ${stubsPath}`) - const stubsRoot = new TolkIndexingRoot(stubsUri, TolkIndexingRootKind.Stdlib) + const stubsRoot = new TolkIndexingRoot(stubsUri, IndexingRootKind.Stdlib) await stubsRoot.index() - const funcStubsRoot = new FuncIndexingRoot(stubsUri, FuncIndexingRootKind.Stdlib) + const funcStubsRoot = new FuncIndexingRoot(stubsUri, IndexingRootKind.Stdlib) await funcStubsRoot.index() } reporter.report(90, "Indexing: (3/3) Workspace") tolkIndex.withRoots([new TolkIndexRoot("workspace", rootUri)]) - const tolkWorkspaceRoot = new TolkIndexingRoot(rootUri, TolkIndexingRootKind.Workspace) + const tolkWorkspaceRoot = new TolkIndexingRoot(rootUri, IndexingRootKind.Workspace) await tolkWorkspaceRoot.index() funcIndex.withRoots([new FuncIndexRoot("workspace", rootUri)]) - const funcWorkspaceRoot = new FuncIndexingRoot(rootUri, FuncIndexingRootKind.Workspace) + const funcWorkspaceRoot = new FuncIndexingRoot(rootUri, IndexingRootKind.Workspace) await funcWorkspaceRoot.index() reporter.report(100, "Ready") diff --git a/server/src/tolk-indexing-root.ts b/server/src/tolk-indexing-root.ts deleted file mode 100644 index c07a97dc..00000000 --- a/server/src/tolk-indexing-root.ts +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright © 2025 TON Studio -import {glob} from "glob" -import {index} from "@server/languages/tolk/indexes" -import {fileURLToPath} from "node:url" -import * as path from "node:path" -import {filePathToUri, findTolkFile} from "@server/files" - -export enum TolkIndexingRootKind { - Stdlib = "stdlib", - Workspace = "workspace", -} - -export class TolkIndexingRoot { - public constructor( - public root: string, - public kind: TolkIndexingRootKind, - ) {} - - public async index(): Promise { - const ignore = - this.kind === TolkIndexingRootKind.Stdlib - ? [] - : [ - ".git/**", - "allure-results/**", - "**/node_modules/**", - "**/dist/**", - "**/__testdata/**", - ] - - const rootDir = fileURLToPath(this.root) - const files = await glob("**/*.tolk", { - cwd: rootDir, - ignore: ignore, - }) - if (files.length === 0) { - console.warn(`No file to index in ${this.root}`) - } - for (const filePath of files) { - console.info("Indexing:", filePath) - const absPath = path.join(rootDir, filePath) - const uri = filePathToUri(absPath) - const file = await findTolkFile(uri) - index.addFile(uri, file, false) - } - } -}