diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-22 19:29:41 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-22 19:29:41 +0300 |
| commit | a04e510ee205c8f137636f0bd47263a64b047730 (patch) | |
| tree | 2d2dc048466b2d686d8d0589d7a3dc668a54b625 /internal | |
| parent | 02603f6f33c89b796f5eff60d0406247db26d9e1 (diff) | |
lsp: refine ';;text;' detection to require non-empty, non-space content and closing ';'; ensure bare ';;' and ';;;' do not auto-trigger; add tests
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/lsp/completion_prefix_strip_test.go | 7 | ||||
| -rw-r--r-- | internal/lsp/handlers.go | 69 |
2 files changed, 49 insertions, 27 deletions
diff --git a/internal/lsp/completion_prefix_strip_test.go b/internal/lsp/completion_prefix_strip_test.go index 84f0568..43eb608 100644 --- a/internal/lsp/completion_prefix_strip_test.go +++ b/internal/lsp/completion_prefix_strip_test.go @@ -73,3 +73,10 @@ func TestTryLLMCompletion_DoubleSemicolonEmpty_DoesNotAutoTrigger(t *testing.T) if len(items) != 0 { t.Fatalf("expected no items when inline ';;' is empty") } if fake.calls != 0 { t.Fatalf("LLM should not be called; calls=%d", fake.calls) } } + +func TestHasDoubleSemicolonTrigger_Variants(t *testing.T) { + if hasDoubleSemicolonTrigger(";;") { t.Fatalf("bare ';;' should not trigger") } + if hasDoubleSemicolonTrigger(";; ;") { t.Fatalf("';;' followed by space should not trigger") } + if hasDoubleSemicolonTrigger(";;;") { t.Fatalf("';;;' should not trigger (no content)") } + if !hasDoubleSemicolonTrigger(";;x;") { t.Fatalf("expected trigger for ';;x;' pattern") } +} diff --git a/internal/lsp/handlers.go b/internal/lsp/handlers.go index 9d58a44..f630eb5 100644 --- a/internal/lsp/handlers.go +++ b/internal/lsp/handlers.go @@ -781,16 +781,21 @@ func (s *Server) tryLLMCompletion(p CompletionParams, above, current, below, fun logging.Logf("lsp ", "%scompletion skip=busy another LLM request in flight%s", logging.AnsiYellow, logging.AnsiBase) return []CompletionItem{}, false, true } - sysPrompt, userPrompt := buildPrompts(inParams, p, above, current, below, funcCtx) - messages := []llm.Message{ - {Role: "system", Content: sysPrompt}, - {Role: "user", Content: userPrompt}, - } + sysPrompt, userPrompt := buildPrompts(inParams, p, above, current, below, funcCtx) + messages := []llm.Message{ + {Role: "system", Content: sysPrompt}, + {Role: "user", Content: userPrompt}, + } if hasExtra && extraText != "" { messages = append(messages, llm.Message{Role: "user", Content: "Additional context:\n" + extraText}) } - // Compute total sent context size (sum of message contents) + // If an inline prompt marker is present, make the instruction stricter: code only. + if inlinePrompt { + messages[0].Content = "You are a precise code completion/refactoring engine. Output only the code to insert with no prose, no comments, and no backticks. Return raw code only." + } + + // Compute total sent context size (sum of message contents) var sentSize int for _, m := range messages { sentSize += len(m.Content) @@ -1057,27 +1062,37 @@ func promptRemovalEditsForLine(line string, lineNum int) []TextEdit { } func hasDoubleSemicolonTrigger(line string) bool { - pos := 0 - for pos < len(line) { - j := strings.Index(line[pos:], ";;") - if j < 0 { - return false - } - j += pos - if j+2 < len(line) && line[j+2] != ' ' { - if k := strings.Index(line[j+2:], ";"); k >= 0 { - closeIdx := j + 2 + k - if closeIdx-1 >= 0 && line[closeIdx-1] != ' ' { - return true - } - pos = closeIdx + 1 - continue - } - return false - } - pos = j + 2 - } - return false + pos := 0 + for pos < len(line) { + j := strings.Index(line[pos:], ";;") + if j < 0 { + return false + } + j += pos + contentStart := j + 2 + if contentStart >= len(line) { + return false // nothing after ';;' + } + // First content char cannot be space or another ';' + first := line[contentStart] + if first == ' ' || first == ';' { + pos = contentStart + 1 + continue + } + // Require at least one content char before a closing ';' + k := strings.Index(line[contentStart+1:], ";") + if k < 0 { + return false + } + closeIdx := contentStart + 1 + k + // Disallow trailing space before closing ';' + if closeIdx-1 >= 0 && line[closeIdx-1] == ' ' { + pos = closeIdx + 1 + continue + } + return true + } + return false } func collectSemicolonMarkers(line string, lineNum int) []TextEdit { |
