summaryrefslogtreecommitdiff
path: root/internal/hexailsp
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-28 17:30:44 +0300
committerPaul Buetow <paul@buetow.org>2025-09-28 17:30:44 +0300
commit0761409497041c752086b9aded08cf9e32e30fd2 (patch)
treee62721bc119d4ae435d2609292faea06a68244a4 /internal/hexailsp
parent0ac2d186e84f77d73d924e2c0ce975a17c3a8078 (diff)
Add --config flag support across CLI, LSP, and tmux tools
Diffstat (limited to 'internal/hexailsp')
-rw-r--r--internal/hexailsp/run.go15
-rw-r--r--internal/hexailsp/run_more_test.go4
-rw-r--r--internal/hexailsp/run_test.go10
3 files changed, 19 insertions, 10 deletions
diff --git a/internal/hexailsp/run.go b/internal/hexailsp/run.go
index ffb9f86..750e544 100644
--- a/internal/hexailsp/run.go
+++ b/internal/hexailsp/run.go
@@ -25,7 +25,12 @@ type ServerFactory func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.S
// Run configures logging, loads config, builds the LLM client and runs the LSP server.
// It is thin and delegates to RunWithFactory for testability.
+
func Run(logPath string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ return RunWithConfig(logPath, "", stdin, stdout, stderr)
+}
+
+func RunWithConfig(logPath string, configPath string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
logger := log.New(stderr, "hexai-lsp ", log.LstdFlags|log.Lmsgprefix)
if strings.TrimSpace(logPath) != "" {
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
@@ -36,19 +41,20 @@ func Run(logPath string, stdin io.Reader, stdout io.Writer, stderr io.Writer) er
logger.SetOutput(f)
}
logging.Bind(logger)
- cfg := appconfig.Load(logger)
+ loadOpts := appconfig.LoadOptions{ConfigPath: configPath}
+ cfg := appconfig.LoadWithOptions(logger, loadOpts)
if err := cfg.Validate(); err != nil {
logger.Fatalf("invalid config: %v", err)
}
if cfg.StatsWindowMinutes > 0 {
stats.SetWindow(time.Duration(cfg.StatsWindowMinutes) * time.Minute)
}
- return RunWithFactory(logPath, stdin, stdout, logger, cfg, nil, nil)
+ return RunWithFactory(logPath, configPath, stdin, stdout, logger, cfg, nil, nil)
}
// RunWithFactory is the testable entrypoint. When client is nil, it is built from cfg+env.
// When factory is nil, lsp.NewServer is used.
-func RunWithFactory(logPath string, stdin io.Reader, stdout io.Writer, logger *log.Logger, cfg appconfig.App, client llm.Client, factory ServerFactory) error {
+func RunWithFactory(logPath string, configPath string, stdin io.Reader, stdout io.Writer, logger *log.Logger, cfg appconfig.App, client llm.Client, factory ServerFactory) error {
normalizeLoggingConfig(&cfg)
if err := cfg.Validate(); err != nil {
logger.Fatalf("invalid config: %v", err)
@@ -58,7 +64,9 @@ func RunWithFactory(logPath string, stdin io.Reader, stdout io.Writer, logger *l
store := runtimeconfig.New(cfg)
logContext := strings.TrimSpace(logPath) != ""
+ loadOpts := appconfig.LoadOptions{ConfigPath: strings.TrimSpace(configPath)}
opts := makeServerOptions(cfg, logContext, client)
+ opts.ConfigLoadOptions = loadOpts
opts.ConfigStore = store
server := factory(stdin, stdout, logger, opts)
if configurable, ok := server.(interface{ ApplyOptions(lsp.ServerOptions) }); ok {
@@ -72,6 +80,7 @@ func RunWithFactory(logPath string, stdin io.Reader, stdout io.Writer, logger *l
client = newClient
}
opts := makeServerOptions(updated, logContext, client)
+ opts.ConfigLoadOptions = loadOpts
opts.ConfigStore = store
configurable.ApplyOptions(opts)
})
diff --git a/internal/hexailsp/run_more_test.go b/internal/hexailsp/run_more_test.go
index faaae41..338dd48 100644
--- a/internal/hexailsp/run_more_test.go
+++ b/internal/hexailsp/run_more_test.go
@@ -44,7 +44,7 @@ func TestRunWithFactory_BuildsOptionsAndClient(t *testing.T) {
cfg.MaxTokens = 123
cfg.PromptCodeActionRewriteSystem = "RSYS"
cfg.PromptCodeActionRewriteUser = "RUSER"
- if err := RunWithFactory("", &in, &out, logger, cfg, nil, factory); err != nil {
+ if err := RunWithFactory("", "", &in, &out, logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
if captured.MaxTokens != 123 {
@@ -71,7 +71,7 @@ func TestRunWithFactory_SubscriptionAppliesUpdates(t *testing.T) {
cfg := appconfig.Load(nil)
cfg.StatsWindowMinutes = 0
cfg.ContextMode = " WINDOW "
- if err := RunWithFactory("", &in, &out, logger, cfg, stubClient{}, factory); err != nil {
+ if err := RunWithFactory("", "", &in, &out, logger, cfg, stubClient{}, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
if capturedStore == nil {
diff --git a/internal/hexailsp/run_test.go b/internal/hexailsp/run_test.go
index 340a08a..6a3c789 100644
--- a/internal/hexailsp/run_test.go
+++ b/internal/hexailsp/run_test.go
@@ -36,7 +36,7 @@ func TestRunWithFactory_UsesDefaultsAndCallsServer(t *testing.T) {
gotOpts = opts
return &fakeServer{opts: opts}
}
- if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
+ 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 {
@@ -71,7 +71,7 @@ func TestRunWithFactory_BuildsClientWhenKeysPresent(t *testing.T) {
got = opts.Client
return &fakeServer{opts: opts}
}
- if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
+ if err := RunWithFactory("", "", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
if got == nil {
@@ -104,7 +104,7 @@ func TestRunWithFactory_NormalizesContextMode_AndSetsPreviewLimit(t *testing.T)
gotOpts = opts
return &fakeServer{opts: opts}
}
- if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
+ 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" {
@@ -130,13 +130,13 @@ func TestRunWithFactory_LogContextFlag(t *testing.T) {
}
return &fakeServer{opts: opts}
}
- if err := RunWithFactory("/tmp/some.log", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
+ if err := RunWithFactory("/tmp/some.log", "", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
if !got1.LogContext {
t.Fatalf("expected LogContext true when logPath is non-empty")
}
- if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
+ if err := RunWithFactory("", "", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil {
t.Fatalf("RunWithFactory error: %v", err)
}
if got2.LogContext {