hledger-lsp 0.2.6 → 0.2.8
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/out/features/completion.js +4 -6
- package/out/features/completion.js.map +1 -1
- package/out/features/formatter.d.ts +11 -3
- package/out/features/formatter.d.ts.map +1 -1
- package/out/features/formatter.js +175 -56
- package/out/features/formatter.js.map +1 -1
- package/out/features/hover.d.ts.map +1 -1
- package/out/features/hover.js +3 -1
- package/out/features/hover.js.map +1 -1
- package/out/features/inlayHints.d.ts +1 -24
- package/out/features/inlayHints.d.ts.map +1 -1
- package/out/features/inlayHints.js +315 -218
- package/out/features/inlayHints.js.map +1 -1
- package/out/features/semanticTokens.d.ts.map +1 -1
- package/out/features/semanticTokens.js +14 -68
- package/out/features/semanticTokens.js.map +1 -1
- package/out/features/transactionAnalyzer.js +3 -5
- package/out/features/transactionAnalyzer.js.map +1 -1
- package/out/features/validator.d.ts.map +1 -1
- package/out/features/validator.js +7 -4
- package/out/features/validator.js.map +1 -1
- package/out/parser/includes.d.ts +0 -1
- package/out/parser/includes.d.ts.map +1 -1
- package/out/parser/includes.js +3 -6
- package/out/parser/includes.js.map +1 -1
- package/out/parser/index.d.ts +1 -1
- package/out/parser/index.d.ts.map +1 -1
- package/out/parser/index.js +4 -10
- package/out/parser/index.js.map +1 -1
- package/out/server/settings.d.ts +4 -4
- package/out/server/settings.d.ts.map +1 -1
- package/out/server/settings.js +2 -2
- package/out/server/settings.js.map +1 -1
- package/out/server/workspace.js +24 -22
- package/out/server/workspace.js.map +1 -1
- package/out/server.js +1 -1
- package/out/server.js.map +1 -1
- package/out/utils/runningBalanceCalculator.d.ts.map +1 -1
- package/out/utils/runningBalanceCalculator.js +0 -51
- package/out/utils/runningBalanceCalculator.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,30 +15,7 @@ export declare class InlayHintsProvider {
|
|
|
15
15
|
* Provide inlay hints for a document
|
|
16
16
|
*/
|
|
17
17
|
provideInlayHints(document: TextDocument, range: Range, parsed: ParsedDocument, settings?: HledgerSettings): InlayHint[];
|
|
18
|
-
|
|
19
|
-
* Calculate insertion position for inferred amount hint
|
|
20
|
-
*/
|
|
21
|
-
private calculateInferredAmountHintInsertionPosition;
|
|
22
|
-
/**
|
|
23
|
-
* Calculate insertion position for Running Balance assertion hint
|
|
24
|
-
*/
|
|
25
|
-
private calculateAssertionHintInsertionPosition;
|
|
26
|
-
/**
|
|
27
|
-
* Calculate insertion position for Cost assertion hint
|
|
28
|
-
*/
|
|
29
|
-
private calculateCostHintInsertionPosition;
|
|
30
|
-
/**
|
|
31
|
-
* Get hints for inferred amounts (postings with amounts marked as inferred)
|
|
32
|
-
*/
|
|
33
|
-
private getInferredAmountHints;
|
|
34
|
-
/**
|
|
35
|
-
* Get hints for running balances after each posting (with pre-calculated state)
|
|
36
|
-
*/
|
|
37
|
-
private getRunningBalanceHintsWithState;
|
|
38
|
-
/**
|
|
39
|
-
* Get hints for cost conversions
|
|
40
|
-
*/
|
|
41
|
-
private getCostConversionHints;
|
|
18
|
+
private processTransactions;
|
|
42
19
|
}
|
|
43
20
|
export declare const inlayHintsProvider: InlayHintsProvider;
|
|
44
21
|
//# sourceMappingURL=inlayHints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inlayHints.d.ts","sourceRoot":"","sources":["../../src/features/inlayHints.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAA+C,KAAK,EAAW,MAAM,uBAAuB,CAAC;AAC/G,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAElE,OAAO,EAAE,cAAc,EAAgC,MAAM,UAAU,CAAC;AAIxE,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"inlayHints.d.ts","sourceRoot":"","sources":["../../src/features/inlayHints.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAA+C,KAAK,EAAW,MAAM,uBAAuB,CAAC;AAC/G,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAElE,OAAO,EAAE,cAAc,EAAgC,MAAM,UAAU,CAAC;AAIxE,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAG5B,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,iBAAiB,CACf,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,cAAc,EACtB,QAAQ,CAAC,EAAE,eAAe,GACzB,SAAS,EAAE;IA8Kd,OAAO,CAAC,mBAAmB;CAgQ5B;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -14,262 +14,359 @@ const vscode_uri_1 = require("vscode-uri");
|
|
|
14
14
|
const amountFormatter_1 = require("../utils/amountFormatter");
|
|
15
15
|
const runningBalanceCalculator_1 = require("../utils/runningBalanceCalculator");
|
|
16
16
|
const settings_1 = require("../server/settings");
|
|
17
|
+
const formatter_1 = require("./formatter");
|
|
17
18
|
class InlayHintsProvider {
|
|
18
19
|
/**
|
|
19
20
|
* Provide inlay hints for a document
|
|
20
21
|
*/
|
|
21
22
|
provideInlayHints(document, range, parsed, settings) {
|
|
22
23
|
const config = { ...settings_1.DEFAULT_INLAY_HINTS_OPTIONS, ...settings?.inlayHints };
|
|
24
|
+
const formattingOptions = { ...settings_1.DEFAULT_FORMATTING_OPTIONS, ...settings?.formatting };
|
|
23
25
|
const hints = [];
|
|
24
|
-
// Normalize document URI to ensure proper encoding
|
|
25
26
|
const documentUri = vscode_uri_1.URI.parse(document.uri).toString();
|
|
26
27
|
// If showing running balances, we need to process all transactions to accumulate balances
|
|
27
|
-
// Otherwise, only process transactions within the requested range
|
|
28
28
|
const runningBalances = config.showRunningBalances
|
|
29
29
|
? (0, runningBalanceCalculator_1.calculateRunningBalances)(parsed)
|
|
30
30
|
: new Map();
|
|
31
|
-
// Only process transactions within the requested range
|
|
32
31
|
for (const transaction of parsed.transactions) {
|
|
33
|
-
// Only show inlay hints for transactions in the current document
|
|
34
32
|
if (transaction.sourceUri?.toString() !== documentUri) {
|
|
35
33
|
continue;
|
|
36
34
|
}
|
|
37
35
|
const txLine = transaction.line ?? 0;
|
|
38
|
-
// Skip transactions outside the range
|
|
39
36
|
if (txLine < range.start.line || txLine > range.end.line) {
|
|
40
37
|
continue;
|
|
41
38
|
}
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
// Cost conversion hints
|
|
51
|
-
if (config.showCostConversions) {
|
|
52
|
-
hints.push(...this.getCostConversionHints(document, transaction, parsed, settings));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return hints;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Calculate insertion position for inferred amount hint
|
|
59
|
-
*/
|
|
60
|
-
calculateInferredAmountHintInsertionPosition(line, accountEnd, amount, parsed, settings) {
|
|
61
|
-
const options = {
|
|
62
|
-
...settings_1.DEFAULT_FORMATTING_OPTIONS,
|
|
63
|
-
...settings?.formatting
|
|
64
|
-
};
|
|
65
|
-
// Get amount layout to determine pre-decimal width
|
|
66
|
-
const layout = (0, amountFormatter_1.getAmountLayout)(amount, parsed, options, '');
|
|
67
|
-
const preDecimalWidth = layout.commodityBefore.length +
|
|
68
|
-
(layout.spaceBetweenCommodityAndAmount && layout.commodityBefore ? 1 : 0) +
|
|
69
|
-
(layout.negPosSign ? 1 : 0) +
|
|
70
|
-
layout.amountIntegerString.length;
|
|
71
|
-
// Calculate the target position for the start of the amount (before the decimal)
|
|
72
|
-
const targetColumn = Math.max(options.decimalAlignColumn, accountEnd + options.minSpacing + preDecimalWidth);
|
|
73
|
-
const amountStartColumn = targetColumn - preDecimalWidth;
|
|
74
|
-
return amountStartColumn;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Calculate insertion position for Running Balance assertion hint
|
|
78
|
-
*/
|
|
79
|
-
calculateAssertionHintInsertionPosition(line, accountEnd, amount, parsed, settings) {
|
|
80
|
-
const options = {
|
|
81
|
-
...settings_1.DEFAULT_FORMATTING_OPTIONS,
|
|
82
|
-
...settings?.formatting
|
|
83
|
-
};
|
|
84
|
-
// TODO implement proper calculation for assertion hint position
|
|
85
|
-
return options.decimalAlignColumn + 4; // +3 for ".00"
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Calculate insertion position for Cost assertion hint
|
|
89
|
-
*/
|
|
90
|
-
calculateCostHintInsertionPosition(line, accountEnd, amount, parsed, settings) {
|
|
91
|
-
const options = {
|
|
92
|
-
...settings_1.DEFAULT_FORMATTING_OPTIONS,
|
|
93
|
-
...settings?.formatting
|
|
94
|
-
};
|
|
95
|
-
// TODO implement proper calculation for assertion hint position
|
|
96
|
-
return options.decimalAlignColumn + 3;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Get hints for inferred amounts (postings with amounts marked as inferred)
|
|
100
|
-
*/
|
|
101
|
-
getInferredAmountHints(document, transaction, parsed, settings) {
|
|
102
|
-
const hints = [];
|
|
103
|
-
const txLine = transaction.line ?? 0;
|
|
104
|
-
let postingIndex = 0;
|
|
105
|
-
for (const posting of transaction.postings) {
|
|
106
|
-
// Show hint only for inferred amounts
|
|
107
|
-
if (posting.amount && posting.amount.inferred) {
|
|
108
|
-
const lineNum = txLine + 1 + postingIndex; // +1 for header, then posting index
|
|
39
|
+
// Use formatter to calculate ideal widths (Grid)
|
|
40
|
+
// We pass the inlay hints config so the grid accounts for inferred items
|
|
41
|
+
const widths = formatter_1.formattingProvider.calculateTransactionWidths(transaction, parsed, formattingOptions, config);
|
|
42
|
+
let postingIndex = 0;
|
|
43
|
+
for (const posting of transaction.postings) {
|
|
44
|
+
const lineNum = txLine + 1 + postingIndex;
|
|
45
|
+
// Get the current line content
|
|
109
46
|
const line = document.getText({
|
|
110
47
|
start: { line: lineNum, character: 0 },
|
|
111
48
|
end: { line: lineNum, character: Number.MAX_SAFE_INTEGER }
|
|
112
49
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
if
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
50
|
+
const accountEndIndex = line.indexOf(posting.account) + posting.account.length;
|
|
51
|
+
// Find comment position
|
|
52
|
+
const commentMatch = line.match(/[;#]/);
|
|
53
|
+
const commentIndex = commentMatch ? commentMatch.index : -1;
|
|
54
|
+
// Content "End" (before comment)
|
|
55
|
+
const contentEndIndex = commentIndex !== -1 ? commentIndex : line.length;
|
|
56
|
+
const contentBeforeComment = line.substring(0, contentEndIndex);
|
|
57
|
+
const trimmedContent = contentBeforeComment.trimEnd();
|
|
58
|
+
// Check if there is unexpected content after account
|
|
59
|
+
// If the line is just "Account", trimmedContent length is accountEndIndex.
|
|
60
|
+
// If the line is "Account $10", trimmedContent is longer.
|
|
61
|
+
// We only insert hints if we are "at the end" of the relevant explicit content.
|
|
62
|
+
// 1. Inferred Amount
|
|
63
|
+
if (config.showInferredAmounts && posting.amount && posting.amount.inferred) {
|
|
64
|
+
// Check if we have explicit content blocking us (e.g. tags, or manually written stuff)
|
|
65
|
+
// If the content after account is just whitespace, we are good.
|
|
66
|
+
if (trimmedContent.length <= accountEndIndex) {
|
|
67
|
+
const amountPreDecimalWidth = widths.amount.commodityBefore +
|
|
68
|
+
widths.amount.spaceBetweenCommodityBeforeAndAmount +
|
|
69
|
+
widths.amount.negPosSign +
|
|
70
|
+
widths.amount.integerPart;
|
|
71
|
+
const targetColumn = formattingOptions.decimalAlignColumn;
|
|
72
|
+
// Calculate where the hint should visually start to align the decimal
|
|
73
|
+
const hintStartColumn = targetColumn - amountPreDecimalWidth;
|
|
74
|
+
// Current position is contentEndIndex.
|
|
75
|
+
// We need padding to reach hintStartColumn.
|
|
76
|
+
// Note: InlayHint padding is visual.
|
|
77
|
+
const currentColumn = contentEndIndex; // Assuming simple chars
|
|
78
|
+
const paddingNeeded = Math.max(1, hintStartColumn - currentColumn); // At least 1 space
|
|
79
|
+
const amountText = (0, amountFormatter_1.formatAmount)(posting.amount.quantity, posting.amount.commodity, parsed, settings?.formatting);
|
|
80
|
+
const labelPart = {
|
|
81
|
+
value: amountText,
|
|
82
|
+
command: {
|
|
83
|
+
title: 'Insert inferred amount',
|
|
84
|
+
command: 'hledger.insertInferredAmount',
|
|
85
|
+
arguments: [
|
|
86
|
+
document.uri,
|
|
87
|
+
lineNum,
|
|
88
|
+
contentEndIndex, // Insert at end of current content
|
|
89
|
+
posting.amount.quantity,
|
|
90
|
+
posting.amount.commodity
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
hints.push({
|
|
95
|
+
position: vscode_languageserver_1.Position.create(lineNum, contentEndIndex),
|
|
96
|
+
label: [labelPart],
|
|
97
|
+
kind: vscode_languageserver_1.InlayHintKind.Parameter,
|
|
98
|
+
paddingLeft: true, // Let VS Code handle standard padding? No, we want exact alignment.
|
|
99
|
+
// If we use paddingLeft, it adds a standard space.
|
|
100
|
+
// To do exact alignment, we might need to prepend spaces to the label value.
|
|
101
|
+
// But user warned about VS Code truncating large padding.
|
|
102
|
+
// Ideally, if the formatter ran, `currentColumn` matches `hintStartColumn`.
|
|
103
|
+
// If not, we pad.
|
|
104
|
+
});
|
|
105
|
+
// Correct padding approach for alignment:
|
|
106
|
+
// We can use `label` with leading spaces.
|
|
107
|
+
const paddingString = ' '.repeat(paddingNeeded);
|
|
108
|
+
// But wait, if we use `paddingLeft: true`, it adds roughly one space width?
|
|
109
|
+
// We want exact column alignment.
|
|
110
|
+
// Better to add spaces to the label value if we want strict alignment.
|
|
111
|
+
// The user said: "if we pad the inlay hint itself then it has to be quite large... vscode seems to shorten is to ..."
|
|
112
|
+
// This implies we should rely on the DOCUMENT having the whitespace (via formatter).
|
|
113
|
+
// IF the document has whitespace, `paddingNeeded` will be small (0 or 1).
|
|
114
|
+
// IF the document does not, we unfortunately have to pad the hint.
|
|
115
|
+
// Let's modify the label to include the padding.
|
|
116
|
+
labelPart.value = paddingString + amountText;
|
|
117
|
+
// If we have a comment, we insert AT the comment position?
|
|
118
|
+
// If we insert at `contentEndIndex`, and comment is at `commentIndex` (which equals contentEndIndex),
|
|
119
|
+
// the hint appears before the comment.
|
|
120
|
+
// This pushes the comment to the right. Correct.
|
|
145
121
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
122
|
+
}
|
|
123
|
+
// 2. Inferred Cost
|
|
124
|
+
// ... (similar logic, using widths.cost)
|
|
125
|
+
if (config.showCostConversions && posting.cost && posting.cost.inferred) {
|
|
126
|
+
// We can only show inferred cost if we are "past" the amount.
|
|
127
|
+
// If amount was inferred, we effectively "added" it above.
|
|
128
|
+
// But LSP Inlay Hints are independent.
|
|
129
|
+
// If we have an inferred amount AND inferred cost, we need to stack them?
|
|
130
|
+
// VS Code places hints at the same position in order.
|
|
131
|
+
// So if we push Amount Hint, then Cost Hint, they appear: [Amount] [Cost].
|
|
132
|
+
// We just need to calculate the Cost padding relative to the End of the Amount.
|
|
133
|
+
// Where does the Amount end?
|
|
134
|
+
// Amount End = TargetColumn (decimal align) + PostDecimalWidth.
|
|
135
|
+
const amountPostDecimalWidth = widths.amount.decimalMark +
|
|
136
|
+
widths.amount.decimalPart +
|
|
137
|
+
widths.amount.spaceBetweenAmountAndCommodityAfter +
|
|
138
|
+
widths.amount.commodityAfter;
|
|
139
|
+
// The visual end of the amount block (whether explicit or inferred)
|
|
140
|
+
const amountVisualEnd = formattingOptions.decimalAlignColumn + amountPostDecimalWidth;
|
|
141
|
+
// Cost Start
|
|
142
|
+
// We align Cost based on widths.cost?
|
|
143
|
+
// Usually Cost keeps going.
|
|
144
|
+
// Formatter logic: `line += renderAmountLayout(..., widths.cost)`
|
|
145
|
+
// It just appends.
|
|
146
|
+
// So we just need minimal padding from Amount End.
|
|
147
|
+
// But if we are in "Inferred Amount" case, our "current physical position" is still `contentEndIndex`.
|
|
148
|
+
// The Amount Hint adds virtual width.
|
|
149
|
+
// We need to account for that.
|
|
150
|
+
// This suggests we should calculate a "virtual cursor" position.
|
|
151
|
+
// Start at `contentEndIndex`.
|
|
152
|
+
// If Inferred Amount:
|
|
153
|
+
// Add padding + AmountText width to virtual cursor.
|
|
154
|
+
// If Explicit Amount:
|
|
155
|
+
// Virtual cursor is at end of amount (which is `contentEndIndex`).
|
|
156
|
+
// Let's implement this "Virtual Cursor" flow.
|
|
157
|
+
}
|
|
158
|
+
postingIndex++;
|
|
154
159
|
}
|
|
155
|
-
postingIndex++;
|
|
156
160
|
}
|
|
157
|
-
return
|
|
161
|
+
return this.processTransactions(parsed, document, range, config, formattingOptions, runningBalances);
|
|
158
162
|
}
|
|
159
|
-
|
|
160
|
-
* Get hints for running balances after each posting (with pre-calculated state)
|
|
161
|
-
*/
|
|
162
|
-
getRunningBalanceHintsWithState(document, transaction, parsed, runningBalances, settings) {
|
|
163
|
+
processTransactions(parsed, document, range, config, formattingOptions, runningBalances) {
|
|
163
164
|
const hints = [];
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
for (let postingIndex = 0; postingIndex < transaction.postings.length; postingIndex++) {
|
|
175
|
-
const posting = transaction.postings[postingIndex];
|
|
176
|
-
const balanceMap = postingBalances.get(postingIndex);
|
|
177
|
-
// Don't show running balance hint if posting already has a balance assertion
|
|
178
|
-
// Show hint for both explicit and inferred amounts
|
|
179
|
-
if (balanceMap && !posting.assertion) {
|
|
165
|
+
const documentUri = vscode_uri_1.URI.parse(document.uri).toString();
|
|
166
|
+
for (const transaction of parsed.transactions) {
|
|
167
|
+
if (transaction.sourceUri?.toString() !== documentUri)
|
|
168
|
+
continue;
|
|
169
|
+
const txLine = transaction.line ?? 0;
|
|
170
|
+
if (txLine < range.start.line || txLine > range.end.line)
|
|
171
|
+
continue;
|
|
172
|
+
const widths = formatter_1.formattingProvider.calculateTransactionWidths(transaction, parsed, formattingOptions, config);
|
|
173
|
+
let postingIndex = 0;
|
|
174
|
+
for (const posting of transaction.postings) {
|
|
180
175
|
const lineNum = txLine + 1 + postingIndex;
|
|
181
176
|
const line = document.getText({
|
|
182
177
|
start: { line: lineNum, character: 0 },
|
|
183
178
|
end: { line: lineNum, character: Number.MAX_SAFE_INTEGER }
|
|
184
179
|
});
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const balanceHints = [];
|
|
198
|
-
for (const [commodity, balance] of balanceMap.entries()) {
|
|
199
|
-
const formattedBalance = (0, amountFormatter_1.formatAmount)(balance, commodity, parsed, settings?.formatting);
|
|
200
|
-
balanceHints.push(formattedBalance);
|
|
180
|
+
const commentMatch = line.match(/[;#]/);
|
|
181
|
+
const commentIndex = commentMatch ? commentMatch.index : -1;
|
|
182
|
+
const contentEndIndex = commentIndex !== -1 ? commentIndex : line.length;
|
|
183
|
+
const trimmedContent = line.substring(0, contentEndIndex).trimEnd();
|
|
184
|
+
// Virtual cursor assumes we are starting from the end of the clean content
|
|
185
|
+
// Use contentEndIndex (actual cursor) to account for existing whitespace
|
|
186
|
+
let virtualColumn = contentEndIndex ?? 0;
|
|
187
|
+
const insertPosition = vscode_languageserver_1.Position.create(lineNum, contentEndIndex);
|
|
188
|
+
// 1. Amount
|
|
189
|
+
if (posting.amount && !posting.amount.inferred) {
|
|
190
|
+
// Explicit amount. Update virtual cursor to end of content.
|
|
191
|
+
// (which is already done by init virtualColumn = trimmedContent.length)
|
|
201
192
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
193
|
+
else if (config.showInferredAmounts && posting.amount?.inferred) {
|
|
194
|
+
// Only show if we don't have unexpected garbage
|
|
195
|
+
// AND if we are not blocked by explicit items (Cost or Assertion)
|
|
196
|
+
const hasExplicitCost = posting.cost && !posting.cost.inferred;
|
|
197
|
+
const hasExplicitAssertion = posting.assertion;
|
|
198
|
+
if (!hasExplicitCost && !hasExplicitAssertion) {
|
|
199
|
+
const amountPreDecimalWidth = widths.amount.commodityBefore +
|
|
200
|
+
widths.amount.spaceBetweenCommodityBeforeAndAmount +
|
|
201
|
+
widths.amount.negPosSign +
|
|
202
|
+
widths.amount.integerPart;
|
|
203
|
+
const targetColumn = formattingOptions.decimalAlignColumn;
|
|
204
|
+
const requiredPadding = Math.max(0, targetColumn - virtualColumn - amountPreDecimalWidth);
|
|
205
|
+
const amountText = (0, amountFormatter_1.formatAmount)(posting.amount.quantity, posting.amount.commodity, parsed, formattingOptions);
|
|
206
|
+
const label = ' '.repeat(requiredPadding) + amountText;
|
|
207
|
+
hints.push({
|
|
208
|
+
position: insertPosition,
|
|
209
|
+
label: [{
|
|
210
|
+
value: label,
|
|
211
|
+
command: {
|
|
212
|
+
title: 'Insert inferred amount',
|
|
213
|
+
command: 'hledger.insertInferredAmount',
|
|
214
|
+
arguments: [document.uri, lineNum, contentEndIndex, posting.amount.quantity, posting.amount.commodity]
|
|
215
|
+
}
|
|
216
|
+
}],
|
|
217
|
+
kind: vscode_languageserver_1.InlayHintKind.Parameter,
|
|
218
|
+
paddingLeft: false
|
|
219
|
+
});
|
|
220
|
+
virtualColumn += label.length;
|
|
216
221
|
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// No amount (or blocked inferred).
|
|
225
|
+
// We effectively skip the "Amount Block".
|
|
226
|
+
// But for alignment of subsequent items, we might need to pad PAST the amount block?
|
|
227
|
+
// See formatter logic:
|
|
228
|
+
/*
|
|
229
|
+
const postDecimalWidth = ...
|
|
230
|
+
line += ' '.repeat(padding + preDecimalWidth + postDecimalWidth);
|
|
231
|
+
*/
|
|
232
|
+
// If we have hidden amounts but show costs, we might need to pad.
|
|
233
|
+
// But Inlay Hints are additive. We can't insert "just padding" easily without a label.
|
|
234
|
+
// Unless we attach it to the next hint.
|
|
235
|
+
}
|
|
236
|
+
// 2. Cost
|
|
237
|
+
if (posting.cost && !posting.cost.inferred) {
|
|
238
|
+
// Explicit cost. Virtual cursor should logically be after this.
|
|
239
|
+
// But wait, if explicit cost exists, it's in the text.
|
|
240
|
+
// So virtualColumn (initially `trimmedContent.length`) ALREADY includes it.
|
|
241
|
+
// We don't need to do anything.
|
|
242
|
+
}
|
|
243
|
+
else if (config.showCostConversions && posting.cost?.inferred) {
|
|
244
|
+
// We want to add Cost Hint.
|
|
245
|
+
// Check if blocked by explicit assertion
|
|
246
|
+
const hasExplicitAssertion = posting.assertion;
|
|
247
|
+
if (!hasExplicitAssertion) {
|
|
248
|
+
const amountIsPresent = (posting.amount && !posting.amount.inferred) ||
|
|
249
|
+
(config.showInferredAmounts && posting.amount?.inferred && !hasExplicitAssertion && !(posting.cost && !posting.cost.inferred));
|
|
250
|
+
// Note: Logic for amountIsPresent is a bit circular if we re-check blocking logic.
|
|
251
|
+
// Simplified: If virtualColumn has moved (meaning we added hint) OR we have explicit amount.
|
|
252
|
+
// But virtualColumn update handles the Hint case.
|
|
253
|
+
// Explicit Amount case? virtualColumn includes it.
|
|
254
|
+
// So we just need to check if we are "at the start"?
|
|
255
|
+
// Or just always pad?
|
|
256
|
+
// If virtualColumn > trimmedContent.length, we added an Amount Hint.
|
|
257
|
+
// If explicit Amount exists, trimmedContent.length includes it.
|
|
258
|
+
// So we basically always want a space IF there is something before us.
|
|
259
|
+
// If "Account" -> Inferred Amount -> " $10" -> Inferred Cost.
|
|
260
|
+
// virtualColumn is at end of "$10". We want space.
|
|
261
|
+
const padding = 1;
|
|
262
|
+
const marker = (posting.cost.type === 'unit' ? '@' : '@@'); // Space handled by padding
|
|
263
|
+
const costText = (0, amountFormatter_1.formatAmount)(posting.cost.amount.quantity, posting.cost.amount.commodity, parsed, formattingOptions);
|
|
264
|
+
const label = ' '.repeat(padding) + marker + ' ' + costText;
|
|
265
|
+
hints.push({
|
|
266
|
+
position: insertPosition,
|
|
267
|
+
label: [{
|
|
268
|
+
value: label,
|
|
269
|
+
command: {
|
|
270
|
+
title: 'Insert cost',
|
|
271
|
+
command: 'hledger.insertCost',
|
|
272
|
+
arguments: [document.uri, lineNum, contentEndIndex, posting.cost.amount.quantity, posting.cost.amount.commodity]
|
|
273
|
+
}
|
|
274
|
+
}],
|
|
275
|
+
kind: vscode_languageserver_1.InlayHintKind.Parameter,
|
|
276
|
+
paddingLeft: false
|
|
277
|
+
});
|
|
278
|
+
virtualColumn += label.length;
|
|
262
279
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
280
|
+
}
|
|
281
|
+
// 3. Running Balance (Assertion)
|
|
282
|
+
if (config.showRunningBalances && !posting.assertion) {
|
|
283
|
+
// Find running balance
|
|
284
|
+
const txIndex = parsed.transactions.indexOf(transaction);
|
|
285
|
+
const postingBalances = runningBalances.get(txIndex);
|
|
286
|
+
const balanceMap = postingBalances?.get(postingIndex);
|
|
287
|
+
if (balanceMap) {
|
|
288
|
+
// Align assertion?
|
|
289
|
+
// Currently formatter aligns assertions using `widths.assertion`.
|
|
290
|
+
// But as noted in formatter.ts, running balances are dynamic width.
|
|
291
|
+
// We can try to align to `widths.assertion` START if it exists?
|
|
292
|
+
// Or just align to a fixed column?
|
|
293
|
+
// Formatter says: `line += renderAmountLayout(layout, widths.assertion)` with `marker = ' = '`.
|
|
294
|
+
// If we are appending to existing text/hints:
|
|
295
|
+
// We want to reach `widths.assertion` start column?
|
|
296
|
+
// Where does assertion start in the grid?
|
|
297
|
+
// It starts after Cost block.
|
|
298
|
+
// Cost block width = widths.cost....
|
|
299
|
+
// We need to calculate cumulative width of grid?
|
|
300
|
+
// Implementation Simplification:
|
|
301
|
+
// Just add " = " + balances.
|
|
302
|
+
// The user wants alignment.
|
|
303
|
+
// If we have explicit assertions elsewhere, `widths.assertion` will be non-zero.
|
|
304
|
+
// We can try to align the "=" to separate column?
|
|
305
|
+
// That requires tracking the cumulative width of (Indent + Account + Amount + Cost).
|
|
306
|
+
// Let's Calculate the Ideal Start Column for Assertion
|
|
307
|
+
// Indent + Account + 2 spaces + AmountBlock + CostBlock
|
|
308
|
+
// AmountBlock width = preDecimal + decimalMark + postDecimal...
|
|
309
|
+
// Wait, `widths` just gives MAX widths of components.
|
|
310
|
+
// Constructing the full offset:
|
|
311
|
+
let idealStart = formattingOptions.indentation + widths.account + 2;
|
|
312
|
+
// Amount Block Width
|
|
313
|
+
const amountBlockWidth = widths.amount.commodityBefore +
|
|
314
|
+
widths.amount.spaceBetweenCommodityBeforeAndAmount +
|
|
315
|
+
widths.amount.negPosSign +
|
|
316
|
+
widths.amount.integerPart +
|
|
317
|
+
widths.amount.decimalMark +
|
|
318
|
+
widths.amount.decimalPart +
|
|
319
|
+
widths.amount.spaceBetweenAmountAndCommodityAfter +
|
|
320
|
+
widths.amount.commodityAfter;
|
|
321
|
+
// Correct logic: The Amount aligns at `decimalAlignColumn`.
|
|
322
|
+
// So the Amount Block *ends* at `decimalAlignColumn` + `postDecimalPart`.
|
|
323
|
+
const amountPostDecimal = widths.amount.decimalMark +
|
|
324
|
+
widths.amount.decimalPart +
|
|
325
|
+
widths.amount.spaceBetweenAmountAndCommodityAfter +
|
|
326
|
+
widths.amount.commodityAfter;
|
|
327
|
+
const amountEndColumn = formattingOptions.decimalAlignColumn + amountPostDecimal;
|
|
328
|
+
// Cost Block
|
|
329
|
+
// Cost is appended after Amount.
|
|
330
|
+
// Does it strictly align?
|
|
331
|
+
// Formatter: `line += renderAmountLayout(layout, widths.cost)`
|
|
332
|
+
// It renders the cost width.
|
|
333
|
+
// Unlike Amount, Cost is not decimal-aligned to a global column in the simplified logic,
|
|
334
|
+
// it just takes up `widths.cost` space.
|
|
335
|
+
const costBlockWidth = widths.cost.marker + // " @ "
|
|
336
|
+
widths.cost.commodityBefore +
|
|
337
|
+
widths.cost.spaceBetweenCommodityBeforeAndAmount +
|
|
338
|
+
widths.cost.negPosSign +
|
|
339
|
+
widths.cost.integerPart +
|
|
340
|
+
widths.cost.decimalMark +
|
|
341
|
+
widths.cost.decimalPart +
|
|
342
|
+
widths.cost.spaceBetweenAmountAndCommodityAfter +
|
|
343
|
+
widths.cost.commodityAfter;
|
|
344
|
+
const costEndColumn = amountEndColumn + costBlockWidth;
|
|
345
|
+
// So Assertion should start at `costEndColumn`.
|
|
346
|
+
const padding = Math.max(1, costEndColumn - virtualColumn);
|
|
347
|
+
const balanceStrings = [];
|
|
348
|
+
for (const [comm, amount] of balanceMap) {
|
|
349
|
+
balanceStrings.push((0, amountFormatter_1.formatAmount)(amount, comm, parsed, formattingOptions));
|
|
350
|
+
}
|
|
351
|
+
const balanceText = balanceStrings.join(', ');
|
|
352
|
+
const label = ' '.repeat(padding) + '= ' + balanceText;
|
|
353
|
+
hints.push({
|
|
354
|
+
position: insertPosition,
|
|
355
|
+
label: [{
|
|
356
|
+
value: label,
|
|
357
|
+
command: {
|
|
358
|
+
title: 'Insert balance assertion',
|
|
359
|
+
command: 'hledger.insertBalanceAssertion',
|
|
360
|
+
arguments: [document.uri, lineNum, posting.account, balanceStrings]
|
|
361
|
+
}
|
|
362
|
+
}],
|
|
363
|
+
kind: vscode_languageserver_1.InlayHintKind.Type,
|
|
364
|
+
paddingLeft: false
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
postingIndex++;
|
|
271
369
|
}
|
|
272
|
-
postingIndex++;
|
|
273
370
|
}
|
|
274
371
|
return hints;
|
|
275
372
|
}
|