summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-02 13:47:07 +0200
committerPaul Buetow <paul@buetow.org>2026-03-02 13:47:07 +0200
commita065de381f0343b580db47509a8c0f46384c7ae8 (patch)
treeb36ded44ce20058833f4a73a8e9cf9a6c74a565b /internal
parent393af17e8f274537a8fa6c302e0bcab21d191e7b (diff)
lsp: simplify ServerOptions to config-first model (task 410)
Diffstat (limited to 'internal')
-rw-r--r--internal/hexailsp/run.go63
-rw-r--r--internal/hexailsp/run_more_test.go14
-rw-r--r--internal/hexailsp/run_test.go26
-rw-r--r--internal/lsp/server.go87
-rw-r--r--internal/lsp/triggers_config_test.go10
5 files changed, 39 insertions, 161 deletions
diff --git a/internal/hexailsp/run.go b/internal/hexailsp/run.go
index e357cbb..3c86414 100644
--- a/internal/hexailsp/run.go
+++ b/internal/hexailsp/run.go
@@ -137,63 +137,12 @@ func ensureFactory(factory ServerFactory) ServerFactory {
}
func makeServerOptions(cfg appconfig.App, logContext bool, client llm.Client, loadOpts appconfig.LoadOptions, ignoreChecker *ignore.Checker) lsp.ServerOptions {
- // Map custom actions from appconfig to lsp type
- var customs []lsp.CustomAction
- if len(cfg.CustomActions) > 0 {
- customs = make([]lsp.CustomAction, 0, len(cfg.CustomActions))
- for _, ca := range cfg.CustomActions {
- customs = append(customs, lsp.CustomAction{
- ID: ca.ID,
- Title: ca.Title,
- Kind: ca.Kind,
- Scope: ca.Scope,
- Instruction: ca.Instruction,
- System: ca.System,
- User: ca.User,
- })
- }
- }
return lsp.ServerOptions{
- ConfigLoadOptions: loadOpts,
- LogContext: logContext,
- ConfigStore: nil,
- Config: &cfg,
- MaxTokens: cfg.MaxTokens,
- ContextMode: cfg.ContextMode,
- WindowLines: cfg.ContextWindowLines,
- MaxContextTokens: cfg.MaxContextTokens,
- CodingTemperature: cfg.CodingTemperature,
- Client: client,
- TriggerCharacters: cfg.TriggerCharacters,
- ManualInvokeMinPrefix: cfg.ManualInvokeMinPrefix,
- CompletionDebounceMs: cfg.CompletionDebounceMs,
- CompletionThrottleMs: cfg.CompletionThrottleMs,
- CompletionWaitAll: cfg.CompletionWaitAll,
- InlineOpen: cfg.InlineOpen,
- InlineClose: cfg.InlineClose,
- ChatSuffix: cfg.ChatSuffix,
- ChatPrefixes: cfg.ChatPrefixes,
-
- // Prompts
- PromptCompSysGeneral: cfg.PromptCompletionSystemGeneral,
- PromptCompSysParams: cfg.PromptCompletionSystemParams,
- PromptCompSysInline: cfg.PromptCompletionSystemInline,
- PromptCompUserGeneral: cfg.PromptCompletionUserGeneral,
- PromptCompUserParams: cfg.PromptCompletionUserParams,
- PromptCompExtraHeader: cfg.PromptCompletionExtraHeader,
- PromptNativeCompletion: cfg.PromptNativeCompletion,
- PromptChatSystem: cfg.PromptChatSystem,
- PromptRewriteSystem: cfg.PromptCodeActionRewriteSystem,
- PromptDiagnosticsSystem: cfg.PromptCodeActionDiagnosticsSystem,
- PromptDocumentSystem: cfg.PromptCodeActionDocumentSystem,
- PromptRewriteUser: cfg.PromptCodeActionRewriteUser,
- PromptDiagnosticsUser: cfg.PromptCodeActionDiagnosticsUser,
- PromptDocumentUser: cfg.PromptCodeActionDocumentUser,
- PromptGoTestSystem: cfg.PromptCodeActionGoTestSystem,
- PromptGoTestUser: cfg.PromptCodeActionGoTestUser,
- PromptSimplifySystem: cfg.PromptCodeActionSimplifySystem,
- PromptSimplifyUser: cfg.PromptCodeActionSimplifyUser,
- CustomActions: customs,
- IgnoreChecker: ignoreChecker,
+ ConfigLoadOptions: loadOpts,
+ LogContext: logContext,
+ ConfigStore: nil,
+ Config: &cfg,
+ Client: client,
+ IgnoreChecker: ignoreChecker,
}
}
diff --git a/internal/hexailsp/run_more_test.go b/internal/hexailsp/run_more_test.go
index 338dd48..7017811 100644
--- a/internal/hexailsp/run_more_test.go
+++ b/internal/hexailsp/run_more_test.go
@@ -47,10 +47,13 @@ func TestRunWithFactory_BuildsOptionsAndClient(t *testing.T) {
if err := RunWithFactory("", "", &in, &out, logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
- if captured.MaxTokens != 123 {
+ if captured.Config == nil {
+ t.Fatalf("expected Config to be set in ServerOptions")
+ }
+ if captured.Config.MaxTokens != 123 {
t.Fatalf("opts not applied: %+v", captured)
}
- if captured.PromptRewriteSystem != "RSYS" || captured.PromptRewriteUser != "RUSER" {
+ if captured.Config.PromptCodeActionRewriteSystem != "RSYS" || captured.Config.PromptCodeActionRewriteUser != "RUSER" {
t.Fatalf("prompts not mapped: %+v", captured)
}
if captured.Client == nil {
@@ -88,10 +91,13 @@ func TestRunWithFactory_SubscriptionAppliesUpdates(t *testing.T) {
t.Fatalf("expected ApplyOptions to be invoked on config update, got %d calls", len(runner.opts))
}
latest := runner.opts[len(runner.opts)-1]
- if latest.MaxTokens != updated.MaxTokens {
+ if latest.Config == nil {
+ t.Fatalf("expected Config on latest options")
+ }
+ if latest.Config.MaxTokens != updated.MaxTokens {
t.Fatalf("expected updated max tokens, got %+v", latest)
}
- if latest.ContextMode != "always-full" {
+ if latest.Config.ContextMode != "always-full" {
t.Fatalf("expected normalized context mode, got %+v", latest)
}
}
diff --git a/internal/hexailsp/run_test.go b/internal/hexailsp/run_test.go
index 12b56c0..743f064 100644
--- a/internal/hexailsp/run_test.go
+++ b/internal/hexailsp/run_test.go
@@ -39,17 +39,20 @@ func TestRunWithFactory_UsesDefaultsAndCallsServer(t *testing.T) {
if err := RunWithFactory("", "", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
- if gotOpts.MaxTokens != cfg.MaxTokens {
- t.Fatalf("MaxTokens want %d got %d", cfg.MaxTokens, gotOpts.MaxTokens)
+ if gotOpts.Config == nil {
+ t.Fatalf("expected Config to be set in ServerOptions")
}
- if gotOpts.ContextMode != cfg.ContextMode {
- t.Fatalf("ContextMode want %q got %q", cfg.ContextMode, gotOpts.ContextMode)
+ if gotOpts.Config.MaxTokens != cfg.MaxTokens {
+ t.Fatalf("MaxTokens want %d got %d", cfg.MaxTokens, gotOpts.Config.MaxTokens)
}
- if gotOpts.WindowLines != cfg.ContextWindowLines {
- t.Fatalf("WindowLines want %d got %d", cfg.ContextWindowLines, gotOpts.WindowLines)
+ if gotOpts.Config.ContextMode != cfg.ContextMode {
+ t.Fatalf("ContextMode want %q got %q", cfg.ContextMode, gotOpts.Config.ContextMode)
}
- if gotOpts.MaxContextTokens != cfg.MaxContextTokens {
- t.Fatalf("MaxContextTokens want %d got %d", cfg.MaxContextTokens, gotOpts.MaxContextTokens)
+ if gotOpts.Config.ContextWindowLines != cfg.ContextWindowLines {
+ t.Fatalf("ContextWindowLines want %d got %d", cfg.ContextWindowLines, gotOpts.Config.ContextWindowLines)
+ }
+ if gotOpts.Config.MaxContextTokens != cfg.MaxContextTokens {
+ t.Fatalf("MaxContextTokens want %d got %d", cfg.MaxContextTokens, gotOpts.Config.MaxContextTokens)
}
if gotOpts.Client != nil { // with no env, openai client fails to build
@@ -107,8 +110,11 @@ func TestRunWithFactory_NormalizesContextMode_AndSetsPreviewLimit(t *testing.T)
if err := RunWithFactory("", "", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
- if gotOpts.ContextMode != "file-on-new-func" {
- t.Fatalf("ContextMode not normalized: %q", gotOpts.ContextMode)
+ if gotOpts.Config == nil {
+ t.Fatalf("expected Config to be set in ServerOptions")
+ }
+ if gotOpts.Config.ContextMode != "file-on-new-func" {
+ t.Fatalf("ContextMode not normalized: %q", gotOpts.Config.ContextMode)
}
if logging.PreviewForLog("abcdef") != "abc…" {
t.Fatalf("PreviewForLog not respecting limit: %q", logging.PreviewForLog("abcdef"))
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 385f5ce..dc76975 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -63,49 +63,9 @@ type ServerOptions struct {
LogContext bool
ConfigStore *runtimeconfig.Store
Config *appconfig.App
- MaxTokens int
- ContextMode string
- WindowLines int
- MaxContextTokens int
ConfigLoadOptions appconfig.LoadOptions
- Client llm.Client
- TriggerCharacters []string
- CodingTemperature *float64
- ManualInvokeMinPrefix int
- CompletionDebounceMs int
- CompletionThrottleMs int
- CompletionWaitAll *bool
-
- // Inline/chat triggers
- InlineOpen string
- InlineClose string
- ChatSuffix string
- ChatPrefixes []string
-
- // Prompt templates
- PromptCompSysGeneral string
- PromptCompSysParams string
- PromptCompSysInline string
- PromptCompUserGeneral string
- PromptCompUserParams string
- PromptCompExtraHeader string
- PromptNativeCompletion string
- PromptChatSystem string
- PromptRewriteSystem string
- PromptDiagnosticsSystem string
- PromptDocumentSystem string
- PromptRewriteUser string
- PromptDiagnosticsUser string
- PromptDocumentUser string
- PromptGoTestSystem string
- PromptGoTestUser string
- PromptSimplifySystem string
- PromptSimplifyUser string
-
- // Custom actions
- CustomActions []CustomAction
-
+ Client llm.Client
// Gitignore-aware file checker (optional)
IgnoreChecker *ignore.Checker
}
@@ -158,51 +118,6 @@ func (s *Server) applyOptions(opts ServerOptions) {
s.cfg = opts.ConfigStore.Snapshot()
} else {
s.cfg = appconfig.App{}
- // populate from legacy ServerOptions fields
- s.cfg.MaxTokens = opts.MaxTokens
- s.cfg.ContextMode = opts.ContextMode
- s.cfg.ContextWindowLines = opts.WindowLines
- s.cfg.MaxContextTokens = opts.MaxContextTokens
- s.cfg.TriggerCharacters = append([]string{}, opts.TriggerCharacters...)
- s.cfg.CodingTemperature = opts.CodingTemperature
- s.cfg.ManualInvokeMinPrefix = opts.ManualInvokeMinPrefix
- s.cfg.CompletionDebounceMs = opts.CompletionDebounceMs
- s.cfg.CompletionThrottleMs = opts.CompletionThrottleMs
- s.cfg.CompletionWaitAll = opts.CompletionWaitAll
- s.cfg.InlineOpen = opts.InlineOpen
- s.cfg.InlineClose = opts.InlineClose
- s.cfg.ChatSuffix = opts.ChatSuffix
- s.cfg.ChatPrefixes = append([]string{}, opts.ChatPrefixes...)
- s.cfg.PromptCompletionSystemGeneral = opts.PromptCompSysGeneral
- s.cfg.PromptCompletionSystemParams = opts.PromptCompSysParams
- s.cfg.PromptCompletionSystemInline = opts.PromptCompSysInline
- s.cfg.PromptCompletionUserGeneral = opts.PromptCompUserGeneral
- s.cfg.PromptCompletionUserParams = opts.PromptCompUserParams
- s.cfg.PromptCompletionExtraHeader = opts.PromptCompExtraHeader
- s.cfg.PromptNativeCompletion = opts.PromptNativeCompletion
- s.cfg.PromptChatSystem = opts.PromptChatSystem
- s.cfg.PromptCodeActionRewriteSystem = opts.PromptRewriteSystem
- s.cfg.PromptCodeActionDiagnosticsSystem = opts.PromptDiagnosticsSystem
- s.cfg.PromptCodeActionDocumentSystem = opts.PromptDocumentSystem
- s.cfg.PromptCodeActionRewriteUser = opts.PromptRewriteUser
- s.cfg.PromptCodeActionDiagnosticsUser = opts.PromptDiagnosticsUser
- s.cfg.PromptCodeActionDocumentUser = opts.PromptDocumentUser
- s.cfg.PromptCodeActionGoTestSystem = opts.PromptGoTestSystem
- s.cfg.PromptCodeActionGoTestUser = opts.PromptGoTestUser
- s.cfg.PromptCodeActionSimplifySystem = opts.PromptSimplifySystem
- s.cfg.PromptCodeActionSimplifyUser = opts.PromptSimplifyUser
- s.cfg.CustomActions = make([]appconfig.CustomAction, len(opts.CustomActions))
- for i, ca := range opts.CustomActions {
- s.cfg.CustomActions[i] = appconfig.CustomAction{
- ID: ca.ID,
- Title: ca.Title,
- Kind: ca.Kind,
- Scope: ca.Scope,
- Instruction: ca.Instruction,
- System: ca.System,
- User: ca.User,
- }
- }
}
s.llmClient = opts.Client
if opts.Client != nil {
diff --git a/internal/lsp/triggers_config_test.go b/internal/lsp/triggers_config_test.go
index 9ab2752..193e117 100644
--- a/internal/lsp/triggers_config_test.go
+++ b/internal/lsp/triggers_config_test.go
@@ -7,6 +7,8 @@ import (
"log"
"testing"
"time"
+
+ "codeberg.org/snonux/hexai/internal/appconfig"
)
func TestShouldSuppressForChatTriggerEOL_CustomConfig(t *testing.T) {
@@ -28,9 +30,8 @@ func TestShouldSuppressForChatTriggerEOL_CustomConfig(t *testing.T) {
func TestNewServer_AssignsTriggerGlobals_AndParsingUsesThem(t *testing.T) {
var out bytes.Buffer
- s := NewServer(bytes.NewReader(nil), &out, log.New(io.Discard, "", 0), ServerOptions{
- InlineOpen: "<", InlineClose: ">", ChatSuffix: ")", ChatPrefixes: []string{":"},
- })
+ cfg := appconfig.App{InlineOpen: "<", InlineClose: ">", ChatSuffix: ")", ChatPrefixes: []string{":"}}
+ s := NewServer(bytes.NewReader(nil), &out, log.New(io.Discard, "", 0), ServerOptions{Config: &cfg})
openStr, _, openChar, closeChar := s.inlineMarkers()
if openChar != '<' || closeChar != '>' {
t.Fatalf("inline markers not applied: %q %q", string(openChar), string(closeChar))
@@ -67,7 +68,8 @@ func TestIsTriggerEvent_BareDoubleOpenBlocksEvenWithContextTriggerChar(t *testin
func TestDetectAndHandleChat_CustomConfig_InsertsReply(t *testing.T) {
var out bytes.Buffer
- s := NewServer(bytes.NewReader(nil), &out, log.New(io.Discard, "", 0), ServerOptions{ChatSuffix: "#", ChatPrefixes: []string{")"}})
+ cfg := appconfig.App{ChatSuffix: "#", ChatPrefixes: []string{")"}}
+ s := NewServer(bytes.NewReader(nil), &out, log.New(io.Discard, "", 0), ServerOptions{Config: &cfg})
s.llmClient = fakeLLM{resp: "Hello\nmulti-line reply"}
uri := "file:///chat2.go"
s.setDocument(uri, "ok)#\n\n")