hledger-lsp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +632 -0
- package/out/extension.d.ts +1 -0
- package/out/extension.d.ts.map +1 -0
- package/out/extension.js +2 -0
- package/out/extension.js.map +1 -0
- package/out/features/codeActions.d.ts +73 -0
- package/out/features/codeActions.d.ts.map +1 -0
- package/out/features/codeActions.js +417 -0
- package/out/features/codeActions.js.map +1 -0
- package/out/features/completion.d.ts +94 -0
- package/out/features/completion.d.ts.map +1 -0
- package/out/features/completion.js +323 -0
- package/out/features/completion.js.map +1 -0
- package/out/features/definition.d.ts +12 -0
- package/out/features/definition.d.ts.map +1 -0
- package/out/features/definition.js +61 -0
- package/out/features/definition.js.map +1 -0
- package/out/features/documentLinks.d.ts +17 -0
- package/out/features/documentLinks.d.ts.map +1 -0
- package/out/features/documentLinks.js +68 -0
- package/out/features/documentLinks.js.map +1 -0
- package/out/features/findReferences.d.ts +33 -0
- package/out/features/findReferences.d.ts.map +1 -0
- package/out/features/findReferences.js +79 -0
- package/out/features/findReferences.js.map +1 -0
- package/out/features/foldingRanges.d.ts +27 -0
- package/out/features/foldingRanges.d.ts.map +1 -0
- package/out/features/foldingRanges.js +111 -0
- package/out/features/foldingRanges.js.map +1 -0
- package/out/features/formatter.d.ts +70 -0
- package/out/features/formatter.d.ts.map +1 -0
- package/out/features/formatter.js +373 -0
- package/out/features/formatter.js.map +1 -0
- package/out/features/hover.d.ts +66 -0
- package/out/features/hover.d.ts.map +1 -0
- package/out/features/hover.js +387 -0
- package/out/features/hover.js.map +1 -0
- package/out/features/inlayHints.d.ts +43 -0
- package/out/features/inlayHints.d.ts.map +1 -0
- package/out/features/inlayHints.js +221 -0
- package/out/features/inlayHints.js.map +1 -0
- package/out/features/selectionRange.d.ts +47 -0
- package/out/features/selectionRange.d.ts.map +1 -0
- package/out/features/selectionRange.js +273 -0
- package/out/features/selectionRange.js.map +1 -0
- package/out/features/semanticTokens.d.ts +83 -0
- package/out/features/semanticTokens.d.ts.map +1 -0
- package/out/features/semanticTokens.js +370 -0
- package/out/features/semanticTokens.js.map +1 -0
- package/out/features/symbols.d.ts +47 -0
- package/out/features/symbols.d.ts.map +1 -0
- package/out/features/symbols.js +249 -0
- package/out/features/symbols.js.map +1 -0
- package/out/features/transactionAnalyzer.d.ts +63 -0
- package/out/features/transactionAnalyzer.d.ts.map +1 -0
- package/out/features/transactionAnalyzer.js +127 -0
- package/out/features/transactionAnalyzer.js.map +1 -0
- package/out/features/validator.d.ts +142 -0
- package/out/features/validator.d.ts.map +1 -0
- package/out/features/validator.js +633 -0
- package/out/features/validator.js.map +1 -0
- package/out/parser/ast.d.ts +37 -0
- package/out/parser/ast.d.ts.map +1 -0
- package/out/parser/ast.js +606 -0
- package/out/parser/ast.js.map +1 -0
- package/out/parser/includes.d.ts +25 -0
- package/out/parser/includes.d.ts.map +1 -0
- package/out/parser/includes.js +106 -0
- package/out/parser/includes.js.map +1 -0
- package/out/parser/index.d.ts +54 -0
- package/out/parser/index.d.ts.map +1 -0
- package/out/parser/index.js +146 -0
- package/out/parser/index.js.map +1 -0
- package/out/server/deps.d.ts +19 -0
- package/out/server/deps.d.ts.map +1 -0
- package/out/server/deps.js +77 -0
- package/out/server/deps.js.map +1 -0
- package/out/server/settings.d.ts +60 -0
- package/out/server/settings.d.ts.map +1 -0
- package/out/server/settings.js +110 -0
- package/out/server/settings.js.map +1 -0
- package/out/server.d.ts +3 -0
- package/out/server.d.ts.map +1 -0
- package/out/server.js +420 -0
- package/out/server.js.map +1 -0
- package/out/types.d.ts +84 -0
- package/out/types.d.ts.map +1 -0
- package/out/types.js +6 -0
- package/out/types.js.map +1 -0
- package/out/utils/index.d.ts +38 -0
- package/out/utils/index.d.ts.map +1 -0
- package/out/utils/index.js +89 -0
- package/out/utils/index.js.map +1 -0
- package/out/utils/uri.d.ts +32 -0
- package/out/utils/uri.d.ts.map +1 -0
- package/out/utils/uri.js +215 -0
- package/out/utils/uri.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Symbol providers for document outline and workspace-wide search
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.workspaceSymbolProvider = exports.documentSymbolProvider = exports.WorkspaceSymbolProvider = exports.DocumentSymbolProvider = void 0;
|
|
7
|
+
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
8
|
+
class DocumentSymbolProvider {
|
|
9
|
+
/**
|
|
10
|
+
* Provide document symbols for outline view
|
|
11
|
+
* Returns hierarchical symbols representing the structure of the document
|
|
12
|
+
*/
|
|
13
|
+
provideDocumentSymbols(document, parsedDoc) {
|
|
14
|
+
const symbols = [];
|
|
15
|
+
const lines = document.getText().split('\n');
|
|
16
|
+
// Add directive symbols
|
|
17
|
+
for (const directive of parsedDoc.directives) {
|
|
18
|
+
// Only include directives from the current document
|
|
19
|
+
if (directive.sourceUri && directive.sourceUri !== document.uri) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const line = this.findDirectiveLine(lines, directive);
|
|
23
|
+
if (line !== -1) {
|
|
24
|
+
const range = vscode_languageserver_1.Range.create(line, 0, line, lines[line].length);
|
|
25
|
+
const kind = this.getDirectiveSymbolKind(directive.type);
|
|
26
|
+
symbols.push({
|
|
27
|
+
name: `${directive.type} ${directive.value}`,
|
|
28
|
+
detail: directive.comment || undefined,
|
|
29
|
+
kind,
|
|
30
|
+
range,
|
|
31
|
+
selectionRange: range
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Add transaction symbols
|
|
36
|
+
for (const transaction of parsedDoc.transactions) {
|
|
37
|
+
// Only include transactions from the current document
|
|
38
|
+
if (transaction.sourceUri && transaction.sourceUri !== document.uri) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const line = this.findTransactionLine(lines, transaction);
|
|
42
|
+
if (line !== -1) {
|
|
43
|
+
// Calculate the range of the entire transaction (including postings)
|
|
44
|
+
const endLine = this.findTransactionEndLine(lines, line);
|
|
45
|
+
const range = vscode_languageserver_1.Range.create(line, 0, endLine, lines[endLine].length);
|
|
46
|
+
const selectionRange = vscode_languageserver_1.Range.create(line, 0, line, lines[line].length);
|
|
47
|
+
const statusIcon = transaction.status === 'cleared' ? '* ' :
|
|
48
|
+
transaction.status === 'pending' ? '! ' : '';
|
|
49
|
+
const name = `${transaction.date} ${statusIcon}${transaction.description}`;
|
|
50
|
+
// Create child symbols for postings
|
|
51
|
+
const children = [];
|
|
52
|
+
for (const posting of transaction.postings) {
|
|
53
|
+
const postingLine = this.findPostingLine(lines, posting.account, line, endLine);
|
|
54
|
+
if (postingLine !== -1) {
|
|
55
|
+
const postingRange = vscode_languageserver_1.Range.create(postingLine, 0, postingLine, lines[postingLine].length);
|
|
56
|
+
const amountStr = posting.amount ? ` ${posting.amount.quantity}${posting.amount.commodity}` : '';
|
|
57
|
+
children.push({
|
|
58
|
+
name: posting.account + amountStr,
|
|
59
|
+
kind: vscode_languageserver_1.SymbolKind.Field,
|
|
60
|
+
range: postingRange,
|
|
61
|
+
selectionRange: postingRange
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
symbols.push({
|
|
66
|
+
name,
|
|
67
|
+
detail: transaction.code || undefined,
|
|
68
|
+
kind: vscode_languageserver_1.SymbolKind.Event,
|
|
69
|
+
range,
|
|
70
|
+
selectionRange,
|
|
71
|
+
children: children.length > 0 ? children : undefined
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return symbols;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Find the line number of a directive in the document
|
|
79
|
+
*/
|
|
80
|
+
findDirectiveLine(lines, directive) {
|
|
81
|
+
const pattern = new RegExp(`^${directive.type}\\s+${this.escapeRegex(directive.value)}`);
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
if (pattern.test(lines[i])) {
|
|
84
|
+
return i;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Find the line number of a transaction in the document
|
|
91
|
+
*/
|
|
92
|
+
findTransactionLine(lines, transaction) {
|
|
93
|
+
const datePattern = transaction.date.replace(/\//g, '\\/');
|
|
94
|
+
const statusChar = transaction.status === 'cleared' ? '\\*' :
|
|
95
|
+
transaction.status === 'pending' ? '!' : '';
|
|
96
|
+
const codeStr = transaction.code ? `\\(${this.escapeRegex(transaction.code)}\\)\\s*` : '';
|
|
97
|
+
const descPattern = this.escapeRegex(transaction.description);
|
|
98
|
+
const pattern = new RegExp(`^${datePattern}(?:\\s*=\\s*[0-9/-]+)?\\s*${statusChar}\\s*${codeStr}${descPattern}`);
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
if (pattern.test(lines[i])) {
|
|
101
|
+
return i;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Find the end line of a transaction (last posting or comment)
|
|
108
|
+
*/
|
|
109
|
+
findTransactionEndLine(lines, startLine) {
|
|
110
|
+
let endLine = startLine;
|
|
111
|
+
// Move to the next line and keep going while we have indented lines (postings/comments)
|
|
112
|
+
for (let i = startLine + 1; i < lines.length; i++) {
|
|
113
|
+
const line = lines[i];
|
|
114
|
+
// Empty line or unindented line marks the end
|
|
115
|
+
if (line.trim() === '' || !line.match(/^\s/)) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
endLine = i;
|
|
119
|
+
}
|
|
120
|
+
return endLine;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Find a posting line within a transaction
|
|
124
|
+
*/
|
|
125
|
+
findPostingLine(lines, account, startLine, endLine) {
|
|
126
|
+
const accountPattern = new RegExp(`^\\s+${this.escapeRegex(account)}`);
|
|
127
|
+
for (let i = startLine + 1; i <= endLine; i++) {
|
|
128
|
+
if (accountPattern.test(lines[i])) {
|
|
129
|
+
return i;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return -1;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get the appropriate SymbolKind for a directive type
|
|
136
|
+
*/
|
|
137
|
+
getDirectiveSymbolKind(directiveType) {
|
|
138
|
+
switch (directiveType) {
|
|
139
|
+
case 'account':
|
|
140
|
+
return vscode_languageserver_1.SymbolKind.Class;
|
|
141
|
+
case 'commodity':
|
|
142
|
+
return vscode_languageserver_1.SymbolKind.Number;
|
|
143
|
+
case 'payee':
|
|
144
|
+
return vscode_languageserver_1.SymbolKind.String;
|
|
145
|
+
case 'tag':
|
|
146
|
+
return vscode_languageserver_1.SymbolKind.Property;
|
|
147
|
+
case 'include':
|
|
148
|
+
return vscode_languageserver_1.SymbolKind.File;
|
|
149
|
+
case 'alias':
|
|
150
|
+
return vscode_languageserver_1.SymbolKind.Variable;
|
|
151
|
+
default:
|
|
152
|
+
return vscode_languageserver_1.SymbolKind.Constant;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Escape special regex characters
|
|
157
|
+
*/
|
|
158
|
+
escapeRegex(str) {
|
|
159
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.DocumentSymbolProvider = DocumentSymbolProvider;
|
|
163
|
+
class WorkspaceSymbolProvider {
|
|
164
|
+
/**
|
|
165
|
+
* Provide workspace-wide symbol search
|
|
166
|
+
* Returns flat list of symbols matching the query across all files
|
|
167
|
+
*/
|
|
168
|
+
provideWorkspaceSymbols(query, parsedDoc) {
|
|
169
|
+
const symbols = [];
|
|
170
|
+
const lowerQuery = query.toLowerCase();
|
|
171
|
+
// Search accounts
|
|
172
|
+
for (const account of parsedDoc.accounts) {
|
|
173
|
+
if (account.name.toLowerCase().includes(lowerQuery)) {
|
|
174
|
+
const uri = account.sourceUri || '';
|
|
175
|
+
const line = account.line ?? 0;
|
|
176
|
+
const location = vscode_languageserver_1.Location.create(uri, vscode_languageserver_1.Range.create(line, 0, line, 0));
|
|
177
|
+
symbols.push({
|
|
178
|
+
name: account.name,
|
|
179
|
+
kind: vscode_languageserver_1.SymbolKind.Class,
|
|
180
|
+
location,
|
|
181
|
+
containerName: account.declared ? 'Declared Account' : 'Account'
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Search payees
|
|
186
|
+
for (const payee of parsedDoc.payees) {
|
|
187
|
+
if (payee.name.toLowerCase().includes(lowerQuery)) {
|
|
188
|
+
const uri = payee.sourceUri || '';
|
|
189
|
+
const line = payee.line ?? 0;
|
|
190
|
+
const location = vscode_languageserver_1.Location.create(uri, vscode_languageserver_1.Range.create(line, 0, line, 0));
|
|
191
|
+
symbols.push({
|
|
192
|
+
name: payee.name,
|
|
193
|
+
kind: vscode_languageserver_1.SymbolKind.String,
|
|
194
|
+
location,
|
|
195
|
+
containerName: payee.declared ? 'Declared Payee' : 'Payee'
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Search commodities
|
|
200
|
+
for (const commodity of parsedDoc.commodities) {
|
|
201
|
+
if (commodity.name.toLowerCase().includes(lowerQuery)) {
|
|
202
|
+
const uri = commodity.sourceUri || '';
|
|
203
|
+
const line = commodity.line ?? 0;
|
|
204
|
+
const location = vscode_languageserver_1.Location.create(uri, vscode_languageserver_1.Range.create(line, 0, line, 0));
|
|
205
|
+
symbols.push({
|
|
206
|
+
name: commodity.name,
|
|
207
|
+
kind: vscode_languageserver_1.SymbolKind.Number,
|
|
208
|
+
location,
|
|
209
|
+
containerName: commodity.declared ? 'Declared Commodity' : 'Commodity'
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Search tags
|
|
214
|
+
for (const tag of parsedDoc.tags) {
|
|
215
|
+
if (tag.name.toLowerCase().includes(lowerQuery)) {
|
|
216
|
+
const uri = tag.sourceUri || '';
|
|
217
|
+
const line = tag.line ?? 0;
|
|
218
|
+
const location = vscode_languageserver_1.Location.create(uri, vscode_languageserver_1.Range.create(line, 0, line, 0));
|
|
219
|
+
symbols.push({
|
|
220
|
+
name: tag.name,
|
|
221
|
+
kind: vscode_languageserver_1.SymbolKind.Property,
|
|
222
|
+
location,
|
|
223
|
+
containerName: tag.declared ? 'Declared Tag' : 'Tag'
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Search transactions by description
|
|
228
|
+
for (const transaction of parsedDoc.transactions) {
|
|
229
|
+
if (transaction.description.toLowerCase().includes(lowerQuery)) {
|
|
230
|
+
const uri = transaction.sourceUri || '';
|
|
231
|
+
const line = transaction.line ?? 0;
|
|
232
|
+
const location = vscode_languageserver_1.Location.create(uri, vscode_languageserver_1.Range.create(line, 0, line, 0));
|
|
233
|
+
const statusIcon = transaction.status === 'cleared' ? '* ' :
|
|
234
|
+
transaction.status === 'pending' ? '! ' : '';
|
|
235
|
+
symbols.push({
|
|
236
|
+
name: `${transaction.date} ${statusIcon}${transaction.description}`,
|
|
237
|
+
kind: vscode_languageserver_1.SymbolKind.Event,
|
|
238
|
+
location,
|
|
239
|
+
containerName: 'Transaction'
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return symbols;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
exports.WorkspaceSymbolProvider = WorkspaceSymbolProvider;
|
|
247
|
+
exports.documentSymbolProvider = new DocumentSymbolProvider();
|
|
248
|
+
exports.workspaceSymbolProvider = new WorkspaceSymbolProvider();
|
|
249
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.js","sourceRoot":"","sources":["../../src/features/symbols.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction history analyzer for smart completions
|
|
3
|
+
*
|
|
4
|
+
* Analyzes transaction patterns to provide intelligent suggestions:
|
|
5
|
+
* - Payee to account associations
|
|
6
|
+
* - Account usage frequency
|
|
7
|
+
* - Common amounts for payee/account combinations
|
|
8
|
+
*/
|
|
9
|
+
import { ParsedDocument } from '../types';
|
|
10
|
+
export interface PayeeAccountPattern {
|
|
11
|
+
account: string;
|
|
12
|
+
frequency: number;
|
|
13
|
+
lastUsed?: Date;
|
|
14
|
+
}
|
|
15
|
+
export interface AccountFrequency {
|
|
16
|
+
account: string;
|
|
17
|
+
count: number;
|
|
18
|
+
lastUsed?: Date;
|
|
19
|
+
}
|
|
20
|
+
export interface AmountSuggestion {
|
|
21
|
+
amount: number;
|
|
22
|
+
commodity: string;
|
|
23
|
+
frequency: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class TransactionAnalyzer {
|
|
26
|
+
private payeeToAccounts;
|
|
27
|
+
private accountFrequency;
|
|
28
|
+
private payeeAccountAmounts;
|
|
29
|
+
/**
|
|
30
|
+
* Analyze a parsed document to build pattern maps
|
|
31
|
+
*/
|
|
32
|
+
analyze(parsed: ParsedDocument): void;
|
|
33
|
+
/**
|
|
34
|
+
* Analyze a single transaction
|
|
35
|
+
*/
|
|
36
|
+
private analyzeTransaction;
|
|
37
|
+
/**
|
|
38
|
+
* Get accounts commonly used with a payee, sorted by frequency
|
|
39
|
+
*/
|
|
40
|
+
getAccountsForPayee(payee: string, limit?: number): PayeeAccountPattern[];
|
|
41
|
+
/**
|
|
42
|
+
* Get all accounts sorted by usage frequency
|
|
43
|
+
*/
|
|
44
|
+
getAccountsByFrequency(limit?: number): AccountFrequency[];
|
|
45
|
+
/**
|
|
46
|
+
* Get common amounts for a payee/account combination
|
|
47
|
+
*/
|
|
48
|
+
getAmountSuggestionsForPayeeAccount(payee: string, account: string, commodity: string, limit?: number): AmountSuggestion[];
|
|
49
|
+
/**
|
|
50
|
+
* Get the most common payee/account pairs (for general insights)
|
|
51
|
+
*/
|
|
52
|
+
getMostCommonPayeeAccountPairs(limit?: number): Array<{
|
|
53
|
+
payee: string;
|
|
54
|
+
account: string;
|
|
55
|
+
frequency: number;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Check if we have any pattern data
|
|
59
|
+
*/
|
|
60
|
+
hasPatterns(): boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare const transactionAnalyzer: TransactionAnalyzer;
|
|
63
|
+
//# sourceMappingURL=transactionAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactionAnalyzer.d.ts","sourceRoot":"","sources":["../../src/features/transactionAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAe,MAAM,UAAU,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,mBAAmB,CAA+C;IAE1E;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAYrC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA+B1B;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,mBAAmB,EAAE;IAe7E;;OAEG;IACH,sBAAsB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAY1D;;OAEG;IACH,mCAAmC,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAU,GAChB,gBAAgB,EAAE;IAgBrB;;OAEG;IACH,8BAA8B,CAAC,KAAK,GAAE,MAAW,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAehH;;OAEG;IACH,WAAW,IAAI,OAAO;CAGvB;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Transaction history analyzer for smart completions
|
|
4
|
+
*
|
|
5
|
+
* Analyzes transaction patterns to provide intelligent suggestions:
|
|
6
|
+
* - Payee to account associations
|
|
7
|
+
* - Account usage frequency
|
|
8
|
+
* - Common amounts for payee/account combinations
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.transactionAnalyzer = exports.TransactionAnalyzer = void 0;
|
|
12
|
+
class TransactionAnalyzer {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.payeeToAccounts = new Map();
|
|
15
|
+
this.accountFrequency = new Map();
|
|
16
|
+
this.payeeAccountAmounts = new Map(); // payee:account -> amount -> count
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Analyze a parsed document to build pattern maps
|
|
20
|
+
*/
|
|
21
|
+
analyze(parsed) {
|
|
22
|
+
// Reset maps
|
|
23
|
+
this.payeeToAccounts.clear();
|
|
24
|
+
this.accountFrequency.clear();
|
|
25
|
+
this.payeeAccountAmounts.clear();
|
|
26
|
+
// Analyze each transaction
|
|
27
|
+
for (const transaction of parsed.transactions) {
|
|
28
|
+
this.analyzeTransaction(transaction);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Analyze a single transaction
|
|
33
|
+
*/
|
|
34
|
+
analyzeTransaction(transaction) {
|
|
35
|
+
const payee = transaction.description;
|
|
36
|
+
if (!payee)
|
|
37
|
+
return;
|
|
38
|
+
// Track accounts used with this payee
|
|
39
|
+
for (const posting of transaction.postings) {
|
|
40
|
+
const account = posting.account;
|
|
41
|
+
// Update payee-to-account mapping
|
|
42
|
+
if (!this.payeeToAccounts.has(payee)) {
|
|
43
|
+
this.payeeToAccounts.set(payee, new Map());
|
|
44
|
+
}
|
|
45
|
+
const accountMap = this.payeeToAccounts.get(payee);
|
|
46
|
+
accountMap.set(account, (accountMap.get(account) || 0) + 1);
|
|
47
|
+
// Update account frequency
|
|
48
|
+
this.accountFrequency.set(account, (this.accountFrequency.get(account) || 0) + 1);
|
|
49
|
+
// Track amounts for this payee/account combination
|
|
50
|
+
if (posting.amount) {
|
|
51
|
+
const key = `${payee}:${account}`;
|
|
52
|
+
if (!this.payeeAccountAmounts.has(key)) {
|
|
53
|
+
this.payeeAccountAmounts.set(key, new Map());
|
|
54
|
+
}
|
|
55
|
+
const amountMap = this.payeeAccountAmounts.get(key);
|
|
56
|
+
const amount = posting.amount.quantity;
|
|
57
|
+
amountMap.set(amount, (amountMap.get(amount) || 0) + 1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get accounts commonly used with a payee, sorted by frequency
|
|
63
|
+
*/
|
|
64
|
+
getAccountsForPayee(payee, limit = 10) {
|
|
65
|
+
const accountMap = this.payeeToAccounts.get(payee);
|
|
66
|
+
if (!accountMap)
|
|
67
|
+
return [];
|
|
68
|
+
const patterns = [];
|
|
69
|
+
for (const [account, frequency] of accountMap.entries()) {
|
|
70
|
+
patterns.push({ account, frequency });
|
|
71
|
+
}
|
|
72
|
+
// Sort by frequency (descending)
|
|
73
|
+
patterns.sort((a, b) => b.frequency - a.frequency);
|
|
74
|
+
return patterns.slice(0, limit);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get all accounts sorted by usage frequency
|
|
78
|
+
*/
|
|
79
|
+
getAccountsByFrequency(limit) {
|
|
80
|
+
const frequencies = [];
|
|
81
|
+
for (const [account, count] of this.accountFrequency.entries()) {
|
|
82
|
+
frequencies.push({ account, count });
|
|
83
|
+
}
|
|
84
|
+
// Sort by frequency (descending)
|
|
85
|
+
frequencies.sort((a, b) => b.count - a.count);
|
|
86
|
+
return limit ? frequencies.slice(0, limit) : frequencies;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get common amounts for a payee/account combination
|
|
90
|
+
*/
|
|
91
|
+
getAmountSuggestionsForPayeeAccount(payee, account, commodity, limit = 5) {
|
|
92
|
+
const key = `${payee}:${account}`;
|
|
93
|
+
const amountMap = this.payeeAccountAmounts.get(key);
|
|
94
|
+
if (!amountMap)
|
|
95
|
+
return [];
|
|
96
|
+
const suggestions = [];
|
|
97
|
+
for (const [amount, frequency] of amountMap.entries()) {
|
|
98
|
+
suggestions.push({ amount, commodity, frequency });
|
|
99
|
+
}
|
|
100
|
+
// Sort by frequency (descending)
|
|
101
|
+
suggestions.sort((a, b) => b.frequency - a.frequency);
|
|
102
|
+
return suggestions.slice(0, limit);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get the most common payee/account pairs (for general insights)
|
|
106
|
+
*/
|
|
107
|
+
getMostCommonPayeeAccountPairs(limit = 20) {
|
|
108
|
+
const pairs = [];
|
|
109
|
+
for (const [payee, accountMap] of this.payeeToAccounts.entries()) {
|
|
110
|
+
for (const [account, frequency] of accountMap.entries()) {
|
|
111
|
+
pairs.push({ payee, account, frequency });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Sort by frequency (descending)
|
|
115
|
+
pairs.sort((a, b) => b.frequency - a.frequency);
|
|
116
|
+
return pairs.slice(0, limit);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if we have any pattern data
|
|
120
|
+
*/
|
|
121
|
+
hasPatterns() {
|
|
122
|
+
return this.payeeToAccounts.size > 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.TransactionAnalyzer = TransactionAnalyzer;
|
|
126
|
+
exports.transactionAnalyzer = new TransactionAnalyzer();
|
|
127
|
+
//# sourceMappingURL=transactionAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactionAnalyzer.js","sourceRoot":"","sources":["../../src/features/transactionAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator for hledger journal files
|
|
3
|
+
*
|
|
4
|
+
* Provides validation for:
|
|
5
|
+
* - Transaction balance
|
|
6
|
+
* - Undeclared items (accounts, payees, commodities, tags)
|
|
7
|
+
* - Missing amounts
|
|
8
|
+
* - Include directives
|
|
9
|
+
*/
|
|
10
|
+
import { Diagnostic } from 'vscode-languageserver/node';
|
|
11
|
+
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
12
|
+
import { ParsedDocument } from '../types';
|
|
13
|
+
import { FileReader } from '../parser/index';
|
|
14
|
+
export interface ValidationResult {
|
|
15
|
+
diagnostics: Diagnostic[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validation settings
|
|
19
|
+
*/
|
|
20
|
+
export interface ValidationSettings {
|
|
21
|
+
validation?: {
|
|
22
|
+
balance?: boolean;
|
|
23
|
+
missingAmounts?: boolean;
|
|
24
|
+
undeclaredAccounts?: boolean;
|
|
25
|
+
undeclaredPayees?: boolean;
|
|
26
|
+
undeclaredCommodities?: boolean;
|
|
27
|
+
undeclaredTags?: boolean;
|
|
28
|
+
dateOrdering?: boolean;
|
|
29
|
+
balanceAssertions?: boolean;
|
|
30
|
+
emptyTransactions?: boolean;
|
|
31
|
+
invalidDates?: boolean;
|
|
32
|
+
futureDates?: boolean;
|
|
33
|
+
emptyDescriptions?: boolean;
|
|
34
|
+
includeFiles?: boolean;
|
|
35
|
+
circularIncludes?: boolean;
|
|
36
|
+
};
|
|
37
|
+
severity?: {
|
|
38
|
+
undeclaredAccounts?: 'error' | 'warning' | 'information' | 'hint';
|
|
39
|
+
undeclaredPayees?: 'error' | 'warning' | 'information' | 'hint';
|
|
40
|
+
undeclaredCommodities?: 'error' | 'warning' | 'information' | 'hint';
|
|
41
|
+
undeclaredTags?: 'error' | 'warning' | 'information' | 'hint';
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options for validation
|
|
46
|
+
*/
|
|
47
|
+
export interface ValidationOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Base URI for resolving include paths
|
|
50
|
+
*/
|
|
51
|
+
baseUri?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Function to check if files exist
|
|
54
|
+
*/
|
|
55
|
+
fileReader?: FileReader;
|
|
56
|
+
/**
|
|
57
|
+
* Validation settings from user configuration
|
|
58
|
+
*/
|
|
59
|
+
settings?: ValidationSettings;
|
|
60
|
+
}
|
|
61
|
+
export declare class Validator {
|
|
62
|
+
/**
|
|
63
|
+
* Validate a parsed hledger document
|
|
64
|
+
*/
|
|
65
|
+
validate(document: TextDocument, parsedDoc: ParsedDocument, options?: ValidationOptions): ValidationResult;
|
|
66
|
+
/**
|
|
67
|
+
* Validate transaction balance
|
|
68
|
+
* Transactions must balance (all amounts sum to zero per commodity)
|
|
69
|
+
*/
|
|
70
|
+
private validateBalance;
|
|
71
|
+
/**
|
|
72
|
+
* Validate missing amounts in transaction
|
|
73
|
+
* At most one posting can omit an amount
|
|
74
|
+
*/
|
|
75
|
+
private validateMissingAmounts;
|
|
76
|
+
/**
|
|
77
|
+
* Validate undeclared items
|
|
78
|
+
* Warn when accounts, payees, commodities, or tags are used but not declared
|
|
79
|
+
*/
|
|
80
|
+
private validateUndeclaredItems;
|
|
81
|
+
/**
|
|
82
|
+
* Get the range for a transaction (first line only for now)
|
|
83
|
+
*/
|
|
84
|
+
private getTransactionRange;
|
|
85
|
+
/**
|
|
86
|
+
* Find the first occurrence of a string in the document
|
|
87
|
+
*/
|
|
88
|
+
private findFirstOccurrence;
|
|
89
|
+
/**
|
|
90
|
+
* Validate date ordering
|
|
91
|
+
* Warn if transactions are not in chronological order
|
|
92
|
+
*/
|
|
93
|
+
private validateDateOrdering;
|
|
94
|
+
/**
|
|
95
|
+
* Format an amount with commodity according to declared format
|
|
96
|
+
*/
|
|
97
|
+
private formatAmountWithCommodity;
|
|
98
|
+
/**
|
|
99
|
+
* Validate balance assertions
|
|
100
|
+
* Check if balance assertions match calculated balances
|
|
101
|
+
*/
|
|
102
|
+
private validateBalanceAssertions;
|
|
103
|
+
/**
|
|
104
|
+
* Parse a date string to a Date object
|
|
105
|
+
*/
|
|
106
|
+
private parseDate;
|
|
107
|
+
/**
|
|
108
|
+
* Find the range for a specific posting within a transaction
|
|
109
|
+
*/
|
|
110
|
+
private findPostingRange;
|
|
111
|
+
/**
|
|
112
|
+
* Validate empty transactions
|
|
113
|
+
* Transactions must have at least 2 postings
|
|
114
|
+
*/
|
|
115
|
+
private validateEmptyTransaction;
|
|
116
|
+
/**
|
|
117
|
+
* Validate date format
|
|
118
|
+
* Check for invalid dates like 2024-13-01 or 2024-02-30
|
|
119
|
+
*/
|
|
120
|
+
private validateDateFormat;
|
|
121
|
+
/**
|
|
122
|
+
* Validate future dates
|
|
123
|
+
* Warn about transactions dated in the future
|
|
124
|
+
*/
|
|
125
|
+
private validateFutureDate;
|
|
126
|
+
/**
|
|
127
|
+
* Validate empty descriptions
|
|
128
|
+
* Warn about transactions with no description/payee
|
|
129
|
+
*/
|
|
130
|
+
private validateEmptyDescription;
|
|
131
|
+
/**
|
|
132
|
+
* Validate include directives
|
|
133
|
+
* Check for missing files and circular includes
|
|
134
|
+
*/
|
|
135
|
+
private validateIncludeDirectives;
|
|
136
|
+
/**
|
|
137
|
+
* Check if a circular include exists by recursively following includes
|
|
138
|
+
*/
|
|
139
|
+
private checkCircularInclude;
|
|
140
|
+
}
|
|
141
|
+
export declare const validator: Validator;
|
|
142
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/features/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAsB,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAwB,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;QAClE,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;QAChE,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;QACrE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;KAC/D,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,qBAAa,SAAS;IACpB;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB;IAuF1G;;;OAGG;IACH,OAAO,CAAC,eAAe;IAwDvB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAiG/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAsB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkB3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA8BjC;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAoDjC;;OAEG;IACH,OAAO,CAAC,SAAS;IAOjB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiCxB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAgBhC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA2E1B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAwB1B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAgBhC;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAyEjC;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAmC7B;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
|