summaryrefslogtreecommitdiff
path: root/internal/lsp/handlers_completion.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/lsp/handlers_completion.go')
-rw-r--r--internal/lsp/handlers_completion.go78
1 files changed, 42 insertions, 36 deletions
diff --git a/internal/lsp/handlers_completion.go b/internal/lsp/handlers_completion.go
index df541cc..f7f41ef 100644
--- a/internal/lsp/handlers_completion.go
+++ b/internal/lsp/handlers_completion.go
@@ -113,7 +113,8 @@ func (s *Server) prepareCompletionPlan(p CompletionParams, above, current, below
hasExtra: hasExtra,
extraText: extraText,
}
- plan.inlinePrompt = lineHasInlinePrompt(current, s.inlineOpenChar, s.inlineCloseChar)
+ _, _, openChar, closeChar := s.inlineMarkers()
+ plan.inlinePrompt = lineHasInlinePrompt(current, openChar, closeChar)
if !plan.inlinePrompt && !s.isTriggerEvent(p, current) {
logging.Logf("lsp ", "%scompletion skip=no-trigger line=%d char=%d current=%q%s", logging.AnsiYellow, p.Position.Line, p.Position.Character, trimLen(current), logging.AnsiBase)
return plan, []CompletionItem{}, true
@@ -130,7 +131,7 @@ func (s *Server) prepareCompletionPlan(p CompletionParams, above, current, below
logging.AnsiGreen, logging.PreviewForLog(cleaned), logging.AnsiBase)
return plan, s.makeCompletionItems(cleaned, plan.inParams, current, p, docStr), true
}
- if isBareDoubleOpen(current, s.inlineOpenChar, s.inlineCloseChar) || isBareDoubleOpen(below, s.inlineOpenChar, s.inlineCloseChar) {
+ if isBareDoubleOpen(current, openChar, closeChar) || isBareDoubleOpen(below, openChar, closeChar) {
logging.Logf("lsp ", "%scompletion skip=empty-double-semicolon line=%d char=%d current=%q%s", logging.AnsiYellow, p.Position.Line, p.Position.Character, trimLen(current), logging.AnsiBase)
return plan, []CompletionItem{}, true
}
@@ -148,19 +149,17 @@ func (s *Server) executeChatCompletion(ctx context.Context, plan completionPlan)
sentSize += len(m.Content)
}
s.incSentCounters(sentSize)
- opts := []llm.RequestOption{llm.WithMaxTokens(s.maxTokens)}
- if s.codingTemperature != nil {
- opts = append(opts, llm.WithTemperature(*s.codingTemperature))
- }
+ opts := s.llmRequestOpts()
s.waitForDebounce(ctx)
if !s.waitForThrottle(ctx) {
return nil, false
}
- if s.llmClient == nil {
+ client := s.currentLLMClient()
+ if client == nil {
return nil, false
}
- logging.Logf("lsp ", "completion llm=requesting model=%s", s.llmClient.DefaultModel())
- text, err := s.llmClient.Chat(ctx, messages, opts...)
+ logging.Logf("lsp ", "completion llm=requesting model=%s", client.DefaultModel())
+ text, err := client.Chat(ctx, messages, opts...)
if err != nil {
logging.Logf("lsp ", "llm completion error: %v", err)
s.logLLMStats()
@@ -198,15 +197,16 @@ func parseManualInvoke(ctx any) bool {
// shouldSuppressForChatTriggerEOL returns true when a chat trigger like ">" follows ?, !, :, or ; at EOL.
func (s *Server) shouldSuppressForChatTriggerEOL(current string, p CompletionParams) bool {
t := strings.TrimRight(current, " \t")
- if s.chatSuffix == "" {
+ suffix, prefixes, _ := s.chatConfig()
+ if suffix == "" {
return false
}
- if strings.HasSuffix(t, s.chatSuffix) {
- if len(t) < len(s.chatSuffix)+1 {
+ if strings.HasSuffix(t, suffix) {
+ if len(t) < len(suffix)+1 {
return false
}
- prev := string(t[len(t)-len(s.chatSuffix)-1])
- for _, pf := range s.chatPrefixes {
+ prev := string(t[len(t)-len(suffix)-1])
+ for _, pf := range prefixes {
if prev == pf {
logging.Logf("lsp ", "completion skip=chat-trigger-eol uri=%s line=%d", p.TextDocument.URI, p.Position.Line)
return true
@@ -246,33 +246,38 @@ func (s *Server) prefixHeuristicAllows(inlinePrompt bool, current string, p Comp
}
start := computeWordStart(current, j)
min := 1
- if manualInvoke && s.manualInvokeMinPrefix >= 0 {
- min = s.manualInvokeMinPrefix
+ if manualInvoke {
+ if v := s.manualInvokeMinPrefix(); v >= 0 {
+ min = v
+ }
}
return j-start >= min
}
// tryProviderNativeCompletion attempts provider-native completion and returns items when successful.
func (s *Server) tryProviderNativeCompletion(current string, p CompletionParams, above, below, funcCtx, docStr string, hasExtra bool, extraText string, inParams bool) ([]CompletionItem, bool) {
- cc, ok := s.llmClient.(llm.CodeCompleter)
+ client := s.currentLLMClient()
+ cc, ok := client.(llm.CodeCompleter)
if !ok {
return nil, false
}
before, after := s.docBeforeAfter(p.TextDocument.URI, p.Position)
path := strings.TrimPrefix(p.TextDocument.URI, "file://")
// Build provider-native prompt from template
- prompt := renderTemplate(s.promptNativeCompletion, map[string]string{
+ cfg := s.currentConfig()
+ _, _, openChar, closeChar := s.inlineMarkers()
+ prompt := renderTemplate(cfg.PromptNativeCompletion, map[string]string{
"path": path,
"before": before,
})
lang := ""
temp := 0.0
- if s.codingTemperature != nil {
- temp = *s.codingTemperature
+ if cfg.CodingTemperature != nil {
+ temp = *cfg.CodingTemperature
}
prov := ""
- if s.llmClient != nil {
- prov = s.llmClient.Name()
+ if client != nil {
+ prov = client.Name()
}
logging.Logf("lsp ", "completion path=codex provider=%s uri=%s", prov, path)
ctx2, cancel2 := context.WithTimeout(context.Background(), 15*time.Second)
@@ -291,8 +296,8 @@ func (s *Server) tryProviderNativeCompletion(current string, p CompletionParams,
s.incSentCounters(sentBytes)
s.incRecvCounters(len(suggestions[0]))
// Contribute to global stats (provider-native path)
- if s.llmClient != nil {
- _ = stats.Update(ctx2, s.llmClient.Name(), s.llmClient.DefaultModel(), sentBytes, len(suggestions[0]))
+ if client != nil {
+ _ = stats.Update(ctx2, client.Name(), client.DefaultModel(), sentBytes, len(suggestions[0]))
}
s.logLLMStats()
cleaned := strings.TrimSpace(suggestions[0])
@@ -301,7 +306,7 @@ func (s *Server) tryProviderNativeCompletion(current string, p CompletionParams,
if cleaned != "" {
cleaned = stripDuplicateGeneralPrefix(current[:p.Position.Character], cleaned)
}
- if cleaned != "" && hasDoubleOpenTrigger(current, s.inlineOpenChar, s.inlineCloseChar) {
+ if cleaned != "" && hasDoubleOpenTrigger(current, openChar, closeChar) {
indent := leadingIndent(current)
if indent != "" {
cleaned = applyIndent(indent, cleaned)
@@ -325,7 +330,7 @@ func (s *Server) tryProviderNativeCompletion(current string, p CompletionParams,
// waitForDebounce sleeps until there has been no input activity for at least
// completionDebounce. If debounce is zero or ctx is done, it returns promptly.
func (s *Server) waitForDebounce(ctx context.Context) {
- d := s.completionDebounce
+ d := s.completionDebounce()
if d <= 0 {
return
}
@@ -355,7 +360,7 @@ func (s *Server) waitForDebounce(ctx context.Context) {
// waitForThrottle enforces a minimum spacing between LLM calls. Returns false
// if the context is canceled while waiting.
func (s *Server) waitForThrottle(ctx context.Context) bool {
- interval := s.throttleInterval
+ interval := s.completionThrottle()
if interval <= 0 {
return true
}
@@ -386,7 +391,6 @@ func (s *Server) waitForThrottle(ctx context.Context) bool {
// buildCompletionMessages constructs the LLM messages for completion.
func (s *Server) buildCompletionMessages(inlinePrompt, hasExtra bool, extraText string, inParams bool, p CompletionParams, above, current, below, funcCtx string) []llm.Message {
- // Vars for templates
vars := map[string]string{
"file": p.TextDocument.URI,
"function": funcCtx,
@@ -395,19 +399,20 @@ func (s *Server) buildCompletionMessages(inlinePrompt, hasExtra bool, extraText
"below": below,
"char": fmt.Sprintf("%d", p.Position.Character),
}
- sys := s.promptCompSysGeneral
- userTpl := s.promptCompUserGeneral
+ cfg := s.currentConfig()
+ sys := cfg.PromptCompletionSystemGeneral
+ userTpl := cfg.PromptCompletionUserGeneral
if inParams {
- sys = s.promptCompSysParams
- userTpl = s.promptCompUserParams
+ sys = cfg.PromptCompletionSystemParams
+ userTpl = cfg.PromptCompletionUserParams
}
- if inlinePrompt && strings.TrimSpace(s.promptCompSysInline) != "" {
- sys = s.promptCompSysInline
+ if inlinePrompt && strings.TrimSpace(cfg.PromptCompletionSystemInline) != "" {
+ sys = cfg.PromptCompletionSystemInline
}
user := renderTemplate(userTpl, vars)
messages := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: user}}
if hasExtra && strings.TrimSpace(extraText) != "" {
- extra := renderTemplate(s.promptCompExtraHeader, map[string]string{"context": extraText})
+ extra := renderTemplate(cfg.PromptCompletionExtraHeader, map[string]string{"context": extraText})
if strings.TrimSpace(extra) == "" {
extra = extraText
}
@@ -430,7 +435,8 @@ func (s *Server) postProcessCompletion(text string, leftOfCursor string, current
if cleaned != "" {
cleaned = stripDuplicateGeneralPrefix(leftOfCursor, cleaned)
}
- if cleaned != "" && hasDoubleOpenTrigger(currentLine, s.inlineOpenChar, s.inlineCloseChar) {
+ _, _, openChar, closeChar := s.inlineMarkers()
+ if cleaned != "" && hasDoubleOpenTrigger(currentLine, openChar, closeChar) {
if indent := leadingIndent(currentLine); indent != "" {
cleaned = applyIndent(indent, cleaned)
}