summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-08-19 21:28:49 +0300
committerPaul Buetow <paul@buetow.org>2025-08-19 21:28:49 +0300
commit041196eab701b7a7ac79baf486a7c11fc11913e3 (patch)
tree9e90d860eedae85a8d7030f1017844e5dcbba186
parenta58afd061db45678fcf299c3d42f80656ce7e225 (diff)
chore: commit pending changes in status, LSP, and Ollama modules
-rw-r--r--PROJECTSTATUS.md2
-rw-r--r--internal/llm/ollama.go1
-rw-r--r--internal/lsp/handlers.go120
-rw-r--r--internal/lsp/types.go1
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"