Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ dist/
test-workspace/
.vscode-test/
*.wasm
!tree-sitter-tolk/tree-sitter-tolk.wasm
!tree-sitter-fift/tree-sitter-fift.wasm
!tree-sitter-tlb/tree-sitter-tlb.wasm
!**/tree-sitter-tolk/tree-sitter-tolk.wasm
!**/tree-sitter-func/tree-sitter-func.wasm
!**/tree-sitter-fift/tree-sitter-fift.wasm
!**/tree-sitter-tlb/tree-sitter-tlb.wasm

# tests
server/src/e2e/out
Expand Down
1 change: 1 addition & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
!dist/**/*.wasm
!dist/**/*.svg
!dist/**/*.tolk
!dist/**/*.fc

# Include only this files
!LICENSE
Expand Down
10 changes: 9 additions & 1 deletion README-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ Tolk support includes:

FunC support includes:

- Basic syntax highlighting
- Semantic syntax highlighting
- Code completion, imports completion
- Go to definition
- Find all references, workspace symbol search, symbol renaming
- Automatic import updates when renaming and moving files
- Types and documentation on hover
- Inlay hints for method id
- On-the-fly inspections
- Build and test projects based on Blueprint

Fift assembly support includes:

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ Tolk support includes:

FunC support includes:

- Basic syntax highlighting
- Semantic syntax highlighting
- Code completion, imports completion
- Go to definition
- Find all references, workspace symbol search, symbol renaming
- Automatic import updates when renaming and moving files
- Types and documentation on hover
- Inlay hints for method id
- On-the-fly inspections
- Build and test projects based on Blueprint

Fift assembly support includes:

Expand Down
7 changes: 6 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,14 @@ async function startServer(context: vscode.ExtensionContext): Promise<vscode.Dis
revealOutputChannelOn: RevealOutputChannelOn.Never,
documentSelector: [
{scheme: "file", language: "tolk"},
{scheme: "file", language: "func"},
{scheme: "file", language: "fift"},
{scheme: "file", language: "tlb"},
{scheme: "untitled", language: "tolk"},
],
synchronize: {
configurationSection: "ton",
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.{tolk,tlb}"),
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.{tolk,fc,func,tlb}"),
},
initializationOptions: {
clientConfig: getClientConfiguration(),
Expand All @@ -96,6 +97,10 @@ async function startServer(context: vscode.ExtensionContext): Promise<vscode.Dis
context.extensionUri,
"./dist/tree-sitter-tolk.wasm",
).fsPath,
funcLangWasmUri: vscode_uri.joinPath(
context.extensionUri,
"./dist/tree-sitter-func.wasm",
).fsPath,
fiftLangWasmUri: vscode_uri.joinPath(
context.extensionUri,
"./dist/tree-sitter-fift.wasm",
Expand Down
11 changes: 10 additions & 1 deletion client/src/languages/func-language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@
"increaseIndentPattern": "^(.*\\{[^}\"']*|.*\\([^\\)\"']*|.*\\[[^\\]\"']*)$",
"decreaseIndentPattern": "^\\s*[\\}\\]\\)]"
},
"wordPattern": "`[^`]+`|[A-Za-z_$][^\\s+\\-*\\/%,.;(){}\\[\\]=<>|\\^~]*"
"wordPattern": "`[^`]+`|[A-Za-z_$:][^\\s+\\-*\\/%,.;(){}\\[\\]=<>|\\^~]*",
"onEnterRules": [
{
"beforeText": "^\\s*;;;.*$",
"action": {
"indent": "none",
"appendText": ";;; "
}
}
]
}
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default tseslint.config(
"server/src/languages/tolk/tree-sitter-tolk/",
"server/src/languages/fift/tree-sitter-fift/",
"server/src/languages/tolk/tree-sitter-tolk/",
"server/src/languages/func/tree-sitter-func/",
"server/src/languages/tlb/tree-sitter-tlb/",
],
},
Expand Down
27 changes: 26 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"fmt:check": "prettier --check --cache .",
"grammar:wasm": "yarn grammar:tolk:wasm && yarn grammar:fift:wasm && yarn grammar:tlb:wasm",
"grammar:tolk:wasm": "cd server/src/languages/tolk/tree-sitter-tolk && tree-sitter generate && tree-sitter build --wasm",
"grammar:func:wasm": "cd server/src/languages/func/tree-sitter-func && tree-sitter generate && tree-sitter build --wasm",
"grammar:fift:wasm": "cd server/src/languages/fift/tree-sitter-fift && tree-sitter generate && tree-sitter build --wasm",
"grammar:tlb:wasm": "cd server/src/languages/tlb/tree-sitter-tlb && tree-sitter generate && tree-sitter build --wasm",
"watch": "webpack --watch",
Expand Down Expand Up @@ -363,7 +364,17 @@
"ton.tolk.hints.showMethodId": {
"type": "boolean",
"default": true,
"description": "Show method ID hints for contract functions"
"description": "Show method ID hints for get methods"
},
"ton.func.hints.disable": {
"type": "boolean",
"default": false,
"description": "Disable all inlay hints for FunC"
},
"ton.func.hints.showMethodId": {
"type": "boolean",
"default": true,
"description": "Show method ID hints for functions with method_id"
}
}
},
Expand Down Expand Up @@ -404,6 +415,20 @@
"type-compatibility"
],
"description": "List of disabled code inspections. All inspections are enabled by default."
},
"ton.func.inspections.disabled": {
"type": "array",
"items": {
"type": "string",
"enum": [
"unused-parameter",
"unused-type-parameter",
"unused-variable",
"unused-import"
]
},
"default": [],
"description": "List of disabled code inspections for FunC. All inspections are enabled by default."
}
}
},
Expand Down
37 changes: 36 additions & 1 deletion server/src/files.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import * as lsp from "vscode-languageserver"
import {TextDocument} from "vscode-languageserver-textdocument"
import {pathToFileURL} from "node:url"
import {createFiftParser, createTlbParser, createTolkParser} from "@server/parser"
import {createFiftParser, createFuncParser, createTlbParser, createTolkParser} from "@server/parser"
import {readFileVFS, globalVFS} from "@server/vfs/files-adapter"
import {FiftFile} from "@server/languages/fift/psi/FiftFile"
import {TlbFile} from "@server/languages/tlb/psi/TlbFile"
import {URI} from "vscode-uri"
import {TolkFile} from "@server/languages/tolk/psi/TolkFile"
import {measureTime} from "@server/psi/utils"
import {FuncFile} from "@server/languages/func/psi/FuncFile"

