147 lines
3.4 KiB
Go
147 lines
3.4 KiB
Go
// lsp_rename — Rename symbol across workspace. Applies edits directly.
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"ollie/tools/lsp/internal/lsp"
|
|
"ollie/tools/lsp/internal/cmdutil"
|
|
)
|
|
|
|
func main() {
|
|
cmdutil.RunCustom(func() {
|
|
var args struct {
|
|
Path string `json:"path"`
|
|
Line string `json:"line"`
|
|
Col string `json:"col"`
|
|
NewName string `json:"new_name"`
|
|
}
|
|
if err := json.NewDecoder(os.Stdin).Decode(&args); err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
file, _ := filepath.Abs(args.Path)
|
|
line, _ := strconv.Atoi(args.Line)
|
|
col, _ := strconv.Atoi(args.Col)
|
|
|
|
result, err := lsp.ClientRequest(&lsp.BridgeRequest{
|
|
Method: "rename",
|
|
File: file,
|
|
Line: line - 1,
|
|
Col: col - 1,
|
|
NewName: args.NewName,
|
|
})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if result == nil || string(result) == "null" {
|
|
fmt.Println("(rename produced no changes)")
|
|
return
|
|
}
|
|
|
|
summary := applyWorkspaceEdit(result)
|
|
if summary == "" {
|
|
fmt.Println("(rename produced no changes)")
|
|
} else {
|
|
fmt.Print(summary)
|
|
}
|
|
})
|
|
}
|
|
|
|
func applyWorkspaceEdit(raw json.RawMessage) string {
|
|
var edit struct {
|
|
Changes map[string][]textEdit `json:"changes"`
|
|
DocumentChanges []struct {
|
|
TextDocument struct {
|
|
URI string `json:"uri"`
|
|
} `json:"textDocument"`
|
|
Edits []textEdit `json:"edits"`
|
|
} `json:"documentChanges"`
|
|
}
|
|
if err := json.Unmarshal(raw, &edit); err != nil {
|
|
return ""
|
|
}
|
|
|
|
changes := make(map[string][]textEdit)
|
|
if len(edit.DocumentChanges) > 0 {
|
|
for _, dc := range edit.DocumentChanges {
|
|
path := lsp.URIToPath(dc.TextDocument.URI)
|
|
changes[path] = append(changes[path], dc.Edits...)
|
|
}
|
|
} else {
|
|
for uri, edits := range edit.Changes {
|
|
path := lsp.URIToPath(uri)
|
|
changes[path] = edits
|
|
}
|
|
}
|
|
|
|
var sb strings.Builder
|
|
totalEdits := 0
|
|
for path, edits := range changes {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
lines := strings.SplitAfter(string(data), "\n")
|
|
|
|
sort.Slice(edits, func(i, j int) bool {
|
|
if edits[i].Range.Start.Line != edits[j].Range.Start.Line {
|
|
return edits[i].Range.Start.Line > edits[j].Range.Start.Line
|
|
}
|
|
return edits[i].Range.Start.Character > edits[j].Range.Start.Character
|
|
})
|
|
|
|
for _, e := range edits {
|
|
sl, sc := e.Range.Start.Line, e.Range.Start.Character
|
|
el, ec := e.Range.End.Line, e.Range.End.Character
|
|
if sl >= len(lines) {
|
|
continue
|
|
}
|
|
|
|
before := strings.Join(lines[:sl], "") + lines[sl][:min(sc, len(lines[sl]))]
|
|
var after string
|
|
if el < len(lines) {
|
|
after = lines[el][min(ec, len(lines[el])):] + strings.Join(lines[el+1:], "")
|
|
}
|
|
content := before + e.NewText + after
|
|
lines = strings.SplitAfter(content, "\n")
|
|
}
|
|
|
|
os.WriteFile(path, []byte(strings.Join(lines, "")), 0644)
|
|
totalEdits += len(edits)
|
|
fmt.Fprintf(&sb, "%s: %d edit(s)\n", path, len(edits))
|
|
}
|
|
fmt.Fprintf(&sb, "total: %d file(s), %d edit(s)\n", len(changes), totalEdits)
|
|
return sb.String()
|
|
}
|
|
|
|
type textEdit struct {
|
|
Range struct {
|
|
Start struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"start"`
|
|
End struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"end"`
|
|
} `json:"range"`
|
|
NewText string `json:"newText"`
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|