3 files changed,
163 insertions(+),
14 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-08-19 15:06:19 +0300
Authored at:
2026-08-19 14:23:17 +0300
Change ID:
lxwwwvmqwyqwyuxwmrstzmlpwwtvxwzn
Parent:
b7de91e
M
internal/lsp/testdata/completion-contexts.txtar
··· 60 60 ~ monthly^ budget 61 61 62 62 63 -2024-01-15 Grocery 64 - expenses:food $50 65 - expenses:taxi $30 66 - assets:cash 67 - 68 -2024-01-16 ^ 69 - expenses:f^ood $5 70 - assets:cash 63 +2024-01-15 Grocery 64 + expenses:food $50 65 + expenses:taxi $30 66 + assets:cash 67 + 68 +2024-01-16 ^ 69 + expenses:f^ood $5 70 + assets:cash 71 71 72 72 in^c 73 73 20^ ··· 100 100 2024/0^1/20 Grocery Store 101 101 expenses:food $10 102 102 assets:cash 103 + 104 +P 2024-01-15 AAPL $1.5 105 +^P 2024-01-15 AAPL $1.5 106 +P^ 2024-01-15 AAPL $1.5 107 + 108 +P ^ 109 +P 2^ 110 +P 202-^ 111 +P 2024-0^ 112 +P 2024-01-15^ 113 +P 2024-01-15 ^ 114 +P 2024-01-15 U^ 115 +P 2024-01-15 "my fund" U^ 116 +P 2024-01-15 "my fund" $ 117 +P 2024-01-15 AAPL $ 118 +P 2024-01-15 AAPL $1.5^ 119 +P 2024-01-15 AAPL $1.^5 120 +P 2024-01-15 AAPL ^1.5 121 +P 2024-01-15 AAPL 1.5 ^ 122 +P 2024-01-15 AAPL 1.5 €^ 103 123 104 124 -- expect -- 105 125 19:0 date "" ··· 215 235 2024-06-15 216 236 98:6 date "2024/0" 217 237 2024/01/20 238 +103:0 none "" 239 +104:1 none "" 240 +106:2 date "" 241 + today 242 + yesterday 243 + 2 days ago 244 + 2024-06-15 245 + 2024/01/20 246 + 2024-01-16 247 + 2024-01-15 248 + 2024-01-05 249 + 2024-01-03 250 + 2024-01-02 251 + 2024-01-01 252 + 6-10 253 +107:3 date "2" 254 + today 255 + yesterday 256 + 2 days ago 257 + 2024-06-15 258 + 2024/01/20 259 + 2024-01-16 260 + 2024-01-15 261 + 2024-01-05 262 + 2024-01-03 263 + 2024-01-02 264 + 2024-01-01 265 +108:6 date "202-" 266 + today 267 + yesterday 268 + 2 days ago 269 + 2024-06-15 270 + 2024-01-16 271 + 2024-01-15 272 + 2024-01-05 273 + 2024-01-03 274 + 2024-01-02 275 + 2024-01-01 276 +109:8 date "2024-0" 277 + 2024-06-15 278 + 2024-01-16 279 + 2024-01-15 280 + 2024-01-05 281 + 2024-01-03 282 + 2024-01-02 283 + 2024-01-01 284 +110:12 date "2024-01-15" 285 + 2024-01-15 286 +111:13 commodity "" 287 + $ 288 + USD 289 +112:14 commodity "U" 290 + USD 291 +113:24 commodity "U" 292 + USD 293 +116:22 commodity "" 294 + $ 295 + USD 296 +117:21 none "" 297 +118:18 none "" 298 +119:22 commodity "" 299 + $ 300 + USD 301 +120:23 commodity "€"
M
internal/lsp/textdocument_completion.go
··· 84 84 return cmplNone, cursor 85 85 case token.ACCOUNT, token.COMMODITY, token.PAYEE, token.TAG: 86 86 return cmplDirectiveContext(cursor, lineStart, toks) 87 + case token.P: 88 + return cmplPriceContext(cursor, toks) 87 89 case token.TEXT: 88 90 return cmplDirective, lineStart // half-typed keyword or unparseable line 89 91 default: ··· 143 145 fieldStart := toks[0].Span.End.Offset 144 146 fieldEnd := fieldStart 145 147 seen := false 146 - for i := 1; i < len(toks); i++ { 147 - t := toks[i] 148 + for _, t := range toks[1:] { 148 149 switch t.Type { 149 150 case token.WHITESPACE, token.STAR, token.BANG, token.DATE, token.TIME, 150 151 token.EQ, token.EQEQ, token.EQEQEQ: ··· 224 225 return cmplTagName, start 225 226 } 226 227 return cmplNone, cursor 228 +} 229 + 230 +func cmplPriceContext(cursor int, toks []token.Token) (cmplCtx, int) { 231 + kwEnd := toks[0].Span.End.Offset 232 + if cursor > toks[0].Span.Start.Offset && cursor < kwEnd { 233 + return cmplNone, cursor // on the P keyword 234 + } 235 + 236 + dateStart := kwEnd // start of the date field, for replacing partials whole 237 + dateDone := false // a DATE, TIME, or symbol has ended the date field 238 + for _, t := range toks[1:] { // skip the P keyword 239 + if t.Type == token.WHITESPACE { 240 + continue 241 + } 242 + if !dateDone && dateStart == kwEnd { 243 + dateStart = t.Span.Start.Offset 244 + } 245 + if cursor > t.Span.End.Offset { 246 + switch t.Type { 247 + case token.DATE, token.TIME, token.COMMODITYMARK, token.STRING: 248 + dateDone = true 249 + } 250 + continue 251 + } 252 + if cursor < t.Span.Start.Offset { 253 + if (t.Type == token.INT || t.Type == token.DECIMAL) && !dateDone { 254 + return cmplDate, dateStart // partial date fragments ahead 255 + } 256 + if t.Type == token.DATE || t.Type == token.TIME { 257 + return cmplNone, cursor 258 + } 259 + return cmplCommodity, cursor // symbol or price-commodity slot 260 + } 261 + 262 + switch t.Type { 263 + case token.DATE: 264 + return cmplDate, t.Span.Start.Offset 265 + case token.COMMODITYMARK: 266 + return cmplCommodity, t.Span.Start.Offset 267 + case token.STRING: 268 + return cmplCommodity, min(t.Span.Start.Offset+1, cursor) // skip the opening quote 269 + case token.TIME: 270 + return cmplNone, cursor 271 + case token.INT, token.DECIMAL: // a partial date fragment, or the amount quantity 272 + if !dateDone { 273 + return cmplDate, dateStart 274 + } 275 + if cursor == t.Span.End.Offset { 276 + return cmplCommodity, cursor // suffix price-commodity slot 277 + } 278 + return cmplNone, cursor 279 + } 280 + } 281 + // the cursor sits in whitespace after the last token, or only after the keyword 282 + if !dateDone { 283 + start := dateStart 284 + if start == kwEnd { 285 + start = cursor // `P `: the date comes next 286 + } 287 + return cmplDate, start 288 + } 289 + return cmplCommodity, cursor 227 290 } 228 291 229 292 // commentStart completes tag names before ':' of the current tag and tag values after it
M
internal/lsp/textdocument_completion_test.go
··· 176 176 srv.server.analysisFor(uri.URI("file:///test.journal")) // warm the per-doc cache 177 177 178 178 for tname, tt := range map[string]int{ 179 - "1k txns, account": strings.Index(content, "\n 1:2:3 ") + len("\n ") + 2, 180 - "1k txns, empty payee": strings.Index(content, "transaction 1") + len("transaction "), 181 - "1k txns, commodity": strings.Index(content, "2 B @@") + len("2 B"), 182 - "1k txns, date": strings.Index(content, "2000-01-0") + len("2000-01-0"), 179 + "1k txns, account": strings.Index(content, "\n 1:2:3 ") + len("\n ") + 2, 180 + "1k txns, empty payee": strings.Index(content, "transaction 1") + len("transaction "), 181 + "1k txns, commodity": strings.Index(content, "2 B @@") + len("2 B"), 182 + "1k txns, date": strings.Index(content, "2000-01-0") + len("2000-01-0"), 183 + "1k txns, price date": strings.Index(content, "P 2000-01-0") + len("P 2000-01-0"), 184 + "1k txns, price quantity": strings.Index(content, "P 2000-01-01 A 0.70") + len("P 2000-01-01 A 0.70"), 183 185 } { 184 186 b.Run(tname, func(b *testing.B) { 185 187 line, col := lsputil.LineCol(content, tt)