From d49a6cb6ca90d8904f9c85207f6c86f36b7b6dad Mon Sep 17 00:00:00 2001 From: Peter Weinbergr Date: Thu, 10 Sep 2020 11:09:39 -0400 Subject: [PATCH] internal/lsp/protocol: add support for upcoming LSP 3.16 These are the changes needed to support Semantic Tokens in the LSP protocol. Added a function to server.go to make generating server_gen.go work again. Change-Id: I2e09220560b080dd666e25eaf1a39b9960b6f871 Reviewed-on: https://go-review.googlesource.com/c/tools/+/253938 Run-TryBot: Peter Weinberger TryBot-Result: Gobot Gobot Reviewed-by: Rebecca Stambler --- internal/lsp/protocol/tsclient.go | 4 +- internal/lsp/protocol/tsprotocol.go | 82 ++++++++++++++-- internal/lsp/protocol/tsserver.go | 114 ++++++++++++----------- internal/lsp/protocol/typescript/code.ts | 31 ++++-- internal/lsp/protocol/typescript/util.ts | 2 +- internal/lsp/server.go | 4 + internal/lsp/server_gen.go | 6 +- 7 files changed, 169 insertions(+), 74 deletions(-) diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go index 251c74a1e5..d30594b6c6 100644 --- a/internal/lsp/protocol/tsclient.go +++ b/internal/lsp/protocol/tsclient.go @@ -2,8 +2,8 @@ package protocol // Package protocol contains data types and code for LSP jsonrpcs // generated automatically from vscode-languageserver-node -// commit: 399de64448129835b53c7efe8962de91681d6cde -// last fetched Wed Aug 26 2020 20:34:24 GMT-0400 (Eastern Daylight Time) +// commit: 60a5a7825e6f54f57917091f394fd8db7d1724bc +// last fetched Thu Sep 10 2020 09:21:57 GMT-0400 (Eastern Daylight Time) // Code generated (see typescript/README.md) DO NOT EDIT. diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go index 8d774a64a4..13d40c5519 100644 --- a/internal/lsp/protocol/tsprotocol.go +++ b/internal/lsp/protocol/tsprotocol.go @@ -1,7 +1,7 @@ // Package protocol contains data types and code for LSP jsonrpcs // generated automatically from vscode-languageserver-node -// commit: 399de64448129835b53c7efe8962de91681d6cde -// last fetched Wed Aug 26 2020 20:34:24 GMT-0400 (Eastern Daylight Time) +// commit: 60a5a7825e6f54f57917091f394fd8db7d1724bc +// last fetched Thu Sep 10 2020 09:21:57 GMT-0400 (Eastern Daylight Time) package protocol // Code generated (see typescript/README.md) DO NOT EDIT. @@ -390,7 +390,7 @@ type CodeLens struct { */ Command Command `json:"command,omitempty"` /** - * An data entry field that is preserved on a code lens item between + * A data entry field that is preserved on a code lens item between * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest] * (#CodeLensResolveRequest) */ @@ -748,7 +748,7 @@ type CompletionItem struct { */ Command *Command `json:"command,omitempty"` /** - * An data entry field that is preserved on a completion item between + * A data entry field that is preserved on a completion item between * a [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest] * (#CompletionResolveRequest) */ @@ -900,7 +900,7 @@ type CreateFileOptions struct { type Declaration = []Location /*Location | Location[]*/ /** - * Since 3.14.0 + * @since 3.14.0 */ type DeclarationClientCapabilities struct { /** @@ -1862,7 +1862,7 @@ type HoverParams struct { } /** - * Since 3.6.0 + * @since 3.6.0 */ type ImplementationClientCapabilities struct { /** @@ -1874,7 +1874,7 @@ type ImplementationClientCapabilities struct { /** * The client supports additional metadata in the form of definition links. * - * Since 3.14.0 + * @since 3.14.0 */ LinkSupport bool `json:"linkSupport,omitempty"` } @@ -2113,8 +2113,16 @@ type InnerServerCapabilities struct { ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` /** * The server provides Call Hierarchy support. + * + * @since 3.16.0 - Proposed state */ CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"` + /** + * The server provides semantic tokens support. + * + * @since 3.16.0 - Proposed state + */ + SemanticTokensProvider interface{}/*SemanticTokensOptions | SemanticTokensRegistrationOptions*/ `json:"semanticTokensProvider,omitempty"` /** * Experimental server capabilities. */ @@ -2506,6 +2514,12 @@ type RenameClientCapabilities struct { * @since version 3.12.0 */ PrepareSupport bool `json:"prepareSupport,omitempty"` + /** + * Client supports the default behavior result. + * + * @since version 3.16.0 + */ + PrepareSupportDefaultBehavior bool `json:"prepareSupportDefaultBehavior,omitempty"` } /** @@ -2682,7 +2696,8 @@ type SemanticTokensDeltaParams struct { */ TextDocument TextDocumentIdentifier `json:"textDocument"` /** - * The previous result id. + * The result id of a previous response. The result Id can either point to a full response + * or a delta response depending on what was recevied last. */ PreviousResultID string `json:"previousResultId"` WorkDoneProgressParams @@ -2707,6 +2722,40 @@ type SemanticTokensEdit struct { Data []float64 `json:"data,omitempty"` } +/** + * @since 3.16.0 - Proposed state + */ +type SemanticTokensLegend struct { + /** + * The token types a server uses. + */ + TokenTypes []string `json:"tokenTypes"` + /** + * The token modifiers a server uses. + */ + TokenModifiers []string `json:"tokenModifiers"` +} + +/** + * @since 3.16.0 - Proposed state + */ +type SemanticTokensOptions struct { + /** + * The legend used by the server + */ + Legend SemanticTokensLegend `json:"legend"` + /** + * Server supports providing semantic tokens for a sepcific range + * of a document. + */ + Range bool/*boolean | { }*/ `json:"range,omitempty"` + /** + * Server supports providing semantic tokens for a full document. + */ + Full bool/*boolean | */ `json:"full,omitempty"` + WorkDoneProgressOptions +} + /** * @since 3.16.0 - Proposed state */ @@ -2735,6 +2784,15 @@ type SemanticTokensRangeParams struct { PartialResultParams } +/** + * @since 3.16.0 - Proposed state + */ +type SemanticTokensRegistrationOptions struct { + TextDocumentRegistrationOptions + SemanticTokensOptions + StaticRegistrationOptions +} + type ServerCapabilities = struct { /** * Defines how text documents are synced. Is either a detailed structure defining each notification or @@ -2835,8 +2893,16 @@ type ServerCapabilities = struct { ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` /** * The server provides Call Hierarchy support. + * + * @since 3.16.0 - Proposed state */ CallHierarchyProvider interface{}/* bool | CallHierarchyOptions | CallHierarchyRegistrationOptions*/ `json:"callHierarchyProvider,omitempty"` + /** + * The server provides semantic tokens support. + * + * @since 3.16.0 - Proposed state + */ + SemanticTokensProvider interface{}/*SemanticTokensOptions | SemanticTokensRegistrationOptions*/ `json:"semanticTokensProvider,omitempty"` /** * Experimental server capabilities. */ diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go index 29285e057d..d1dada37b1 100644 --- a/internal/lsp/protocol/tsserver.go +++ b/internal/lsp/protocol/tsserver.go @@ -2,8 +2,8 @@ package protocol // Package protocol contains data types and code for LSP jsonrpcs // generated automatically from vscode-languageserver-node -// commit: 399de64448129835b53c7efe8962de91681d6cde -// last fetched Wed Aug 26 2020 20:34:24 GMT-0400 (Eastern Daylight Time) +// commit: 60a5a7825e6f54f57917091f394fd8db7d1724bc +// last fetched Thu Sep 10 2020 09:21:57 GMT-0400 (Eastern Daylight Time) // Code generated (see typescript/README.md) DO NOT EDIT. @@ -18,6 +18,7 @@ import ( type Server interface { DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error WorkDoneProgressCancel(context.Context, *WorkDoneProgressCancelParams) error + SemanticTokensRefresh(context.Context) error Initialized(context.Context, *InitializedParams) error Exit(context.Context) error DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error @@ -39,6 +40,9 @@ type Server interface { PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error) IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error) OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error) + SemanticTokensFull(context.Context, *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) + SemanticTokensFullDelta(context.Context, *SemanticTokensDeltaParams) (interface{} /* SemanticTokens | SemanticTokensDelta | nil*/, error) + SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error) Initialize(context.Context, *ParamInitialize) (*InitializeResult, error) Shutdown(context.Context) error WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error) @@ -60,11 +64,8 @@ type Server interface { RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error) OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error) Rename(context.Context, *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) - PrepareRename(context.Context, *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | null*/, error) + PrepareRename(context.Context, *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/, error) ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /*any | null*/, error) - SemanticTokensFull(context.Context, *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) - SemanticTokensFullDelta(context.Context, *SemanticTokensDeltaParams) (interface{} /* SemanticTokens | SemanticTokensDelta | nil*/, error) - SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) } @@ -84,6 +85,9 @@ func serverDispatch(ctx context.Context, server Server, reply jsonrpc2.Replier, } err := server.WorkDoneProgressCancel(ctx, ¶ms) return true, reply(ctx, nil, err) + case "workspace/semanticTokens/refresh": // notif + err := server.SemanticTokensRefresh(ctx) + return true, reply(ctx, nil, err) case "initialized": // notif var params InitializedParams if err := json.Unmarshal(r.Params(), ¶ms); err != nil { @@ -227,6 +231,27 @@ func serverDispatch(ctx context.Context, server Server, reply jsonrpc2.Replier, } resp, err := server.OutgoingCalls(ctx, ¶ms) return true, reply(ctx, resp, err) + case "textDocument/semanticTokens/full": // req + var params SemanticTokensParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensFull(ctx, ¶ms) + return true, reply(ctx, resp, err) + case "textDocument/semanticTokens/full/delta": // req + var params SemanticTokensDeltaParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensFullDelta(ctx, ¶ms) + return true, reply(ctx, resp, err) + case "textDocument/semanticTokens/range": // req + var params SemanticTokensRangeParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensRange(ctx, ¶ms) + return true, reply(ctx, resp, err) case "initialize": // req var params ParamInitialize if err := json.Unmarshal(r.Params(), ¶ms); err != nil { @@ -387,27 +412,6 @@ func serverDispatch(ctx context.Context, server Server, reply jsonrpc2.Replier, } resp, err := server.ExecuteCommand(ctx, ¶ms) return true, reply(ctx, resp, err) - case "textDocument/semanticTokens/full": // req - var params SemanticTokensParams - if err := json.Unmarshal(r.Params(), ¶ms); err != nil { - return true, sendParseError(ctx, reply, err) - } - resp, err := server.SemanticTokensFull(ctx, ¶ms) - return true, reply(ctx, resp, err) - case "textDocument/semanticTokens/full/delta": // req - var params SemanticTokensDeltaParams - if err := json.Unmarshal(r.Params(), ¶ms); err != nil { - return true, sendParseError(ctx, reply, err) - } - resp, err := server.SemanticTokensFullDelta(ctx, ¶ms) - return true, reply(ctx, resp, err) - case "textDocument/semanticTokens/range": // req - var params SemanticTokensRangeParams - if err := json.Unmarshal(r.Params(), ¶ms); err != nil { - return true, sendParseError(ctx, reply, err) - } - resp, err := server.SemanticTokensRange(ctx, ¶ms) - return true, reply(ctx, resp, err) default: return false, nil @@ -422,6 +426,10 @@ func (s *serverDispatcher) WorkDoneProgressCancel(ctx context.Context, params *W return s.Conn.Notify(ctx, "window/workDoneProgress/cancel", params) } +func (s *serverDispatcher) SemanticTokensRefresh(ctx context.Context) error { + return s.Conn.Notify(ctx, "workspace/semanticTokens/refresh", nil) +} + func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error { return s.Conn.Notify(ctx, "initialized", params) } @@ -545,6 +553,30 @@ func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierar return result, nil } +func (s *serverDispatcher) SemanticTokensFull(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) { + var result *SemanticTokens /*SemanticTokens | null*/ + if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) SemanticTokensFullDelta(ctx context.Context, params *SemanticTokensDeltaParams) (interface{} /* SemanticTokens | SemanticTokensDelta | nil*/, error) { + var result interface{} /* SemanticTokens | SemanticTokensDelta | nil*/ + if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full/delta", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) SemanticTokensRange(ctx context.Context, params *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error) { + var result *SemanticTokens /*SemanticTokens | null*/ + if err := Call(ctx, s.Conn, "textDocument/semanticTokens/range", params, &result); err != nil { + return nil, err + } + return result, nil +} + func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) { var result *InitializeResult if err := Call(ctx, s.Conn, "initialize", params, &result); err != nil { @@ -709,8 +741,8 @@ func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*W return result, nil } -func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | null*/, error) { - var result *Range /*Range | { range: Range, placeholder: string } | null*/ +func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/, error) { + var result *Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/ if err := Call(ctx, s.Conn, "textDocument/prepareRename", params, &result); err != nil { return nil, err } @@ -725,30 +757,6 @@ func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCo return result, nil } -func (s *serverDispatcher) SemanticTokensFull(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) { - var result *SemanticTokens /*SemanticTokens | null*/ - if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full", params, &result); err != nil { - return nil, err - } - return result, nil -} - -func (s *serverDispatcher) SemanticTokensFullDelta(ctx context.Context, params *SemanticTokensDeltaParams) (interface{} /* SemanticTokens | SemanticTokensDelta | nil*/, error) { - var result interface{} /* SemanticTokens | SemanticTokensDelta | nil*/ - if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full/delta", params, &result); err != nil { - return nil, err - } - return result, nil -} - -func (s *serverDispatcher) SemanticTokensRange(ctx context.Context, params *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error) { - var result *SemanticTokens /*SemanticTokens | null*/ - if err := Call(ctx, s.Conn, "textDocument/semanticTokens/range", params, &result); err != nil { - return nil, err - } - return result, nil -} - func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { var result interface{} if err := Call(ctx, s.Conn, method, params, &result); err != nil { diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts index 0bf92650ec..39cea15dec 100644 --- a/internal/lsp/protocol/typescript/code.ts +++ b/internal/lsp/protocol/typescript/code.ts @@ -70,9 +70,13 @@ function findRPCs(node: ts.Node) { const decl: ts.VariableDeclaration = dl.declarations[0]; const name = decl.name.getText() // we want the initializers - if (name == 'method') { // StringLiteral - if (!ts.isStringLiteral(decl.initializer)) - throw new Error(`expect StringLiteral at ${loc(decl)}`); + if (name == 'method') { // mostly StringLiteral but NoSubstitutionTemplateLiteral in protocol.semanticTokens.ts + if (!ts.isStringLiteral(decl.initializer)) { + if (!ts.isNoSubstitutionTemplateLiteral(decl.initializer)) { + console.log(`${decl.initializer.getText()}`); + throw new Error(`expect StringLiteral at ${loc(decl)} got ${strKind(decl.initializer)}`); + } + } rpc = decl.initializer.getText() } else if (name == 'type') { // NewExpression @@ -520,7 +524,7 @@ function goInterface(d: Data, nm: string) { ans = ans.concat(getComments(n)); const json = u.JSON(n); // SelectionRange is a recursive type - let gt = goType(n.type, n.name.getText()); + let gt = goType(n.type, n.name.getText(), nm); if (gt == d.name) gt = '*' + gt; // avoid recursive types // there are several cases where a * is needed starred.forEach(([a, b]) => { @@ -613,12 +617,12 @@ function goTypeAlias(d: Data, nm: string) { } // return a go type and maybe an assocated javascript tag -function goType(n: ts.TypeNode, nm: string): string { +function goType(n: ts.TypeNode, nm: string, parent?: string): string { if (n.getText() == 'T') return 'interface{}'; // should check it's generic if (ts.isTypeReferenceNode(n)) { return goName(n.typeName.getText()); // avoid } else if (ts.isUnionTypeNode(n)) { - return goUnionType(n, nm); + return goUnionType(n, nm, parent); } else if (ts.isIntersectionTypeNode(n)) { return goIntersectionType(n, nm); } else if (strKind(n) == 'StringKeyword') { @@ -659,8 +663,16 @@ function goType(n: ts.TypeNode, nm: string): string { // The choice is uniform interface{}, or some heuristically assigned choice, // or some better sytematic idea I haven't thought of. Using interface{} // is, in practice, impossibly complex in the existing code. -function goUnionType(n: ts.UnionTypeNode, nm: string): string { - const help = `/*${n.getText()}*/` // show the original as a comment +function goUnionType(n: ts.UnionTypeNode, nm: string, parent?: string): string { + let help = `/*${n.getText()}*/` // show the original as a comment + // There are some bad cases with newlines: + // range?: boolean | {\n }; + // full?: boolean | {\n /**\n * The server supports deltas for full documents.\n */\n delta?: boolean;\n } + // These are handled specially: + if (parent == 'SemanticTokensOptions') { + if (nm == 'range') help = help.replace(/\n/, ''); + if (nm == 'full') help = '/*boolean | */'; + } // handle all the special cases switch (n.types.length) { case 2: @@ -725,8 +737,9 @@ function goUnionType(n: ts.UnionTypeNode, nm: string): string { break; case 4: if (nm == 'documentChanges') return `TextDocumentEdit ${help} `; + if (nm == 'textDocument/prepareRename') return `Range ${help} `; default: - throw new Error(`goUnionType ${n.types.length} `) + throw new Error(`goUnionType len=${n.types.length} nm=${nm}`) } // Result will be interface{} with a comment diff --git a/internal/lsp/protocol/typescript/util.ts b/internal/lsp/protocol/typescript/util.ts index 13815d5fb9..77b20233f8 100644 --- a/internal/lsp/protocol/typescript/util.ts +++ b/internal/lsp/protocol/typescript/util.ts @@ -14,7 +14,7 @@ export const fnames = [ `${dir}/${srcDir}/protocol/src/browser/main.ts`, `${dir}${srcDir}/types/src/main.ts`, `${dir}${srcDir}/jsonrpc/src/node/main.ts` ]; -export const gitHash = '399de64448129835b53c7efe8962de91681d6cde' +export const gitHash = '60a5a7825e6f54f57917091f394fd8db7d1724bc' let outFname = 'tsprotocol.go'; let fda: number, fdb: number, fde: number; // file descriptors diff --git a/internal/lsp/server.go b/internal/lsp/server.go index c135b34e5b..dfc2222be4 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -104,6 +104,10 @@ type sentDiagnostics struct { snapshotID uint64 } +func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { + return s.progress.cancel(ctx, params.Token) +} + func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { paramMap := params.(map[string]interface{}) if method == "gopls/diagnoseFiles" { diff --git a/internal/lsp/server_gen.go b/internal/lsp/server_gen.go index 471f1a442f..d22a5eb0c0 100644 --- a/internal/lsp/server_gen.go +++ b/internal/lsp/server_gen.go @@ -176,6 +176,10 @@ func (s *Server) SemanticTokensRange(context.Context, *protocol.SemanticTokensRa return nil, notImplemented("SemanticTokensRange") } +func (s *Server) SemanticTokensRefresh(context.Context) error { + return notImplemented("SemanticTokensRefresh") +} + func (s *Server) SetTrace(context.Context, *protocol.SetTraceParams) error { return notImplemented("SetTrace") } @@ -205,5 +209,5 @@ func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocume } func (s *Server) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { - return s.progress.cancel(ctx, params.Token) + return s.workDoneProgressCancel(ctx, params) }