export const TOLK_PARSED_FILES_CACHE: Map<string, TolkFile> = new Map()
export const FUNC_PARSED_FILES_CACHE: Map<string, FuncFile> = new Map()
export const FIFT_PARSED_FILES_CACHE: Map<string, FiftFile> = new Map()
export const TLB_PARSED_FILES_CACHE: Map<string, TlbFile> = new Map()

Expand Down Expand Up @@ -41,6 +43,34 @@ export function reparseTolkFile(uri: string, content: string): TolkFile {
return file
}

export async function findFuncFile(uri: string, changed: boolean = false): Promise<FuncFile> {
const cached = FUNC_PARSED_FILES_CACHE.get(uri)
if (cached !== undefined && !changed) {
return cached
}

const rawContent = await readOrUndefined(uri)
if (rawContent === undefined) {
console.error(`cannot read ${uri} file`)
}

const content = rawContent ?? ""
return measureTime(`reparse ${uri}`, () => reparseFuncFile(uri, content))
}

export function reparseFuncFile(uri: string, content: string): FuncFile {
const parser = createFuncParser()
const tree = parser.parse(content)
if (!tree) {
throw new Error(`FATAL ERROR: cannot parse ${uri} file`)
}

// TODO: why we have %40 here?
const file = new FuncFile(uri.replace("%40", "@"), tree, content)
FUNC_PARSED_FILES_CACHE.set(uri, file)
return file
}

export async function findFiftFile(uri: string): Promise<FiftFile> {
const cached = FIFT_PARSED_FILES_CACHE.get(uri)
if (cached !== undefined) {
Expand Down Expand Up @@ -108,6 +138,11 @@ export const isTolkFile = (
event?: lsp.TextDocumentChangeEvent<TextDocument>,
): boolean => event?.document.languageId === "tolk" || uri.endsWith(".tolk")

export const isFuncFile = (
uri: string,
event?: lsp.TextDocumentChangeEvent<TextDocument>,
): boolean => event?.document.languageId === "func" || uri.endsWith(".fc") || uri.endsWith(".func")

export const isFiftFile = (
uri: string,
event?: lsp.TextDocumentChangeEvent<TextDocument>,
Expand Down
48 changes: 48 additions & 0 deletions server/src/func-indexing-root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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"

export enum FuncIndexingRootKind {
Stdlib = "stdlib",
Workspace = "workspace",
}

export class FuncIndexingRoot {
public constructor(
public root: string,
public kind: FuncIndexingRootKind,
) {}

public async index(): Promise<void> {
const ignore =
this.kind === FuncIndexingRootKind.Stdlib
? []
: [
".git/**",
"allure-results/**",
"**/node_modules/**",
"**/dist/**",
"**/__testdata/**",
]

const rootDir = fileURLToPath(this.root)
const files = await glob("**/*.{fc,func}", {
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 findFuncFile(uri)
index.addFile(uri, file, false)
}
}
}
45 changes: 45 additions & 0 deletions server/src/languages/func/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
// Copyright © 2025 TON Core
import type {NamedNode} from "@server/languages/func/psi/FuncNode"

export class Cache<TKey, TValue> {
private readonly data: Map<TKey, TValue>

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<number, NamedNode | null>

public constructor() {
this.resolveCache = new Cache()
}

public clear(): void {
console.info(`Clearing caches (resolve: ${this.resolveCache.size})`)
this.resolveCache.clear()
}
}

export const FUNC_CACHE = new CacheManager()
Loading