Skip to content

Commit bca03a0

Browse files
authored
feat(fift/navigation): add lens to go to Tolk sources for this definition (#124)
Fixes #123
1 parent 4f9394a commit bca03a0

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

editors/code/src/extension.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,45 @@ async function startServer(context: vscode.ExtensionContext): Promise<vscode.Dis
196196
})
197197
}
198198

199+
async function resolveFile(filePath: string): Promise<vscode.Uri> {
200+
if (path.isAbsolute(filePath)) {
201+
return vscode.Uri.file(filePath)
202+
}
203+
204+
const workspaceFolders = vscode.workspace.workspaceFolders
205+
if (!workspaceFolders || workspaceFolders.length === 0) {
206+
throw new Error("No workspace folder found")
207+
}
208+
209+
const workspaceRoot = workspaceFolders[0].uri.fsPath
210+
const fullPath = path.join(workspaceRoot, "contracts", filePath)
211+
212+
try {
213+
const uri = vscode.Uri.file(fullPath)
214+
await vscode.workspace.fs.stat(uri)
215+
return uri
216+
} catch {
217+
const foundFiles = await vscode.workspace.findFiles(
218+
`**/${path.basename(filePath)}`,
219+
"**/node_modules/**", // no need to search in node_modules
220+
10,
221+
)
222+
223+
if (foundFiles.length === 0) {
224+
throw new Error(`File not found: ${filePath}`)
225+
}
226+
227+
if (foundFiles.length === 1) {
228+
return foundFiles[0]
229+
} else {
230+
const exactMatch = foundFiles.find(
231+
file => file.fsPath.endsWith(filePath) || file.fsPath.includes(filePath),
232+
)
233+
return exactMatch ?? foundFiles[0]
234+
}
235+
}
236+
}
237+
199238
function registerCommands(disposables: vscode.Disposable[]): void {
200239
disposables.push(
201240
vscode.commands.registerCommand("tolk.showToolchainInfo", async () => {
@@ -613,6 +652,23 @@ Node.js: ${info.environment.nodeVersion ?? "Unknown"}`
613652
void vscode.env.clipboard.writeText(str)
614653
void vscode.window.showInformationMessage(`Copied ${str} to clipboard`)
615654
}),
655+
vscode.commands.registerCommand("ton.openFile", async (filePath: string, line?: number) => {
656+
try {
657+
const uri = await resolveFile(filePath)
658+
659+
if (line !== undefined && line > 0) {
660+
const uriWithFragment = uri.with({
661+
fragment: `L${line}`,
662+
})
663+
await vscode.commands.executeCommand("vscode.open", uriWithFragment)
664+
} else {
665+
await vscode.commands.executeCommand("vscode.open", uri)
666+
}
667+
} catch (error) {
668+
vscode.window.showErrorMessage(`Failed to open file: ${filePath}`)
669+
console.error("Error opening file:", error)
670+
}
671+
}),
616672
)
617673
}
618674

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright © 2025 TON Core
3+
import * as lsp from "vscode-languageserver"
4+
import {RecursiveVisitor} from "@server/visitor/visitor"
5+
import {asLspRange} from "@server/utils/position"
6+
import {FiftFile} from "@server/languages/fift/psi/FiftFile"
7+
import {trimPrefix} from "@server/utils/strings"
8+
9+
export function collectFiftCodeLenses(file: FiftFile): lsp.CodeLens[] {
10+
const result: lsp.CodeLens[] = []
11+
12+
RecursiveVisitor.visit(file.rootNode, (n): boolean => {
13+
if (n.type === "definition") {
14+
const prevComment = n.previousSibling
15+
if (!prevComment || prevComment.type !== "comment") {
16+
return true
17+
}
18+
19+
const text = trimPrefix(prevComment.text, "// ")
20+
const parts = text.split(/ /)
21+
const pathAndLine = parts[0]
22+
if (!pathAndLine) return true
23+
24+
const [path, line] = pathAndLine.split(":")
25+
26+
result.push({
27+
range: asLspRange(n),
28+
command: {
29+
title: `Go to Tolk sources (${pathAndLine})`,
30+
command: "ton.openFile",
31+
arguments: [path, Number(line)],
32+
},
33+
})
34+
}
35+
36+
return true
37+
})
38+
39+
return result
40+
}

server/src/server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ import {IndexingRootKind} from "@server/indexing/indexing"
120120
import {FuncIndexingRoot} from "@server/languages/func/indexing-root"
121121
import {formatTolkFile} from "@server/languages/tolk/format/format"
122122
import {collectFuncCodeLenses} from "@server/languages/func/lens"
123+
import {collectFiftCodeLenses} from "@server/languages/fift/lens"
123124

124125
/**
125126
* Whenever LS is initialized.
@@ -980,6 +981,11 @@ connection.onInitialize(async (initParams: lsp.InitializeParams): Promise<lsp.In
980981
async (params: lsp.CodeLensParams): Promise<lsp.CodeLens[] | null> => {
981982
const uri = params.textDocument.uri
982983

984+
if (isFiftFile(uri)) {
985+
const file = await findFiftFile(uri)
986+
return collectFiftCodeLenses(file)
987+
}
988+
983989
if (isFuncFile(uri)) {
984990
const file = await findFuncFile(uri)
985991
return collectFuncCodeLenses(file)

0 commit comments

Comments
 (0)