Skip to content

Commit a6faeae

Browse files
committed
feat(tolk): support mappings from Tolk 1.3
Fixes #264
1 parent 45cdab4 commit a6faeae

12 files changed

Lines changed: 203 additions & 84 deletions

server/src/acton/ActonToml.ts

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
// Copyright © 2026 TON Core
33

44
import * as path from "node:path"
5+
import * as fs from "node:fs"
56

67
import {URI} from "vscode-uri"
78

8-
import {readFileVFS, globalVFS, existsVFS} from "@server/vfs/files-adapter"
9-
109
export interface WalletInfo {
1110
readonly name: string
1211
readonly isLocal: boolean
@@ -15,12 +14,17 @@ export interface WalletInfo {
1514
export class ActonToml {
1615
public constructor(private readonly uri: string) {}
1716

18-
private async readContent(uri: string): Promise<string | undefined> {
19-
return readFileVFS(globalVFS, uri)
17+
private readContent(uri: string): string | undefined {
18+
try {
19+
const fsPath = URI.parse(uri).fsPath
20+
return fs.readFileSync(fsPath, "utf8")
21+
} catch {
22+
return undefined
23+
}
2024
}
2125

22-
public async getContractIds(): Promise<string[]> {
23-
const content = await this.readContent(this.uri)
26+
public getContractIds(): string[] {
27+
const content = this.readContent(this.uri)
2428
if (!content) return []
2529

2630
const contractIds: string[] = []
@@ -36,38 +40,59 @@ export class ActonToml {
3640
return contractIds
3741
}
3842

39-
public async getWallets(): Promise<WalletInfo[]> {
43+
public getMappings(): Map<string, string> {
44+
const content = this.readContent(this.uri)
45+
if (!content) return new Map()
46+
47+
const mappings: Map<string, string> = new Map()
48+
// Simple manual parsing for [mappings] table
49+
const lines = content.split("\n")
50+
let inMappings = false
51+
for (const line of lines) {
52+
const trimmed = line.trim()
53+
if (trimmed === "[mappings]") {
54+
inMappings = true
55+
continue
56+
}
57+
if (trimmed.startsWith("[") && trimmed !== "[mappings]") {
58+
inMappings = false
59+
continue
60+
}
61+
if (inMappings && trimmed.includes("=")) {
62+
const [rawKey, rawValue] = trimmed.split("=").map(s => s.trim())
63+
if (rawKey && rawValue) {
64+
// remove quotes from key and then @ prefix
65+
const cleanKey = rawKey.replace(/^["']|["']$/g, "")
66+
const key = cleanKey.startsWith("@") ? cleanKey.slice(1) : cleanKey
67+
// remove quotes from value
68+
const cleanValue = rawValue.replace(/^["']|["']$/g, "")
69+
mappings.set(key, cleanValue)
70+
}
71+
}
72+
}
73+
return mappings
74+
}
75+
76+
public get workingDir(): string {
77+
return path.dirname(URI.parse(this.uri).fsPath)
78+
}
79+
80+
public getWallets(): WalletInfo[] {
4081
const wallets: WalletInfo[] = []
4182

4283
const baseUri = URI.parse(this.uri)
4384
const dirPath = path.dirname(baseUri.fsPath)
4485

4586
const walletsTomlUri = URI.file(path.join(dirPath, "wallets.toml")).toString()
46-
const walletsContent = await this.readContent(walletsTomlUri)
87+
const walletsContent = this.readContent(walletsTomlUri)
4788
if (walletsContent) {
48-
// Match [wallets.NAME] where NAME does not contain a dot
49-
const walletRegex = /^\[wallets\.([^\s.\]]+)]/gm
50-
let match: RegExpExecArray | null
51-
while ((match = walletRegex.exec(walletsContent)) !== null) {
52-
const name = match[1]
53-
if (name) {
54-
wallets.push({name, isLocal: true})
55-
}
56-
}
89+
wallets.push(...this.parseWallets(walletsContent, true))
5790
}
5891

5992
const globalWalletsTomlUri = URI.file(path.join(dirPath, "global.wallets.toml")).toString()
60-
const globalWalletsContent = await this.readContent(globalWalletsTomlUri)
93+
const globalWalletsContent = this.readContent(globalWalletsTomlUri)
6194
if (globalWalletsContent) {
62-
// Match [wallets.NAME] where NAME does not contain a dot
63-
const walletRegex = /^\[wallets\.([^\s.\]]+)]/gm
64-
let match: RegExpExecArray | null
65-
while ((match = walletRegex.exec(globalWalletsContent)) !== null) {
66-
const name = match[1]
67-
if (name) {
68-
wallets.push({name, isLocal: false})
69-
}
70-
}
95+
wallets.push(...this.parseWallets(globalWalletsContent, false))
7196
}
7297

7398
const seen: Set<string> = new Set()
@@ -78,15 +103,29 @@ export class ActonToml {
78103
})
79104
}
80105

81-
public static async find(startUri: string): Promise<ActonToml | undefined> {
106+
private parseWallets(content: string, isLocal: boolean): WalletInfo[] {
107+
const wallets: WalletInfo[] = []
108+
// Match [wallets.NAME] where NAME does not contain a dot
109+
const walletRegex = /^\[wallets\.([^\s.\]]+)]/gm
110+
let match: RegExpExecArray | null
111+
while ((match = walletRegex.exec(content)) !== null) {
112+
const name = match[1]
113+
if (name) {
114+
wallets.push({name, isLocal})
115+
}
116+
}
117+
return wallets
118+
}
119+
120+
public static discover(startUri: string): ActonToml | undefined {
82121
let currentPath = URI.parse(startUri).fsPath
83122

84123
for (let i = 0; i < 10; i++) {
85124
const dir = path.dirname(currentPath)
86125
const tomlPath = path.join(dir, "Acton.toml")
87-
const tomlUri = URI.file(tomlPath).toString()
88126

89-
if (await existsVFS(globalVFS, tomlUri)) {
127+
if (fs.existsSync(tomlPath)) {
128+
const tomlUri = URI.file(tomlPath).toString()
90129
return new ActonToml(tomlUri)
91130
}
92131

server/src/languages/tolk/completion/index.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,18 @@ export async function provideTolkCompletion(
126126
new StructFieldModifiersCompletionProvider(),
127127
new EnumCompletionProvider(),
128128
new ContractFieldCompletionProvider(),
129+
new ActonWalletNameCompletionProvider(),
130+
new ActonContractIdCompletionProvider(),
131+
new ImportPathCompletionProvider(),
132+
new ActonGetMethodCompletionProvider(),
129133
]
130134

131135
for (const provider of providers) {
132136
if (!provider.isAvailable(ctx)) continue
133137
provider.addCompletion(ctx, result)
134138
}
135139

136-
const asyncProviders: AsyncCompletionProvider<CompletionContext>[] = [
137-
new ImportPathCompletionProvider(),
138-
new ActonWalletNameCompletionProvider(),
139-
new ActonContractIdCompletionProvider(),
140-
new ActonGetMethodCompletionProvider(),
141-
]
140+
const asyncProviders: AsyncCompletionProvider<CompletionContext>[] = []
142141

143142
for (const provider of asyncProviders) {
144143
if (!provider.isAvailable(ctx)) continue

server/src/languages/tolk/completion/providers/ActonContractIdCompletionProvider.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ export class ActonContractIdCompletionProvider extends ActonStringArgumentComple
1818
return functionName === "build" && qualifierName === undefined && argumentIndex === 0
1919
}
2020

21-
protected async addStringCompletions(
22-
ctx: CompletionContext,
23-
result: CompletionResult,
24-
): Promise<void> {
25-
const actonToml = await ActonToml.find(ctx.element.file.uri)
21+
protected addStringCompletions(ctx: CompletionContext, result: CompletionResult): void {
22+
const actonToml = ActonToml.discover(ctx.element.file.uri)
2623
if (!actonToml) return
2724

28-
const contractIds = await actonToml.getContractIds()
25+
const contractIds = actonToml.getContractIds()
2926
for (const id of contractIds) {
3027
result.add({
3128
label: id,

server/src/languages/tolk/completion/providers/ActonGetMethodCompletionProvider.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@ export class ActonGetMethodCompletionProvider extends ActonStringArgumentComplet
2222
return functionName === "runGetMethod" && qualifierName === "net" && argumentIndex === 1
2323
}
2424

25-
protected async addStringCompletions(
26-
_ctx: CompletionContext,
27-
result: CompletionResult,
28-
): Promise<void> {
29-
await Promise.resolve()
25+
protected addStringCompletions(_ctx: CompletionContext, result: CompletionResult): void {
3026
const getMethods: GetMethod[] = []
3127
index.processElementsByKey(
3228
IndexKey.GetMethods,

server/src/languages/tolk/completion/providers/ActonStringArgumentCompletionProvider.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
// SPDX-License-Identifier: MIT
22
// Copyright © 2026 TON Core
33

4-
import {AsyncCompletionProvider} from "@server/completion/CompletionProvider"
4+
import {CompletionProvider} from "@server/completion/CompletionProvider"
55
import {CompletionContext} from "@server/languages/tolk/completion/CompletionContext"
66
import {CompletionResult} from "@server/completion/WeightedCompletionItem"
77

88
export abstract class ActonStringArgumentCompletionProvider
9-
implements AsyncCompletionProvider<CompletionContext>
9+
implements CompletionProvider<CompletionContext>
1010
{
1111
public isAvailable(ctx: CompletionContext): boolean {
1212
return ctx.insideString
1313
}
1414

15-
public async addCompletion(ctx: CompletionContext, result: CompletionResult): Promise<void> {
15+
public addCompletion(ctx: CompletionContext, result: CompletionResult): void {
1616
const node = ctx.element.node
1717
let parent = node.parent
1818
if (parent && parent.type === "call_argument") {
@@ -50,7 +50,7 @@ export abstract class ActonStringArgumentCompletionProvider
5050
const argIndex = args.findIndex(arg => arg?.id === parent.id)
5151

5252
if (this.shouldAddCompletions(functionName, qualifierName, argIndex)) {
53-
await this.addStringCompletions(ctx, result, functionName, qualifierName, argIndex)
53+
this.addStringCompletions(ctx, result, functionName, qualifierName, argIndex)
5454
}
5555
}
5656

@@ -66,5 +66,5 @@ export abstract class ActonStringArgumentCompletionProvider
6666
functionName: string,
6767
qualifierName: string | undefined,
6868
argumentIndex: number,
69-
): Promise<void>
69+
): void
7070
}

server/src/languages/tolk/completion/providers/ActonWalletNameCompletionProvider.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ export class ActonWalletNameCompletionProvider extends ActonStringArgumentComple
1818
return functionName === "wallet" && qualifierName === "net" && argumentIndex === 0
1919
}
2020

21-
protected async addStringCompletions(
22-
ctx: CompletionContext,
23-
result: CompletionResult,
24-
): Promise<void> {
25-
const actonToml = await ActonToml.find(ctx.element.file.uri)
21+
protected addStringCompletions(ctx: CompletionContext, result: CompletionResult): void {
22+
const actonToml = ActonToml.discover(ctx.element.file.uri)
2623
if (!actonToml) return
2724

28-
const wallets = await actonToml.getWallets()
25+
const wallets = actonToml.getWallets()
2926
for (const wallet of wallets) {
3027
result.add({
3128
label: wallet.name,

0 commit comments

Comments
 (0)