diff --git a/.gitignore b/.gitignore index 55709f7..9ce8591 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ cmd/acme-lsp/acme-lsp cmd/L/L cmd/Lone/Lone +bin/* guide diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..105fca6 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +go build -o ./bin ./cmd/L +go build -o ./bin ./cmd/acme-lsp +go build -o ./bin ./cmd/acmefocused diff --git a/cmd/L/main.go b/cmd/L/main.go index aa3be57..9dccb82 100644 --- a/cmd/L/main.go +++ b/cmd/L/main.go @@ -107,10 +107,15 @@ func usage() { os.Exit(2) } +var logger = log.Default() + func main() { flag.Usage = usage cfg := cmd.Setup(config.ProxyFlags) + logger.SetFlags(log.Llongfile) + logger.SetPrefix("L: ") + err := run(cfg, flag.Args()) if err != nil { log.Fatalf("%v", err) @@ -128,12 +133,19 @@ func run(cfg *config.Config, args []string) error { if err != nil { return fmt.Errorf("dial failed: %v", err) } - defer conn.Close() + + logger.Print(fmt.Sprintf("net.Dial: %s, %s", cfg.ProxyNetwork, cfg.ProxyAddress)) + defer func() { + conn.Close() + logger.Print("L connection closed") + }() stream := jsonrpc2.NewHeaderStream(conn, conn) ctx, rpc, server := proxy.NewClient(ctx, stream, nil) go rpc.Run(ctx) + logger.Print("started JSONN RPC") + ver, err := server.Version(ctx) if err != nil { return err @@ -204,6 +216,7 @@ func run(cfg *config.Config, args []string) error { return rc.Completion(ctx, len(args) > 0 && args[0] == "-e") case "def": args = args[1:] + return rc.Definition(ctx, len(args) > 0 && args[0] == "-p") case "fmt": return rc.OrganizeImportsAndFormat(ctx) diff --git a/cmd/acme-lsp/main.go b/cmd/acme-lsp/main.go index 31afde1..35c7e0c 100644 --- a/cmd/acme-lsp/main.go +++ b/cmd/acme-lsp/main.go @@ -54,9 +54,13 @@ func usage() { os.Exit(2) } +var logger = log.Default() + func main() { flag.Usage = usage cfg := cmd.Setup(config.LangServerFlags | config.ProxyFlags) + logger.SetFlags(log.Llongfile) + logger.SetPrefix("acme-lsp: ") ctx := context.Background() app, err := NewApplication(ctx, cfg, flag.Args()) @@ -99,6 +103,7 @@ func NewApplication(ctx context.Context, cfg *config.Config, args []string) (*Ap func (app *Application) Run(ctx context.Context) error { go app.fm.Run() + log.Print("start acmelsp ListenAndServeProxy") err := acmelsp.ListenAndServeProxy(ctx, app.cfg, app.ss, app.fm) if err != nil { return fmt.Errorf("proxy failed: %v", err) diff --git a/internal/golang_org_x_tools.update.sh b/internal/golang_org_x_tools.update.sh index 3d0d9b3..d2353bf 100755 --- a/internal/golang_org_x_tools.update.sh +++ b/internal/golang_org_x_tools.update.sh @@ -11,8 +11,8 @@ COMMIT=952e2c076240 rm -rf $DIR git clone $REPO ( - cd tools - git checkout $COMMIT + cd tools + git checkout $COMMIT ) mkdir $DIR @@ -23,9 +23,9 @@ mv tools/internal/telemetry $DIR/telemetry mv tools/internal/xcontext $DIR/xcontext ( - cd tools - echo "Packages in this directory is copied from golang.org/x/tools/internal (commit $COMMIT)." - #git show -s --format='(commit %h on %ci).' + cd tools + echo "Packages in this directory is copied from golang.org/x/tools/internal (commit $COMMIT)." + #git show -s --format='(commit %h on %ci).' ) > $DIR/README.txt find $DIR -name '*.go' | xargs sed -i 's!golang.org/x/tools/internal!github.com/fhs/acme-lsp/internal/golang_org_x_tools!' diff --git a/internal/golang_org_x_tools/jsonrpc2/jsonrpc2.go b/internal/golang_org_x_tools/jsonrpc2/jsonrpc2.go index 62c8141..0f40f0c 100644 --- a/internal/golang_org_x_tools/jsonrpc2/jsonrpc2.go +++ b/internal/golang_org_x_tools/jsonrpc2/jsonrpc2.go @@ -139,6 +139,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface Method: method, Params: jsonParams, } + // marshal the request now it is complete data, err := json.Marshal(request) if err != nil { @@ -147,6 +148,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface for _, h := range c.handlers { ctx = h.Request(ctx, c, Send, request) } + // we have to add ourselves to the pending map before we send, otherwise we // are racing the response rchan := make(chan *WireResponse) @@ -177,6 +179,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface for _, h := range c.handlers { ctx = h.Response(ctx, c, Receive, response) } + // is it an error response? if response.Error != nil { return response.Error @@ -184,9 +187,11 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface if result == nil || response.Result == nil { return nil } + if err := json.Unmarshal(*response.Result, result); err != nil { return fmt.Errorf("unmarshalling result: %v", err) } + return nil case <-ctx.Done(): // allow the handler to propagate the cancel @@ -261,6 +266,7 @@ func (r *Request) Reply(ctx context.Context, result interface{}, err error) erro if err != nil { return err } + for _, h := range r.conn.handlers { ctx = h.Response(ctx, r.conn, Send, response) } diff --git a/internal/lsp/acmelsp/client.go b/internal/lsp/acmelsp/client.go index e1329b9..92f0c15 100644 --- a/internal/lsp/acmelsp/client.go +++ b/internal/lsp/acmelsp/client.go @@ -36,6 +36,11 @@ func (h *clientHandler) ShowMessage(ctx context.Context, params *protocol.ShowMe return nil } +func (h *clientHandler) Metadata(ctx context.Context, params *protocol.MetadataParams) error { + log.Printf("LSP Metadata, params: %v\n", params) + return nil +} + func (h *clientHandler) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { if h.cfg.Logger != nil { h.cfg.Logger.Printf("%v: %v\n", params.Type, params.Message) @@ -121,7 +126,7 @@ func (c *Client) init(conn net.Conn, cfg *ClientConfig) error { if cfg.RPCTrace { stream = protocol.LoggingStream(stream, os.Stderr) } - ctx, rpc, server := protocol.NewClient(ctx, stream, &clientHandler{ + ctx, rpc, serverDispatcher := protocol.NewClient(ctx, stream, &clientHandler{ cfg: cfg, hideDiag: cfg.HideDiag, diagWriter: cfg.DiagWriter, @@ -132,6 +137,8 @@ func (c *Client) init(conn net.Conn, cfg *ClientConfig) error { if err != nil { log.Printf("connection terminated: %v", err) } + + log.Printf("client RCP runs ") }() d, err := filepath.Abs(cfg.RootDirectory) @@ -168,7 +175,7 @@ func (c *Client) init(conn net.Conn, cfg *ClientConfig) error { if err := rpc.Notify(ctx, "initialized", &protocol.InitializedParams{}); err != nil { return fmt.Errorf("initialized failed: %v", err) } - c.Server = server + c.Server = serverDispatcher c.initializeResult = &result return nil } diff --git a/internal/lsp/acmelsp/config/config.go b/internal/lsp/acmelsp/config/config.go index 27072a0..b7a7eb8 100644 --- a/internal/lsp/acmelsp/config/config.go +++ b/internal/lsp/acmelsp/config/config.go @@ -80,6 +80,7 @@ type Config struct { // Server describes a LSP server. type Server struct { + Name string // Command that speaks LSP on stdin/stdout. // Can be empty if Address is given. Command []string diff --git a/internal/lsp/acmelsp/exec.go b/internal/lsp/acmelsp/exec.go index 2ee9b02..32f39ab 100644 --- a/internal/lsp/acmelsp/exec.go +++ b/internal/lsp/acmelsp/exec.go @@ -20,7 +20,9 @@ import ( ) type Server struct { - conn net.Conn + Name string + conn net.Conn + Client *Client } @@ -30,6 +32,10 @@ func (s *Server) Close() { } } +func (s *Server) Conn() net.Conn { + return s.conn +} + func execServer(cs *config.Server, cfg *ClientConfig) (*Server, error) { args := cs.Command @@ -190,13 +196,17 @@ func NewServerSet(cfg *config.Config, diagWriter DiagnosticsWriter) (*ServerSet, } logger = log.New(f, "", log.LstdFlags) } + data = append(data, &ServerInfo{ Server: cs, FilenameHandler: &cfg.FilenameHandlers[i], Re: re, Logger: logger, }) + + log.Printf("Add %#v server to serverSet ", cs) } + return &ServerSet{ Data: data, diagWriter: diagWriter, @@ -236,6 +246,9 @@ func (ss *ServerSet) StartForFile(filename string) (*Server, bool, error) { if err != nil { return nil, false, err } + + srv.Name = info.Name + return srv, true, err } diff --git a/internal/lsp/acmelsp/files.go b/internal/lsp/acmelsp/files.go index 465db8d..7d2c92d 100644 --- a/internal/lsp/acmelsp/files.go +++ b/internal/lsp/acmelsp/files.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "sync" "github.com/fhs/acme-lsp/internal/acme" @@ -82,6 +83,7 @@ func (fm *FileManager) Run() { if err := fm.didSave(ev.ID, ev.Name); err != nil { log.Printf("didSave failed in file manager: %v", err) } + if fm.cfg.FormatOnPut { if err := fm.format(ev.ID, ev.Name); err != nil && Verbose { log.Printf("Format failed in file manager: %v", err) @@ -193,6 +195,7 @@ func (fm *FileManager) didSave(winid int, name string) error { if err != nil { return err } + return lsp.DidSave(context.Background(), c, name) }) } diff --git a/internal/lsp/acmelsp/metadata.go b/internal/lsp/acmelsp/metadata.go new file mode 100644 index 0000000..5e738ef --- /dev/null +++ b/internal/lsp/acmelsp/metadata.go @@ -0,0 +1,90 @@ +// Package acmelsp implements the core of acme-lsp commands. +package acmelsp + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/fhs/acme-lsp/internal/lsp/protocol" +) + +//metdata is implemented due to the https://github.com/OmniSharp/omnisharp-roslyn/issues/2238 + +func convertFilePath(p string) (path string) { + path = strings.Replace(p, "$metadata$", fmt.Sprintf("%s/csharp-metadata/", os.TempDir()), 1) //os.TempDir + return +} + +func GetMetaParas(s string) (*protocol.MetadataParams, bool) { // + out := &protocol.MetadataParams{TimeOut: 5000} + parts := strings.Split(s, "/Assembly/") + + if len(parts) < 2 { + return nil, false + } + + pName := strings.TrimPrefix(parts[0], "file:///%24metadata%24/Project/") + + out.ProjectName = pName + + parts = strings.Split(parts[1], "/Symbol/") + + if len(parts) != 2 { + return nil, false + } + + assemblyName := strings.Replace(parts[0], "/", ".", -1) + + out.AssemblyName = assemblyName + + typeName := strings.Replace(strings.TrimSuffix(parts[1], ".cs"), "/", ".", -1) + + out.TypeName = typeName + + return out, true + +} + +func (rc *RemoteCmd) localizeMetadata(ctx context.Context, uri string) (string, error) { + p, ok := GetMetaParas(uri) + if !ok { + return "", fmt.Errorf("failed to parse URI to MetaParas") + } + // server over here is the acme-lsp server + src, err := rc.server.Metadata(ctx, p) + + if err != nil { + return "", fmt.Errorf("failed to retrive metatdata of uri: %s, with err: %w", uri, err) + } + + key := src.SourceName + + if v, ok := rc.metadataSet[key]; ok { + return v, nil + } + + path := convertFilePath(key) + + if err := os.MkdirAll(filepath.Dir(path), 0770); err != nil { + return "", fmt.Errorf("failed to create dir %s for metatdata of uri: %s to disk, with err: %v", filepath.Dir(path), uri, err) + } + + f, err := os.Create(path) // creates a file at current directory + if err != nil { + return "", fmt.Errorf("failed to create file for metatdata of uri: %s to disk, with err: %v", uri, err) + } + + defer f.Close() + + if err := ioutil.WriteFile(path, []byte(src.Source), 0644); err != nil { + return "", fmt.Errorf("failed to write metatdata of uri: %s to disk, with err: %v", uri, err) + } + + rc.metadataSet[key] = path + return path, nil + +} diff --git a/internal/lsp/acmelsp/proxy.go b/internal/lsp/acmelsp/proxy.go index 76fb719..444cd17 100644 --- a/internal/lsp/acmelsp/proxy.go +++ b/internal/lsp/acmelsp/proxy.go @@ -3,6 +3,7 @@ package acmelsp import ( "context" "fmt" + "log" "github.com/fhs/acme-lsp/internal/golang_org_x_tools/jsonrpc2" "github.com/fhs/acme-lsp/internal/lsp/acmelsp/config" @@ -17,6 +18,8 @@ type proxyServer struct { fm *FileManager } +var _ proxy.Server = (*proxyServer)(nil) + func (s *proxyServer) Version(ctx context.Context) (int, error) { return proxy.Version, nil } @@ -58,9 +61,22 @@ func (s *proxyServer) Definition(ctx context.Context, params *protocol.Definitio if err != nil { return nil, fmt.Errorf("Definition: %v", err) } + return srv.Client.Definition(ctx, params) } +func (s *proxyServer) Metadata(ctx context.Context, params *protocol.MetadataParams) (*protocol.MetaSourceRsponse, error) { + srv, err := serverForURI(s.ss, "csharp.cs") + + log.Printf("call to lsp server: %v Call Metadata", srv.Name) + + if err != nil { + return nil, fmt.Errorf("Definition: %v", err) + } + + return srv.Client.Metadata(ctx, params) +} + func (s *proxyServer) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) { srv, err := serverForURI(s.ss, params.TextDocument.URI) if err != nil { @@ -170,11 +186,13 @@ func ListenAndServeProxy(ctx context.Context, cfg *config.Config, ss *ServerSet, if err != nil { return err } - stream := jsonrpc2.NewHeaderStream(conn, conn) - ctx, rpc, _ := proxy.NewServer(ctx, stream, &proxyServer{ + + acmlspStream := jsonrpc2.NewHeaderStream(conn, conn) + ctx, rpc, _ := proxy.NewServer(ctx, acmlspStream, &proxyServer{ ss: ss, fm: fm, }) + go rpc.Run(ctx) } } diff --git a/internal/lsp/acmelsp/remote.go b/internal/lsp/acmelsp/remote.go index 5560578..4cff6f2 100644 --- a/internal/lsp/acmelsp/remote.go +++ b/internal/lsp/acmelsp/remote.go @@ -20,15 +20,20 @@ type RemoteCmd struct { winid int Stdout io.Writer Stderr io.Writer + + metadataSet map[string]string } func NewRemoteCmd(server proxy.Server, winid int) *RemoteCmd { - return &RemoteCmd{ - server: server, - winid: winid, - Stdout: os.Stdout, - Stderr: os.Stderr, + r := &RemoteCmd{ + server: server, + winid: winid, + Stdout: os.Stdout, + Stderr: os.Stderr, + metadataSet: map[string]string{}, } + + return r } func (rc *RemoteCmd) getPosition() (pos *protocol.TextDocumentPositionParams, filename string, err error) { @@ -119,9 +124,30 @@ func (rc *RemoteCmd) Definition(ctx context.Context, print bool) error { if err != nil { return fmt.Errorf("bad server response: %v", err) } + + sufix := ".cs" + uri := pos.TextDocument.URI + + if strings.HasSuffix(uri, sufix) { + for i, loc := range locations { + if !strings.HasPrefix(loc.URI, "file:///%24metadata%24") { + continue + } + + path, err := rc.localizeMetadata(ctx, loc.URI) + if err != nil { + return fmt.Errorf("can't localize metadata: %v", err) + } + + locations[i].URI = protocol.DocumentURI(path) + + } + } + if print { return PrintLocations(rc.Stdout, locations) } + return PlumbLocations(locations) } @@ -192,6 +218,7 @@ func (rc *RemoteCmd) References(ctx context.Context) error { if err != nil { return err } + if len(loc) == 0 { fmt.Fprintf(rc.Stderr, "No references found.\n") return nil diff --git a/internal/lsp/protocol/compat.go b/internal/lsp/protocol/compat.go index 8e3e6e8..4a171f5 100644 --- a/internal/lsp/protocol/compat.go +++ b/internal/lsp/protocol/compat.go @@ -95,3 +95,34 @@ func (ls *Locations) UnmarshalJSON(data []byte) error { return nil } + +// compItems is a type which represents the union of Location and []Location +type compList CompletionList + +func (c *compList) UnmarshalJSON(data []byte) error { + d := strings.TrimSpace(string(data)) + if len(d) == 0 && strings.EqualFold(d, "null") { + return nil + } + + if d[0] == '[' { + var items []CompletionItem + err := json.Unmarshal(data, &items) + if err != nil { + return err + } + + c.Items = items + } else { + + var tmp CompletionList + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + + *c = compList(tmp) + } + + return nil +} diff --git a/internal/lsp/protocol/metadata.go b/internal/lsp/protocol/metadata.go new file mode 100644 index 0000000..569103d --- /dev/null +++ b/internal/lsp/protocol/metadata.go @@ -0,0 +1,66 @@ +package protocol + +//metdata is implemented due to the https://github.com/OmniSharp/omnisharp-roslyn/issues/2238 +const ( + MetadataEndpoint = "o#/metadata" //omnisharp-roslyn endpoint for fetching metadata from csharp LSP + // MetadataEndpoint = "csharp/metadata" //csharp-ls endpoint for fetching metadata from csharp LSP +) + +// https://github.com/OmniSharp/omnisharp-vscode/blob/7689b0d3dd615224ac890dd9bccf9fb0bdae4a64/src/omnisharp/protocol.ts#L96-L111 +// export interface MetadataSource { +// AssemblyName: string; +// ProjectName: string; +// VersionNumber: string; +// Language: string; +// TypeName: string; +// } +// +// export interface MetadataRequest extends MetadataSource { +// Timeout?: number; +// } +// +// export interface MetadataResponse { +// SourceName: string; +// Source: string; +// } + +type MetadataParams OmnisharpRoslynMetadataParams +type MetaSourceRsponse OmnisharpRoslynMetaSourceReponse + +type OmnisharpRoslynMetadataParams struct { + TimeOut int `json:"timeout,omitempty"` + // + AssemblyName string `json:"assemblyName,omitempty"` + ProjectName string `json:"projectName,omitempty"` + VersionNumber string `json:"versionNumber,omitempty"` + Language string `json:"language,omitempty"` + TypeName string `json:"typeName,omitempty"` +} + +type OmnisharpRoslynMetaSourceReponse struct { + Source string `json:"source,omitempty"` + SourceName string `json:"sourceName,omitempty"` +} + +// https://github.com/razzmatazz/csharp-language-server/blob/549740a22cba3217bb431ea06d9e17275c079062/src/CSharpLanguageServer/Server.fs#L71-L80 +// type CSharpMetadataParams = { +// TextDocument: TextDocumentIdentifier +// } +// +// type CSharpMetadataResponse = { +// ProjectName: string; +// AssemblyName: string; +// SymbolName: string; +// Source: string; +// } + +type CsharpLsMetaResponse struct { + Source string `json:"source,omitempty"` + AssemblyName string `json:"assemblyName,omitempty"` + ProjectName string `json:"projectName,omitempty"` + TypeName string `json:"typeName,omitempty"` +} + +type CsharpLsMetadataParams struct { + TextDocument TextDocumentIdentifier `json:"textDocument"` +} diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go index 60475f3..4ef4ec5 100644 --- a/internal/lsp/protocol/tsclient.go +++ b/internal/lsp/protocol/tsclient.go @@ -1,7 +1,5 @@ package protocol -// Code generated (see typescript/README.md) DO NOT EDIT. - import ( "context" "encoding/json" diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go index 5b5bc71..a05e2bf 100644 --- a/internal/lsp/protocol/tsprotocol.go +++ b/internal/lsp/protocol/tsprotocol.go @@ -4,8 +4,6 @@ // last fetched Tue Sep 24 2019 17:44:28 GMT-0400 (Eastern Daylight Time) package protocol -// Code generated (see typescript/README.md) DO NOT EDIT. - /*ImplementationClientCapabilities defined: * Since 3.6.0 */ @@ -986,7 +984,7 @@ type ServerCapabilities struct { /*HoverProvider defined: * The server provides hover support. */ - HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions + HoverProvider interface{} `json:"hoverProvider,omitempty"` // boolean | HoverOptions /*SignatureHelpProvider defined: * The server provides signature help support. @@ -996,37 +994,37 @@ type ServerCapabilities struct { /*DeclarationProvider defined: * The server provides Goto Declaration support. */ - DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions + DeclarationProvider interface{} `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions /*DefinitionProvider defined: * The server provides goto definition support. */ - DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions + DefinitionProvider interface{} `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions /*TypeDefinitionProvider defined: * The server provides Goto Type Definition support. */ - TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions + TypeDefinitionProvider interface{} `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions /*ImplementationProvider defined: * The server provides Goto Implementation support. */ - ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions + ImplementationProvider interface{} `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions /*ReferencesProvider defined: * The server provides find references support. */ - ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions + ReferencesProvider interface{} `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions /*DocumentHighlightProvider defined: * The server provides document highlight support. */ - DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions + DocumentHighlightProvider interface{} `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions /*DocumentSymbolProvider defined: * The server provides document symbol support. */ - DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions + DocumentSymbolProvider interface{} `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions /*CodeActionProvider defined: * The server provides code actions. CodeActionOptions may only be @@ -1048,22 +1046,22 @@ type ServerCapabilities struct { /*ColorProvider defined: * The server provides color provider support. */ - ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions + ColorProvider interface{} `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions /*WorkspaceSymbolProvider defined: * The server provides workspace symbol support. */ - WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions + WorkspaceSymbolProvider interface{} `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions /*DocumentFormattingProvider defined: * The server provides document formatting. */ - DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions + DocumentFormattingProvider interface{} `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions /*DocumentRangeFormattingProvider defined: * The server provides document range formatting. */ - DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions + DocumentRangeFormattingProvider interface{} `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions /*DocumentOnTypeFormattingProvider defined: * The server provides document formatting on typing. @@ -1080,12 +1078,12 @@ type ServerCapabilities struct { /*FoldingRangeProvider defined: * The server provides folding provider support. */ - FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions + FoldingRangeProvider interface{} `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions /*SelectionRangeProvider defined: * The server provides selection range support. */ - SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions + SelectionRangeProvider interface{} `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions /*ExecuteCommandProvider defined: * The server provides execute command support. @@ -3307,7 +3305,7 @@ type CompletionItem struct { /*Documentation defined: * A human-readable string that represents a doc-comment. */ - Documentation MarkupContent `json:"documentation,omitempty"` // string | MarkupContent + Documentation interface{} `json:"documentation,omitempty"` // string | MarkupContent /*Deprecated defined: * Indicates if this item is deprecated. diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go index e5b4893..46438f6 100644 --- a/internal/lsp/protocol/tsserver.go +++ b/internal/lsp/protocol/tsserver.go @@ -1,7 +1,5 @@ package protocol -// Code generated (see typescript/README.md) DO NOT EDIT. - import ( "context" "encoding/json" @@ -11,7 +9,7 @@ import ( "github.com/fhs/acme-lsp/internal/golang_org_x_tools/xcontext" ) -type Server interface { +type server interface { DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error Initialized(context.Context, *InitializedParams) error Exit(context.Context) error @@ -57,6 +55,15 @@ type Server interface { ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) } +type ExtendServer interface { + Metadata(context.Context, *MetadataParams) (*MetaSourceRsponse, error) +} + +type Server interface { + server + ExtendServer +} + func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { if delivered { return false @@ -521,7 +528,18 @@ func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, deliver log.Error(ctx, "", err) } return true + case MetadataEndpoint: // req csharp/metadata + var params MetadataParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Metadata(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Error(ctx, "", err) + } + return true default: return false } @@ -659,11 +677,17 @@ func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSa } func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList, error) { - var result CompletionList - if err := s.Conn.Call(ctx, "textDocument/completion", params, &result); err != nil { + //var result CompletionList + + var items compList + // var items []CompletionItem + + if err := s.Conn.Call(ctx, "textDocument/completion", params, &items); err != nil { return nil, err } - return &result, nil + + res := CompletionList(items) + return &res, nil } func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) (*CompletionItem, error) { @@ -676,6 +700,7 @@ func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover, error) { var result Hover + if err := s.Conn.Call(ctx, "textDocument/hover", params, &result); err != nil { return nil, err } @@ -684,6 +709,7 @@ func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hov func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp, error) { var result SignatureHelp + if err := s.Conn.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil { return nil, err } @@ -692,17 +718,30 @@ func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureH func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) ([]Location, error) { var result Locations + if err := s.Conn.Call(ctx, "textDocument/definition", params, &result); err != nil { return nil, err } + return result, nil } +func (s *serverDispatcher) Metadata(ctx context.Context, params *MetadataParams) (*MetaSourceRsponse, error) { + var result MetaSourceRsponse + + if err := s.Conn.Call(ctx, MetadataEndpoint, params, &result); err != nil { + return nil, err + } + + return &result, nil +} + func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) { var result []Location if err := s.Conn.Call(ctx, "textDocument/references", params, &result); err != nil { return nil, err } + return result, nil } diff --git a/internal/lsp/proxy/server.go b/internal/lsp/proxy/server.go index d0af18f..4d83c73 100644 --- a/internal/lsp/proxy/server.go +++ b/internal/lsp/proxy/server.go @@ -15,7 +15,7 @@ const Version = 1 // Server implements a subset of an LSP protocol server as defined by protocol.Server and // some custom acme-lsp specific methods. -type Server interface { +type subLspServer interface { // Version returns the protocol version. Version(context.Context) (int, error) @@ -50,6 +50,15 @@ type Server interface { TypeDefinition(context.Context, *protocol.TypeDefinitionParams) ([]protocol.Location, error) } +type ExtendServer interface { + Metadata(context.Context, *protocol.MetadataParams) (*protocol.MetaSourceRsponse, error) +} + +type Server interface { + subLspServer + ExtendServer +} + func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { if delivered { return false @@ -101,7 +110,18 @@ func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, deliver log.Error(ctx, "", err) } return true + case protocol.MetadataEndpoint: // req csharp/metadata + var params protocol.MetadataParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Metadata(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Error(ctx, "", err) + } + return true default: return false } @@ -164,6 +184,10 @@ func (s *lspServerDispatcher) Initialized(context.Context, *protocol.Initialized return fmt.Errorf("not implemented") } +func (s *lspServerDispatcher) Metadata(context.Context, *protocol.MetadataParams) (*protocol.MetaSourceRsponse, error) { + return nil, fmt.Errorf("not implemented") +} + func (s *lspServerDispatcher) Exit(context.Context) error { return fmt.Errorf("not implemented") } diff --git a/internal/lsp/text/edit.go b/internal/lsp/text/edit.go index 5c491aa..2b10042 100644 --- a/internal/lsp/text/edit.go +++ b/internal/lsp/text/edit.go @@ -4,6 +4,7 @@ package text import ( "fmt" "io" + "sort" "github.com/fhs/acme-lsp/internal/golang_org_x_tools/span" "github.com/fhs/acme-lsp/internal/lsp/protocol" @@ -24,6 +25,18 @@ type File interface { DisableMark() error } +type byStart []protocol.TextEdit + +func (e byStart) Len() int { return len(e) } +func (e byStart) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e byStart) Less(i, j int) bool { + if e[i].Range.Start.Line == e[j].Range.Start.Line { + return e[i].Range.Start.Character < e[j].Range.Start.Character + } + + return e[i].Range.Start.Line < e[j].Range.Start.Line +} + // Edit applied edits to file f. func Edit(f File, edits []protocol.TextEdit) error { if len(edits) == 0 { @@ -41,6 +54,8 @@ func Edit(f File, edits []protocol.TextEdit) error { f.DisableMark() f.Mark() + sort.Sort(byStart(edits)) + // Applying the edits in reverse order gets the job done. // See https://github.com/golang/go/wiki/gopls#textdocumentformatting-response for i := len(edits) - 1; i >= 0; i-- { @@ -49,6 +64,7 @@ func Edit(f File, edits []protocol.TextEdit) error { q1 := off.LineToOffset(int(e.Range.End.Line), int(e.Range.End.Character)) f.WriteAt(q0, q1, []byte(e.NewText)) } + return nil }