diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-23 08:33:07 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-23 08:33:07 +0200 |
| commit | 56c2c4f64ba1712f7cab28a8dc92a3c14b20eb1d (patch) | |
| tree | 4088f6ceddbfdea3877cd7c7343397a8b0e78d36 /internal/lsp/handlers_completion.go | |
| parent | 3ea11bc5d671d962d01b57fa0fba0bda611025fe (diff) | |
refactor: split oversized functions, fix double logging, add %w wrapping
- lsp/handlers_completion.go: extract buildNativeCompletionCacheKey and
postProcessNativeCompletion; track collectFirstCompletion in inflight;
remove redundant logLLMStats("") on error path
- lsp/handlers.go: extract checkTriggerFromContext and
checkTriggerFromCursorChar; isTriggerEvent reduced from 63→10 lines
- lsp/transport.go: use %w for error wrapping in Content-Length parse
- llm/ollama.go: extract parseOllamaStream; ChatStream reduced to ~35 lines
- appconfig/config_load.go: extract decodeModelEntryFromMap; rename 'any'
to 'found'; decodeModelEntry reduced to ~18 lines
- llm/provider.go: document why providerRegistry is package-level
- integrationtests/ask_test.go: add //go:build integration; move repoRoot
init from init() to TestMain with diagnostic message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/lsp/handlers_completion.go')
| -rw-r--r-- | internal/lsp/handlers_completion.go | 67 |
1 files changed, 41 insertions, 26 deletions
diff --git a/internal/lsp/handlers_completion.go b/internal/lsp/handlers_completion.go index 527d020..d6529de 100644 --- a/internal/lsp/handlers_completion.go +++ b/internal/lsp/handlers_completion.go @@ -321,7 +321,6 @@ func (s *Server) executeChatCompletion(ctx context.Context, plan completionPlan, text, err := client.Chat(ctx, messages, spec.options...) if err != nil { logging.Logf("lsp ", "llm completion error: %v", err) - s.logLLMStats("") return nil, false } s.incRecvCounters(len(text)) @@ -426,6 +425,45 @@ func (s *Server) prefixHeuristicAllows(inlinePrompt bool, current string, p Comp return j-start >= min } +// buildNativeCompletionCacheKey constructs the per-provider cache key for native completions. +func buildNativeCompletionCacheKey(planCacheKey, provider, modelUsed string, clientName string) string { + providerKey := provider + if providerKey == "" { + providerKey = llmutils.CanonicalProvider(clientName) + } + return planCacheKey + "|" + providerKey + ":" + modelUsed +} + +// postProcessNativeCompletion strips duplicates and applies indentation to the raw suggestion. +// Returns the cleaned text, or an empty string when the suggestion should be discarded. +func (s *Server) postProcessNativeCompletion(raw, current string, charOffset int) string { + cleaned := strings.TrimSpace(raw) + if cleaned == "" { + return "" + } + openStr, _, openChar, closeChar := s.inlineMarkers() + cByte := utf16OffsetToByteOffset(current, charOffset) + leftOfCursor := current[:cByte] + cleaned = stripDuplicateAssignmentPrefix(leftOfCursor, cleaned) + if cleaned == "" { + return "" + } + cleaned = stripDuplicateGeneralPrefix(leftOfCursor, cleaned) + if cleaned == "" { + return "" + } + if strings.TrimSpace(cleaned) != "" && hasDoubleOpenTrigger(current, openStr, openChar, closeChar) { + if indent := leadingIndent(current); indent != "" { + cleaned = applyIndent(indent, cleaned) + } + } + // Guard against all-whitespace result without stripping intentional indentation. + if strings.TrimSpace(cleaned) == "" { + return "" + } + return cleaned +} + // tryProviderNativeCompletion attempts provider-native completion and returns items when successful. func (s *Server) tryProviderNativeCompletion(ctx context.Context, plan completionPlan, spec requestSpec, client llm.Client, sortPrefix string) ([]CompletionItem, bool) { cc, ok := client.(llm.CodeCompleter) @@ -437,7 +475,6 @@ func (s *Server) tryProviderNativeCompletion(ctx context.Context, plan completio before, after := s.docBeforeAfter(p.TextDocument.URI, p.Position) path := strings.TrimPrefix(p.TextDocument.URI, "file://") cfg := s.currentConfig() - openStr, _, openChar, closeChar := s.inlineMarkers() prompt := renderTemplate(cfg.PromptNativeCompletion, map[string]string{ "path": path, "before": before, @@ -466,34 +503,12 @@ func (s *Server) tryProviderNativeCompletion(ctx context.Context, plan completio s.incRecvCounters(len(suggestions[0])) _ = stats.Update(ctx2, client.Name(), modelUsed, sentBytes, len(suggestions[0])) s.logLLMStats(modelUsed) - cleaned := strings.TrimSpace(suggestions[0]) - if cleaned == "" { - return nil, false - } - cByte := utf16OffsetToByteOffset(current, p.Position.Character) - cleaned = stripDuplicateAssignmentPrefix(current[:cByte], cleaned) + cleaned := s.postProcessNativeCompletion(suggestions[0], current, p.Position.Character) if cleaned == "" { return nil, false } - cleaned = stripDuplicateGeneralPrefix(current[:cByte], cleaned) - if cleaned == "" { - return nil, false - } - if strings.TrimSpace(cleaned) != "" && hasDoubleOpenTrigger(current, openStr, openChar, closeChar) { - indent := leadingIndent(current) - if indent != "" { - cleaned = applyIndent(indent, cleaned) - } - } - if strings.TrimSpace(cleaned) == "" { - return nil, false - } detail := fmt.Sprintf("Hexai %s:%s", client.Name(), modelUsed) - providerKey := provider - if providerKey == "" { - providerKey = llmutils.CanonicalProvider(client.Name()) - } - cacheKey := plan.cacheKey + "|" + providerKey + ":" + modelUsed + cacheKey := buildNativeCompletionCacheKey(plan.cacheKey, provider, modelUsed, client.Name()) s.completionCachePut(cacheKey, cleaned) items := s.makeCompletionItems(cleaned, plan.inParams, current, p, plan.docStr, detail, sortPrefix) return items, true |
