diff --git a/cmd/L/doc.go b/cmd/L/doc.go index 1afd7e4..390bfb3 100644 --- a/cmd/L/doc.go +++ b/cmd/L/doc.go @@ -15,10 +15,13 @@ attempt to find the focused window ID by connecting to acmefocused List of sub-commands: - comp [-e] + comp [-e] [-E] Print candidate completions at the cursor position. If -e (edit) flag is given and there is only one candidate, - the completion is applied instead of being printed. + the completion is applied instead of being printed. If + -E (Edit) flag is given, the first matching candidate is + applied, and all matches will be displayed in a dedicated + Acme window named /LSP/Completions. def [-p] Find where the symbol at the cursor position is defined @@ -35,9 +38,10 @@ List of sub-commands: impls List implementation location(s) of the symbol under the cursor. - refs + refs [-show] List locations where the symbol under the cursor is used - ("references"). + ("references"). If -show flag is given, all references are displayed + in a dedicated Acme window named /LSP/References (with absolute paths) rn Rename the symbol under the cursor to newname. @@ -75,11 +79,11 @@ List of sub-commands: Current working directory is removed if no directory is specified. -acme.addr string - address where acme is serving 9P file system (default "/tmp/ns.fhs.:0/acme") + address where acme is serving 9P file system (default "/tmp/ns.username.:0/acme") -acme.net string network where acme is serving 9P file system (default "unix") -proxy.addr string - address used for communication between acme-lsp and L (default "/tmp/ns.fhs.:0/acme-lsp.rpc") + address used for communication between acme-lsp and L (default "/tmp/ns.username.:0/acme-lsp.rpc") -proxy.net string network used for communication between acme-lsp and L (default "unix") -showconfig diff --git a/cmd/L/main.go b/cmd/L/main.go index 9979fd6..e0b02a1 100644 --- a/cmd/L/main.go +++ b/cmd/L/main.go @@ -5,7 +5,7 @@ import ( "context" "flag" "fmt" - "io/ioutil" + "io" "log" "net" "os" @@ -63,9 +63,10 @@ List of sub-commands: impls List implementation location(s) of the symbol under the cursor. - refs + refs [-show] List locations where the symbol under the cursor is used - ("references"). + ("references"). If -show flag is given, all references are displayed + in a dedicated Acme window named /LSP/References (with absolute paths) rn Rename the symbol under the cursor to newname. @@ -231,7 +232,8 @@ func run(cfg *config.Config, args []string) error { case "impls": return rc.Implementation(ctx, true) case "refs": - return rc.References(ctx) + args = args[1:] + return rc.References(ctx, len(args) > 0 && args[0] == "-show") case "rn": args = args[1:] if len(args) < 1 { @@ -280,7 +282,7 @@ func getFocusedWinID(addr string) (string, error) { return "", fmt.Errorf("$winid is empty and could not dial acmefocused: %v", err) } defer conn.Close() - b, err := ioutil.ReadAll(conn) + b, err := io.ReadAll(conn) if err != nil { return "", fmt.Errorf("$winid is empty and could not read acmefocused: %v", err) } diff --git a/cmd/L/main_test.go b/cmd/L/main_test.go index 367c0b0..bd42d5d 100644 --- a/cmd/L/main_test.go +++ b/cmd/L/main_test.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -38,7 +37,7 @@ func TestGetFocusedWinIDFromServer(t *testing.T) { os.Unsetenv("winid") want := "321" - dir, err := ioutil.TempDir("", "acmefocused") + dir, err := os.MkdirTemp("", "acmefocused") if err != nil { t.Fatalf("couldn't create temporary directory: %v", err) } diff --git a/cmd/Lone/doc.go b/cmd/Lone/doc.go index 6badb72..eb4b505 100644 --- a/cmd/Lone/doc.go +++ b/cmd/Lone/doc.go @@ -62,17 +62,27 @@ List of sub-commands: be very useful in practice. -acme.addr string - address where acme is serving 9P file system (default "/tmp/ns.fhs.:0/acme") + address where acme is serving 9P file system (default "/tmp/ns.username.:0/acme") -acme.net string network where acme is serving 9P file system (default "unix") -debug turn on debugging prints (deprecated: use -v) -dial value - language server address for filename match (e.g. '\.go$:localhost:4389') + map filename to language server address. The format is + 'handlers:host:port'. See -server flag for format of + handlers. (e.g. '\.go$:localhost:4389') + -hidediag + hide diagnostics sent by LSP server -rootdir string - root directory used for LSP initialization. (default "/") + root directory used for LSP initialization (default "/") + -rpc.trace + print the full rpc trace in lsp inspector format -server value - language server command for filename match (e.g. '\.go$:gopls') + map filename to language server command. The format is + 'handlers:cmd' where cmd is the LSP server command and handlers is + a comma separated list of 'regexp[@lang]'. The regexp matches the + filename and lang is a language identifier. (e.g. '\.go$:gopls' or + 'go.mod$@go.mod,go.sum$@go.sum,\.go$@go:gopls') -showconfig show configuration values and exit -v Verbose output diff --git a/cmd/Lone/main.go b/cmd/Lone/main.go index f8407d8..5f4287c 100644 --- a/cmd/Lone/main.go +++ b/cmd/Lone/main.go @@ -150,7 +150,7 @@ func run(cfg *config.Config, args []string) error { case "hov": err = rc.Hover(ctx) case "refs": - err = rc.References(ctx) + err = rc.References(ctx, false) case "rn": if len(args) < 2 { usage() diff --git a/cmd/acme-lsp/doc.go b/cmd/acme-lsp/doc.go index 669b695..13e9174 100644 --- a/cmd/acme-lsp/doc.go +++ b/cmd/acme-lsp/doc.go @@ -32,21 +32,31 @@ be changed by the FormatOnPut and CodeActionsOnPut configuration options. Usage: acme-lsp [flags] -acme.addr string - address where acme is serving 9P file system (default "/tmp/ns.fhs.:0/acme") + address where acme is serving 9P file system (default "/tmp/ns.username.:0/acme") -acme.net string network where acme is serving 9P file system (default "unix") -debug turn on debugging prints (deprecated: use -v) -dial value - language server address for filename match (e.g. '\.go$:localhost:4389') + map filename to language server address. The format is + 'handlers:host:port'. See -server flag for format of + handlers. (e.g. '\.go$:localhost:4389') + -hidediag + hide diagnostics sent by LSP server -proxy.addr string - address used for communication between acme-lsp and L (default "/tmp/ns.fhs.:0/acme-lsp.rpc") + address used for communication between acme-lsp and L (default "/tmp/ns.username.:0/acme-lsp.rpc") -proxy.net string network used for communication between acme-lsp and L (default "unix") -rootdir string - root directory used for LSP initialization. (default "/") + root directory used for LSP initialization (default "/") + -rpc.trace + print the full rpc trace in lsp inspector format -server value - language server command for filename match (e.g. '\.go$:gopls') + map filename to language server command. The format is + 'handlers:cmd' where cmd is the LSP server command and handlers is + a comma separated list of 'regexp[@lang]'. The regexp matches the + filename and lang is a language identifier. (e.g. '\.go$:gopls' or + 'go.mod$@go.mod,go.sum$@go.sum,\.go$@go:gopls') -showconfig show configuration values and exit -v Verbose output diff --git a/cmd/acmefocused/main_test.go b/cmd/acmefocused/main_test.go index f428b66..3c3b094 100644 --- a/cmd/acmefocused/main_test.go +++ b/cmd/acmefocused/main_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net" "os" "os/exec" @@ -35,7 +35,7 @@ func TestListenAndServe(t *testing.T) { t.Skip("skipping on windows because unix domain sockets are not supported") } - dir, err := ioutil.TempDir("", "acmefocused-test") + dir, err := os.MkdirTemp("", "acmefocused-test") if err != nil { t.Fatalf("couldn't create temporary directory: %v", err) } @@ -74,7 +74,7 @@ func TestListenAndServe(t *testing.T) { continue } want := []byte(testWinID + "\n") - got, err := ioutil.ReadAll(conn) + got, err := io.ReadAll(conn) if err != nil { t.Errorf("read failed: %v", err) } diff --git a/internal/acme/acme.go b/internal/acme/acme.go index 32a969d..1756dab 100644 --- a/internal/acme/acme.go +++ b/internal/acme/acme.go @@ -11,7 +11,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "os" "path" @@ -160,7 +159,7 @@ func Windows() ([]WinInfo, error) { return nil, err } defer index.Close() - data, err := ioutil.ReadAll(index) + data, err := io.ReadAll(index) if err != nil { return nil, err } @@ -328,7 +327,7 @@ func (w *Win) ReadAll(file string) ([]byte, error) { if err != nil { return nil, err } - return ioutil.ReadAll(f) + return io.ReadAll(f) } func (w *Win) ID() int { @@ -782,7 +781,7 @@ type EventHandler interface { Look(arg string) bool } -func (w *Win) loadText(e *Event, h EventHandler) { +func (w *Win) loadText(e *Event, _ EventHandler) { if len(e.Text) == 0 && e.Q0 < e.Q1 { w.Addr("#%d,#%d", e.Q0, e.Q1) data, err := w.ReadAll("xdata") diff --git a/internal/acmeutil/acme.go b/internal/acmeutil/acme.go index 1f59527..25c75ad 100644 --- a/internal/acmeutil/acme.go +++ b/internal/acmeutil/acme.go @@ -126,3 +126,23 @@ func Hijack(name string) (*Win, error) { } return nil, fmt.Errorf("hijack %q: window not found", name) } + +// NOTE: maybe remove and use FileReadWriter instead +type fileWriter func([]byte) (int, error) + +func (wr fileWriter) Write(p []byte) (int, error) { + return wr(p) +} + +func (w *Win) fileWriter(name string) io.Writer { + var writer fileWriter + writer = func(p []byte) (n int, err error) { + return w.Write(name, p) + } + return writer +} + +// returns io.Writer attached to this windows "body" +func (w *Win) BodyWriter() io.Writer { + return w.fileWriter("body") +} diff --git a/internal/lsp/acmelsp/acmelsp.go b/internal/lsp/acmelsp/acmelsp.go index 83d6bde..6561593 100644 --- a/internal/lsp/acmelsp/acmelsp.go +++ b/internal/lsp/acmelsp/acmelsp.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "sort" "strconv" @@ -78,10 +77,17 @@ func getLine(p string, l int) string { return "" } -func PrintLocations(w io.Writer, loc []protocol.Location) error { - wd, err := os.Getwd() - if err != nil { - wd = "" +func PrintLocations(w io.Writer, loc []protocol.Location, wds ...string) error { + var wd string + if len(wds) == 0 { + var err error + wd, err = os.Getwd() + if err != nil { + wd = "" + } + } else { + // workaround to pass "", filepath.Rel then uses absolute paths inside lsp.LocationLink + wd = wds[0] } sort.Slice(loc, func(i, j int) bool { a := loc[i] @@ -192,7 +198,7 @@ func CodeActionAndFormat(ctx context.Context, server FormatServer, doc *protocol if err != nil { return err } - b, err := ioutil.ReadAll(rd) + b, err := io.ReadAll(rd) if err != nil { return err } @@ -206,9 +212,6 @@ func CodeActionAndFormat(ctx context.Context, server FormatServer, doc *protocol }, }, }) - if err != nil { - return err - } } } edits, err := server.Formatting(ctx, &protocol.DocumentFormattingParams{ diff --git a/internal/lsp/acmelsp/acmelsp_test.go b/internal/lsp/acmelsp/acmelsp_test.go index dd837da..92af6f1 100644 --- a/internal/lsp/acmelsp/acmelsp_test.go +++ b/internal/lsp/acmelsp/acmelsp_test.go @@ -2,7 +2,7 @@ package acmelsp import ( "flag" - "io/ioutil" + "io" "reflect" "regexp" "strings" @@ -112,7 +112,7 @@ func TestParseFlagSet(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { f := flag.NewFlagSet("acme-lsp", flag.ContinueOnError) - f.SetOutput(ioutil.Discard) + f.SetOutput(io.Discard) cfg := config.Default() err := cfg.ParseFlags(config.LangServerFlags, f, tc.args) diff --git a/internal/lsp/acmelsp/assist.go b/internal/lsp/acmelsp/assist.go index 8f141e5..0a05cdf 100644 --- a/internal/lsp/acmelsp/assist.go +++ b/internal/lsp/acmelsp/assist.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "time" "unicode" @@ -145,7 +144,7 @@ func readLeftRight(id int, q0 int) (left, right rune, err error) { return 0, 0, err } - b, err := ioutil.ReadAll(w.FileReadWriter("xdata")) + b, err := io.ReadAll(w.FileReadWriter("xdata")) if err != nil { return 0, 0, err } diff --git a/internal/lsp/acmelsp/client_test.go b/internal/lsp/acmelsp/client_test.go index 5932fed..2fb8ea2 100644 --- a/internal/lsp/acmelsp/client_test.go +++ b/internal/lsp/acmelsp/client_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "io" - "io/ioutil" "os" "path/filepath" "reflect" @@ -46,18 +45,18 @@ func testGoModule(t *testing.T, server string, src string, f func(t *testing.T, } // Create the module - dir, err := ioutil.TempDir("", "examplemod") + dir, err := os.MkdirTemp("", "examplemod") if err != nil { t.Fatalf("TempDir failed: %v", err) } defer os.RemoveAll(dir) gofile := filepath.Join(dir, "main.go") - if err := ioutil.WriteFile(gofile, []byte(src), 0644); err != nil { + if err := os.WriteFile(gofile, []byte(src), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } modfile := filepath.Join(dir, "go.mod") - if err := ioutil.WriteFile(modfile, []byte(goMod), 0644); err != nil { + if err := os.WriteFile(modfile, []byte(goMod), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } @@ -72,7 +71,7 @@ func testGoModule(t *testing.T, server string, src string, f func(t *testing.T, srv, err := execServer(cs, &ClientConfig{ Server: &config.Server{}, RootDirectory: dir, - DiagWriter: &mockDiagosticsWriter{ioutil.Discard}, + DiagWriter: &mockDiagosticsWriter{io.Discard}, Workspaces: nil, }, false) if err != nil { @@ -275,18 +274,18 @@ func main() { var s string } ` - dir, err := ioutil.TempDir("", "examplemod") + dir, err := os.MkdirTemp("", "examplemod") if err != nil { t.Fatalf("TempDir failed: %v", err) } defer os.RemoveAll(dir) gofile := filepath.Join(dir, "main.go") - if err := ioutil.WriteFile(gofile, []byte(src), 0644); err != nil { + if err := os.WriteFile(gofile, []byte(src), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } modfile := filepath.Join(dir, "go.mod") - if err := ioutil.WriteFile(modfile, []byte(goMod), 0644); err != nil { + if err := os.WriteFile(modfile, []byte(goMod), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } @@ -349,14 +348,14 @@ if __name__=='__main__': ` func testPython(t *testing.T, src string, f func(t *testing.T, c *Client, uri protocol.DocumentURI)) { - dir, err := ioutil.TempDir("", "lspexample") + dir, err := os.MkdirTemp("", "lspexample") if err != nil { t.Fatalf("TempDir failed: %v", err) } defer os.RemoveAll(dir) pyfile := filepath.Join(dir, "main.py") - if err := ioutil.WriteFile(pyfile, []byte(src), 0644); err != nil { + if err := os.WriteFile(pyfile, []byte(src), 0644); err != nil { t.Fatalf("WriteFile failed: %v", err) } @@ -367,7 +366,7 @@ func testPython(t *testing.T, src string, f func(t *testing.T, c *Client, uri pr srv, err := execServer(cs, &ClientConfig{ Server: &config.Server{}, RootDirectory: dir, - DiagWriter: &mockDiagosticsWriter{ioutil.Discard}, + DiagWriter: &mockDiagosticsWriter{io.Discard}, Workspaces: nil, }, false) if err != nil { diff --git a/internal/lsp/acmelsp/config/config.go b/internal/lsp/acmelsp/config/config.go index bbdb63f..58da2ad 100644 --- a/internal/lsp/acmelsp/config/config.go +++ b/internal/lsp/acmelsp/config/config.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -211,7 +210,7 @@ func Load() (*Config, error) { } func load(filename string) (*Config, error) { - b, err := ioutil.ReadFile(filename) + b, err := os.ReadFile(filename) if err != nil { return nil, err } diff --git a/internal/lsp/acmelsp/exec_test.go b/internal/lsp/acmelsp/exec_test.go index 55417d7..eead4c8 100644 --- a/internal/lsp/acmelsp/exec_test.go +++ b/internal/lsp/acmelsp/exec_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -62,7 +61,7 @@ func TestServerSetWorkspaces(t *testing.T) { }, }, } - ss, err := NewServerSet(cfg, &mockDiagosticsWriter{ioutil.Discard}) + ss, err := NewServerSet(cfg, &mockDiagosticsWriter{io.Discard}) if err != nil { t.Fatalf("failed to create server set: %v", err) } diff --git a/internal/lsp/acmelsp/remote.go b/internal/lsp/acmelsp/remote.go index 7125ff7..e7877ba 100644 --- a/internal/lsp/acmelsp/remote.go +++ b/internal/lsp/acmelsp/remote.go @@ -216,7 +216,7 @@ func (rc *RemoteCmd) Implementation(ctx context.Context, print bool) error { return PrintLocations(rc.Stdout, loc) } -func (rc *RemoteCmd) References(ctx context.Context) error { +func (rc *RemoteCmd) References(ctx context.Context, showReferences bool) error { pos, _, err := rc.getPosition() if err != nil { return err @@ -234,6 +234,22 @@ func (rc *RemoteCmd) References(ctx context.Context) error { fmt.Fprintf(rc.Stderr, "No references found.\n") return nil } + + if showReferences { + cw, err := acmeutil.Hijack("/LSP/References") + if err != nil { + cw, err = acmeutil.NewWin() + if err != nil { + return err + } + + cw.Name("/LSP/References") + } + defer cw.Win.Ctl("clean") + + cw.Clear() + return PrintLocations(cw.BodyWriter(), loc, "") // NOTE: uses absolute paths + } return PrintLocations(rc.Stdout, loc) }