summaryrefslogtreecommitdiff
path: root/internal/lsp/handlers_document.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/lsp/handlers_document.go')
-rw-r--r--internal/lsp/handlers_document.go160
1 files changed, 85 insertions, 75 deletions
diff --git a/internal/lsp/handlers_document.go b/internal/lsp/handlers_document.go
index 3f9d4b0..6a90919 100644
--- a/internal/lsp/handlers_document.go
+++ b/internal/lsp/handlers_document.go
@@ -2,18 +2,21 @@
package lsp
import (
- "context"
- "encoding/json"
- "codeberg.org/snonux/hexai/internal/llm"
- "codeberg.org/snonux/hexai/internal/logging"
- "strings"
- "time"
+ "context"
+ "encoding/json"
+ "strings"
+ "time"
+
+ "codeberg.org/snonux/hexai/internal/llm"
+ "codeberg.org/snonux/hexai/internal/logging"
)
// Package-level chat trigger vars for helpers without Server receiver.
// NewServer assigns these from configuration on startup.
-var chatSuffixChar byte = '>'
-var chatPrefixSingles = []string{"?", "!", ":", ";"}
+var (
+ chatSuffixChar byte = '>'
+ chatPrefixSingles = []string{"?", "!", ":", ";"}
+)
func (s *Server) handleDidOpen(req Request) {
var p DidOpenTextDocumentParams
@@ -97,7 +100,7 @@ func (s *Server) detectAndHandleChat(uri string) {
if d == nil || len(d.lines) == 0 {
return
}
- for i, raw := range d.lines {
+ for i, raw := range d.lines {
// Find last non-space character index
j := len(raw) - 1
for j >= 0 {
@@ -107,25 +110,32 @@ func (s *Server) detectAndHandleChat(uri string) {
}
break
}
- if j < 0 {
- continue
- }
- // Check suffix/prefix according to configuration
- if s.chatSuffix == "" {
- continue
- }
- // Last non-space must equal suffix
- if string(raw[j]) != s.chatSuffix {
- continue
- }
- // Require at least one char before suffix and that char must be in chatPrefixes
- if j < 1 { continue }
- prev := string(raw[j-1])
- isTrigger := false
- for _, pfx := range s.chatPrefixes {
- if prev == pfx { isTrigger = true; break }
- }
- if !isTrigger { continue }
+ if j < 0 {
+ continue
+ }
+ // Check suffix/prefix according to configuration
+ if s.chatSuffix == "" {
+ continue
+ }
+ // Last non-space must equal suffix
+ if string(raw[j]) != s.chatSuffix {
+ continue
+ }
+ // Require at least one char before suffix and that char must be in chatPrefixes
+ if j < 1 {
+ continue
+ }
+ prev := string(raw[j-1])
+ isTrigger := false
+ for _, pfx := range s.chatPrefixes {
+ if prev == pfx {
+ isTrigger = true
+ break
+ }
+ }
+ if !isTrigger {
+ continue
+ }
// Avoid double-answering: if the next non-empty line starts with '>' we skip.
k := i + 1
for k < len(d.lines) && strings.TrimSpace(d.lines[k]) == "" {
@@ -135,9 +145,9 @@ func (s *Server) detectAndHandleChat(uri string) {
continue
}
// Derive prompt by removing only the trailing '>'
- removeCount := len(s.chatSuffix)
+ removeCount := len(s.chatSuffix)
base := raw[:j+1-removeCount]
- prompt := strings.TrimSpace(base)
+ prompt := strings.TrimSpace(base)
if prompt == "" {
continue
}
@@ -246,37 +256,37 @@ func (s *Server) buildChatHistory(uri string, lineIdx int, currentPrompt string)
// stripTrailingTrigger removes the trailing chat trigger punctuation from a line if present.
func stripTrailingTrigger(sx string) string {
- s := strings.TrimRight(sx, " \t")
- if len(s) == 0 {
- return sx
- }
- // Configurable suffix removal when preceded by configured prefixes
- if len(s) >= 2 && s[len(s)-1] == chatSuffixChar {
- prev := string(s[len(s)-2])
- for _, pf := range chatPrefixSingles {
- if prev == pf {
- return strings.TrimRight(s[:len(s)-1], " \t")
- }
- }
- }
- // Legacy: remove one trailing punctuation (?, !, :) to build history nicely
- last := s[len(s)-1]
- switch last {
- case '?', '!', ':':
- return strings.TrimRight(s[:len(s)-1], " \t")
- default:
- return sx
- }
+ s := strings.TrimRight(sx, " \t")
+ if len(s) == 0 {
+ return sx
+ }
+ // Configurable suffix removal when preceded by configured prefixes
+ if len(s) >= 2 && s[len(s)-1] == chatSuffixChar {
+ prev := string(s[len(s)-2])
+ for _, pf := range chatPrefixSingles {
+ if prev == pf {
+ return strings.TrimRight(s[:len(s)-1], " \t")
+ }
+ }
+ }
+ // Legacy: remove one trailing punctuation (?, !, :) to build history nicely
+ last := s[len(s)-1]
+ switch last {
+ case '?', '!', ':':
+ return strings.TrimRight(s[:len(s)-1], " \t")
+ default:
+ return sx
+ }
}
// clientApplyEdit sends a workspace/applyEdit request to the client.
func (s *Server) clientApplyEdit(label string, edit WorkspaceEdit) {
- params := ApplyWorkspaceEditParams{Label: label, Edit: edit}
- id := s.nextReqID()
- req := Request{JSONRPC: "2.0", ID: id, Method: "workspace/applyEdit"}
- b, _ := json.Marshal(params)
- req.Params = b
- s.writeMessage(req)
+ params := ApplyWorkspaceEditParams{Label: label, Edit: edit}
+ id := s.nextReqID()
+ req := Request{JSONRPC: "2.0", ID: id, Method: "workspace/applyEdit"}
+ b, _ := json.Marshal(params)
+ req.Params = b
+ s.writeMessage(req)
}
// nextReqID returns a unique json.RawMessage id for server-initiated requests.
@@ -291,27 +301,27 @@ func (s *Server) nextReqID() json.RawMessage {
// clientShowDocument asks the client to open/focus a document and select a range.
func (s *Server) clientShowDocument(uri string, sel *Range) {
- var params struct {
- URI string `json:"uri"`
- External bool `json:"external,omitempty"`
- TakeFocus bool `json:"takeFocus,omitempty"`
- Selection *Range `json:"selection,omitempty"`
- }
- params.URI = uri
- params.TakeFocus = true
- params.Selection = sel
- id := s.nextReqID()
- req := Request{JSONRPC: "2.0", ID: id, Method: "window/showDocument"}
- b, _ := json.Marshal(params)
- req.Params = b
- s.writeMessage(req)
+ var params struct {
+ URI string `json:"uri"`
+ External bool `json:"external,omitempty"`
+ TakeFocus bool `json:"takeFocus,omitempty"`
+ Selection *Range `json:"selection,omitempty"`
+ }
+ params.URI = uri
+ params.TakeFocus = true
+ params.Selection = sel
+ id := s.nextReqID()
+ req := Request{JSONRPC: "2.0", ID: id, Method: "window/showDocument"}
+ b, _ := json.Marshal(params)
+ req.Params = b
+ s.writeMessage(req)
}
// deferShowDocument schedules a showDocument after a short delay to allow the client
// time to apply any pending edits (e.g., create the file before focusing it).
func (s *Server) deferShowDocument(uri string, sel Range) {
- go func() {
- time.Sleep(120 * time.Millisecond)
- s.clientShowDocument(uri, &sel)
- }()
+ go func() {
+ time.Sleep(120 * time.Millisecond)
+ s.clientShowDocument(uri, &sel)
+ }()
}