Skip to content

Commit 63fe5fe

Browse files
committed
Implement account auto completion
1 parent e438bc1 commit 63fe5fe

5 files changed

Lines changed: 102 additions & 3 deletions

File tree

examples/accounts.ledger

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
account Assets:Cash
2+
account Assets:Bank
3+
account Expenses:Food
4+
account Income:Salary
5+
account Liabilities:CreditCard
6+
account Equity:OpeningBalances

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,20 @@
4949
"mac": "cmd+shift+t",
5050
"when": "editorTextFocus && resourceExtname == '.ledger'"
5151
}
52-
]
52+
],
53+
"configuration": {
54+
"title": "ledger-cli",
55+
"properties": {
56+
"ledger.accountFiles": {
57+
"type": "array",
58+
"items": {
59+
"type": "string"
60+
},
61+
"default": [],
62+
"description": "List of ledger files to include for account auto-completion."
63+
}
64+
}
65+
}
5366
},
5467
"scripts": {
5568
"vscode:prepublish": "npm run compile",

src/completion.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import * as vscode from 'vscode';
2+
3+
export class LedgerAccountCompletion implements vscode.CompletionItemProvider {
4+
accounts: string[] = [];
5+
6+
constructor() {
7+
const defaultAccountsFile = vscode.Uri.joinPath(vscode.extensions.getExtension('parsa2820.ledger-cli')!.extensionUri, 'examples', 'accounts.ledger').fsPath;
8+
const accountFiles = vscode.workspace.getConfiguration('ledger').get<string[]>('accountFiles', []);
9+
if (accountFiles.length === 0) {
10+
accountFiles.push(defaultAccountsFile);
11+
}
12+
accountFiles.forEach(file => {
13+
this.loadAccountFile(file).then(accounts => {
14+
this.accounts.push(...accounts);
15+
});
16+
});
17+
}
18+
19+
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionList<vscode.CompletionItem> | vscode.CompletionItem[]> {
20+
return this.accounts.map(account => new vscode.CompletionItem(account, vscode.CompletionItemKind.Variable));
21+
}
22+
23+
resolveCompletionItem?(item: vscode.CompletionItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CompletionItem> {
24+
throw new Error('Method not implemented.');
25+
}
26+
27+
private async loadAccountFile(filePath: string): Promise<string[]> {
28+
try {
29+
const fileUri = vscode.Uri.file(filePath);
30+
const fileData = await vscode.workspace.fs.readFile(fileUri);
31+
const fileText = Buffer.from(fileData).toString('utf8');
32+
return this.loadAccounts(fileText);
33+
} catch (error) {
34+
console.error(`Error reading account file ${filePath}:`, error);
35+
return [];
36+
}
37+
}
38+
39+
private loadAccounts(fileText: string): string[] {
40+
const accounts: string[] = [];
41+
const lines = fileText.split(/\r?\n/);
42+
for (const line of lines) {
43+
if (this.isAccountDeclarationLine(line)) {
44+
const account = this.extractAccountDeclarationLineEntry(line);
45+
if (account) {
46+
accounts.push(account);
47+
}
48+
}
49+
}
50+
return accounts;
51+
}
52+
53+
private isAccountDeclarationLine(text: string): boolean {
54+
return this.getAccountDeclarationPattern().test(text);
55+
}
56+
57+
private extractAccountDeclarationLineEntry(text: string): string | null {
58+
const pattern = this.getAccountDeclarationPattern();
59+
const match = pattern.exec(text);
60+
if (match && match.groups) {
61+
return match.groups['account'].trim();
62+
}
63+
return null;
64+
}
65+
66+
private getAccountDeclarationPattern(): RegExp {
67+
const accountNamePattern = /(?<account>[\[\(]?[A-Za-z0-9:_\-]+)[\]\)]?/;
68+
const commentPattern = /\s*(?<comment>[;#%\|\*].*)?/;
69+
return new RegExp(
70+
"^account\\s" +
71+
accountNamePattern.source +
72+
commentPattern.source +
73+
"$"
74+
);
75+
}
76+
}

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode';
22
import { LedgerDocumentFormatter } from './formatter';
33
import { registerCommands } from './commands';
4+
import { LedgerAccountCompletion } from './completion';
45

56
export function activate(context: vscode.ExtensionContext) {
67

@@ -9,6 +10,9 @@ export function activate(context: vscode.ExtensionContext) {
910
const formatterRegistration = vscode.languages.registerDocumentFormattingEditProvider('ledger', new LedgerDocumentFormatter());
1011
context.subscriptions.push(formatterRegistration);
1112

13+
const completionRegistration = vscode.languages.registerCompletionItemProvider('ledger', new LedgerAccountCompletion());
14+
context.subscriptions.push(completionRegistration);
15+
1216
registerCommands(context);
1317
}
1418

0 commit comments

Comments
 (0)