diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-19 21:28:49 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-19 21:28:49 +0300 |
| commit | 041196eab701b7a7ac79baf486a7c11fc11913e3 (patch) | |
| tree | 9e90d860eedae85a8d7030f1017844e5dcbba186 | |
| parent | a58afd061db45678fcf299c3d42f80656ce7e225 (diff) | |
chore: commit pending changes in status, LSP, and Ollama modules
| -rw-r--r-- | PROJECTSTATUS.md | 2 | ||||
| -rw-r--r-- | internal/llm/ollama.go | 1 | ||||
| -rw-r--r-- | internal/lsp/handlers.go | 120 | ||||
| -rw-r--r-- | internal/lsp/types.go | 1 |
4 files changed, 62 insertions, 62 deletions
diff --git a/PROJECTSTATUS.md b/PROJECTSTATUS.md index 4608f80..b44cd09 100644 --- a/PROJECTSTATUS.md +++ b/PROJECTSTATUS.md @@ -21,6 +21,7 @@ * [ ] Resolve diagnostics code action feature * [X] LSP server to be used with the Helix text editor * [X] Code completion using LLMs +* [ ] Have all text LLM prompts be configurable. With defaults as of now. * [X] Text completion in general * [ ] Be a replacement for 'github copilot cli' * [ ] Be able to perform inline chats (keeping history in the document) @@ -28,6 +29,7 @@ * [ ] Fine tune when Large Language Model (LLM) completions trigger, as it seems that there are some cases where the Large Language Model (LLM) receives a request but Helix isn't suggesting any completions. There seems to be something odd with the in logic. Investigate the TriggerChar logic and make sure it matches Helix's expectations. * [ ] Only one code completion should run at a time, even if multiple triggers occur simultaneously * [ ] Create "generate unit test" code action for selected code block +* [ ] Can anything else can be done with LSP? Be able to select code blocks and perform code actions on them diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go index 50e9837..c465723 100644 --- a/internal/llm/ollama.go +++ b/internal/llm/ollama.go @@ -57,7 +57,6 @@ func newOllama(baseURL, model string, defaultTemp *float64) Client { } } -// TODO: This function is too long and should be refactored for readability and maintainability. func (c ollamaClient) Chat(ctx context.Context, messages []Message, opts ...RequestOption) (string, error) { o := Options{Model: c.defaultModel} for _, opt := range opts { diff --git a/internal/lsp/handlers.go b/internal/lsp/handlers.go index 57186c1..d140ba1 100644 --- a/internal/lsp/handlers.go +++ b/internal/lsp/handlers.go @@ -96,24 +96,24 @@ func (s *Server) handleCodeAction(req Request) { } func (s *Server) buildRewriteCodeAction(p CodeActionParams, sel string) *CodeAction { - if instr, cleaned := instructionFromSelection(sel); strings.TrimSpace(instr) != "" { - sys := "You are a precise code refactoring engine. Rewrite the given code strictly according to the instruction. Return only the updated code with no prose or backticks. Preserve formatting where reasonable." - user := fmt.Sprintf("Instruction: %s\n\nSelected code to transform:\n%s", instr, cleaned) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - messages := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: user}} - opts := s.llmRequestOpts() - if text, err := s.llmClient.Chat(ctx, messages, opts...); err == nil { - if out := stripCodeFences(strings.TrimSpace(text)); out != "" { - edit := WorkspaceEdit{Changes: map[string][]TextEdit{p.TextDocument.URI: {{Range: p.Range, NewText: out}}}} - ca := CodeAction{Title: "Hexai: rewrite selection", Kind: "refactor.rewrite", Edit: &edit} - return &ca - } - } else { - logging.Logf("lsp ", "codeAction rewrite llm error: %v", err) - } - } - return nil + if instr, cleaned := instructionFromSelection(sel); strings.TrimSpace(instr) != "" { + sys := "You are a precise code refactoring engine. Rewrite the given code strictly according to the instruction. Return only the updated code with no prose or backticks. Preserve formatting where reasonable." + user := fmt.Sprintf("Instruction: %s\n\nSelected code to transform:\n%s", instr, cleaned) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + messages := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: user}} + opts := s.llmRequestOpts() + if text, err := s.llmClient.Chat(ctx, messages, opts...); err == nil { + if out := stripCodeFences(strings.TrimSpace(text)); out != "" { + edit := WorkspaceEdit{Changes: map[string][]TextEdit{p.TextDocument.URI: {{Range: p.Range, NewText: out}}}} + ca := CodeAction{Title: "Hexai: rewrite selection", Kind: "refactor.rewrite", Edit: &edit} + return &ca + } + } else { + logging.Logf("lsp ", "codeAction rewrite llm error: %v", err) + } + } + return nil } func (s *Server) buildDiagnosticsCodeAction(p CodeActionParams, sel string) *CodeAction { @@ -137,16 +137,16 @@ func (s *Server) buildDiagnosticsCodeAction(p CodeActionParams, sel string) *Cod defer cancel() messages := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: b.String()}} opts := s.llmRequestOpts() - if text, err := s.llmClient.Chat(ctx, messages, opts...); err == nil { - if out := stripCodeFences(strings.TrimSpace(text)); out != "" { - edit := WorkspaceEdit{Changes: map[string][]TextEdit{p.TextDocument.URI: {{Range: p.Range, NewText: out}}}} - ca := CodeAction{Title: "Hexai: resolve diagnostics", Kind: "quickfix", Edit: &edit} - return &ca - } - } else { - logging.Logf("lsp ", "codeAction diagnostics llm error: %v", err) - } - return nil + if text, err := s.llmClient.Chat(ctx, messages, opts...); err == nil { + if out := stripCodeFences(strings.TrimSpace(text)); out != "" { + edit := WorkspaceEdit{Changes: map[string][]TextEdit{p.TextDocument.URI: {{Range: p.Range, NewText: out}}}} + ca := CodeAction{Title: "Hexai: resolve diagnostics", Kind: "quickfix", Edit: &edit} + return &ca + } + } else { + logging.Logf("lsp ", "codeAction diagnostics llm error: %v", err) + } + return nil } func (s *Server) llmRequestOpts() []llm.RequestOption { @@ -482,13 +482,13 @@ func (s *Server) tryLLMCompletion(p CompletionParams, above, current, below, fun // Update response counters (received) s.incRecvCounters(len(text)) s.logLLMStats() - cleaned := stripCodeFences(strings.TrimSpace(text)) - if cleaned != "" { - cleaned = stripDuplicateAssignmentPrefix(current[:p.Position.Character], cleaned) - } - if cleaned == "" { - return nil, false - } + cleaned := stripCodeFences(strings.TrimSpace(text)) + if cleaned != "" { + cleaned = stripDuplicateAssignmentPrefix(current[:p.Position.Character], cleaned) + } + if cleaned == "" { + return nil, false + } return s.makeCompletionItems(cleaned, inParams, current, p, docStr), true } @@ -712,7 +712,7 @@ func isIdentChar(ch byte) bool { // already appears immediately to the left of the cursor on the current line. // Also handles simple '=' assignments. func stripDuplicateAssignmentPrefix(prefixBeforeCursor, suggestion string) string { - s2 := strings.TrimLeft(suggestion, " \t") + s2 := strings.TrimLeft(suggestion, " \t") // Prefer := if present at end of prefix if idx := strings.LastIndex(prefixBeforeCursor, ":="); idx >= 0 && idx+2 <= len(prefixBeforeCursor) { // Ensure only spaces follow in prefix (cursor at end of prefix segment) @@ -754,30 +754,30 @@ func stripDuplicateAssignmentPrefix(prefixBeforeCursor, suggestion string) strin // response when the entire output is wrapped, e.g. starting with "```go" or // "```" and ending with "```". It returns the inner content unchanged. func stripCodeFences(s string) string { - t := strings.TrimSpace(s) - if t == "" { - return t - } - lines := splitLines(t) - // find first and last non-empty lines - start := 0 - for start < len(lines) && strings.TrimSpace(lines[start]) == "" { - start++ - } - end := len(lines) - 1 - for end >= 0 && strings.TrimSpace(lines[end]) == "" { - end-- - } - if start >= len(lines) || end < 0 || start > end { - return t - } - first := strings.TrimSpace(lines[start]) - last := strings.TrimSpace(lines[end]) - if strings.HasPrefix(first, "```") && last == "```" && end > start { - inner := strings.Join(lines[start+1:end], "\n") - return inner - } - return t + t := strings.TrimSpace(s) + if t == "" { + return t + } + lines := splitLines(t) + // find first and last non-empty lines + start := 0 + for start < len(lines) && strings.TrimSpace(lines[start]) == "" { + start++ + } + end := len(lines) - 1 + for end >= 0 && strings.TrimSpace(lines[end]) == "" { + end-- + } + if start >= len(lines) || end < 0 || start > end { + return t + } + first := strings.TrimSpace(lines[start]) + last := strings.TrimSpace(lines[end]) + if strings.HasPrefix(first, "```") && last == "```" && end > start { + inner := strings.Join(lines[start+1:end], "\n") + return inner + } + return t } func labelForCompletion(cleaned, filter string) string { diff --git a/internal/lsp/types.go b/internal/lsp/types.go index 42a315f..ce98ac0 100644 --- a/internal/lsp/types.go +++ b/internal/lsp/types.go @@ -1,5 +1,4 @@ // Summary: LSP protocol types used by the server (requests, responses, params, capabilities). -// Not yet reviewed by a human package lsp import "encoding/json" |
