summaryrefslogtreecommitdiff
path: root/internal/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'internal/lsp')
-rw-r--r--internal/lsp/handlers_codeaction.go73
-rw-r--r--internal/lsp/server.go22
2 files changed, 68 insertions, 27 deletions
diff --git a/internal/lsp/handlers_codeaction.go b/internal/lsp/handlers_codeaction.go
index 17e92bc..d8dba38 100644
--- a/internal/lsp/handlers_codeaction.go
+++ b/internal/lsp/handlers_codeaction.go
@@ -31,24 +31,42 @@ func (s *Server) handleCodeAction(req Request) {
}
sel := extractRangeText(d, p.Range)
- actions := make([]CodeAction, 0, 4)
+ actions := make([]CodeAction, 0, 5)
if a := s.buildRewriteCodeAction(p, sel); a != nil {
actions = append(actions, *a)
}
if a := s.buildDiagnosticsCodeAction(p, sel); a != nil {
actions = append(actions, *a)
}
- if a := s.buildDocumentCodeAction(p, sel); a != nil {
- actions = append(actions, *a)
- }
- if a := s.buildGoUnitTestCodeAction(p); a != nil {
- actions = append(actions, *a)
- }
+ if a := s.buildDocumentCodeAction(p, sel); a != nil {
+ actions = append(actions, *a)
+ }
+ if a := s.buildGoUnitTestCodeAction(p); a != nil {
+ actions = append(actions, *a)
+ }
+ if a := s.buildSimplifyCodeAction(p, sel); a != nil {
+ actions = append(actions, *a)
+ }
if len(req.ID) != 0 {
s.reply(req.ID, actions, nil)
}
}
+func (s *Server) buildSimplifyCodeAction(p CodeActionParams, sel string) *CodeAction {
+ if strings.TrimSpace(sel) == "" {
+ return nil
+ }
+ payload := struct {
+ Type string `json:"type"`
+ URI string `json:"uri"`
+ Range Range `json:"range"`
+ Selection string `json:"selection"`
+ }{Type: "simplify", URI: p.TextDocument.URI, Range: p.Range, Selection: sel}
+ raw, _ := json.Marshal(payload)
+ ca := CodeAction{Title: "Hexai: simplify and improve", Kind: "refactor", Data: raw}
+ return &ca
+}
+
func (s *Server) buildRewriteCodeAction(p CodeActionParams, sel string) *CodeAction {
if instr, cleaned := instructionFromSelection(sel); strings.TrimSpace(instr) != "" {
payload := struct {
@@ -97,7 +115,7 @@ func (s *Server) resolveCodeAction(ca CodeAction) (CodeAction, bool) {
if err := json.Unmarshal(ca.Data, &payload); err != nil {
return ca, false
}
- switch payload.Type {
+ switch payload.Type {
case "rewrite":
sys := s.promptRewriteSystem
user := renderTemplate(s.promptRewriteUser, map[string]string{"instruction": payload.Instruction, "selection": payload.Selection})
@@ -155,17 +173,34 @@ func (s *Server) resolveCodeAction(ca CodeAction) (CodeAction, bool) {
} else {
logging.Logf("lsp ", "codeAction document llm error: %v", err)
}
- case "go_test":
- if edit, jumpURI, jumpRange, ok := s.resolveGoTest(payload.URI, payload.Range.Start); ok {
- ca.Edit = &edit
- // After edit is applied, ask client to jump to new test function
- ca.Command = &Command{Title: "Jump to generated test", Command: "hexai.showDocument", Arguments: []any{jumpURI, jumpRange}}
- // Also send a server-initiated showDocument shortly after resolve to cover
- // clients that do not execute commands from code actions.
- s.deferShowDocument(jumpURI, jumpRange)
- return ca, true
- }
- }
+ case "go_test":
+ if edit, jumpURI, jumpRange, ok := s.resolveGoTest(payload.URI, payload.Range.Start); ok {
+ ca.Edit = &edit
+ // After edit is applied, ask client to jump to new test function
+ ca.Command = &Command{Title: "Jump to generated test", Command: "hexai.showDocument", Arguments: []any{jumpURI, jumpRange}}
+ // Also send a server-initiated showDocument shortly after resolve to cover
+ // clients that do not execute commands from code actions.
+ s.deferShowDocument(jumpURI, jumpRange)
+ return ca, true
+ }
+ case "simplify":
+ sys := s.promptRewriteSystem
+ // Reuse rewrite user template with a fixed instruction
+ user := renderTemplate(s.promptRewriteUser, map[string]string{"instruction": "Simplify and improve the code while preserving behavior. Return only the improved code.", "selection": payload.Selection})
+ 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{payload.URI: {{Range: payload.Range, NewText: out}}}}
+ ca.Edit = &edit
+ return ca, true
+ }
+ } else {
+ logging.Logf("lsp ", "codeAction simplify llm error: %v", err)
+ }
+ }
return ca, false
}
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 1c3e676..796d6f4 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -78,9 +78,11 @@ type Server struct {
promptDocumentSystem string
promptRewriteUser string
promptDiagnosticsUser string
- promptDocumentUser string
- promptGoTestSystem string
- promptGoTestUser string
+ promptDocumentUser string
+ promptGoTestSystem string
+ promptGoTestUser string
+ promptSimplifySystem string
+ promptSimplifyUser string
}
// ServerOptions collects configuration for NewServer to avoid long parameter lists.
@@ -119,8 +121,10 @@ type ServerOptions struct {
PromptRewriteUser string
PromptDiagnosticsUser string
PromptDocumentUser string
- PromptGoTestSystem string
- PromptGoTestUser string
+ PromptGoTestSystem string
+ PromptGoTestUser string
+ PromptSimplifySystem string
+ PromptSimplifyUser string
}
func NewServer(r io.Reader, w io.Writer, logger *log.Logger, opts ServerOptions) *Server {
@@ -199,9 +203,11 @@ func NewServer(r io.Reader, w io.Writer, logger *log.Logger, opts ServerOptions)
s.promptDocumentSystem = opts.PromptDocumentSystem
s.promptRewriteUser = opts.PromptRewriteUser
s.promptDiagnosticsUser = opts.PromptDiagnosticsUser
- s.promptDocumentUser = opts.PromptDocumentUser
- s.promptGoTestSystem = opts.PromptGoTestSystem
- s.promptGoTestUser = opts.PromptGoTestUser
+ s.promptDocumentUser = opts.PromptDocumentUser
+ s.promptGoTestSystem = opts.PromptGoTestSystem
+ s.promptGoTestUser = opts.PromptGoTestUser
+ s.promptSimplifySystem = opts.PromptSimplifySystem
+ s.promptSimplifyUser = opts.PromptSimplifyUser
// Assign package-level inline trigger chars for free helper functions
if s.inlineOpen != "" {