1
0
mirror of https://github.com/golang/go synced 2024-11-18 15:14:44 -07:00

internal/lsp: upgrade to current version of LSP

There are a number of new RPCs, for CallHeirarchy, SemanticTokens, and
WorkDoneProgress. Some of the messages now have two slashes, like
'textDocument/semanticTokens/edits'. A few unused RegistrationOptions
are no longer present.

Only generated code has changed. There are no changes any other .go
code.

The typescript code needed a new heuristic for finding the RPCs

Change-Id: Ie6942abac3a0cd60e86afe3fdbc6f6b0e9b30cb0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/216537
Run-TryBot: Peter Weinberger <pjw@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Peter Weinberger 2020-01-27 08:23:25 -05:00
parent d33eef8e68
commit 91d59a9776
8 changed files with 660 additions and 1779 deletions

View File

@ -2,8 +2,8 @@ package protocol
// Package protocol contains data types and code for LSP jsonrpcs // Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node // generated automatically from vscode-languageserver-node
// commit: 635ab1fe6f8c57ce9402e573d007f24d6d290fd3 // commit: 7b90c29d0cb5cd7b9c41084f6cb3781a955adeba
// last fetched Fri Jan 10 2020 17:17:33 GMT-0500 (Eastern Standard Time) // last fetched Thu Jan 23 2020 11:10:31 GMT-0500 (Eastern Standard Time)
// Code generated (see typescript/README.md) DO NOT EDIT. // Code generated (see typescript/README.md) DO NOT EDIT.

View File

@ -1,7 +1,7 @@
// Package protocol contains data types and code for LSP jsonrpcs // Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node // generated automatically from vscode-languageserver-node
// commit: 635ab1fe6f8c57ce9402e573d007f24d6d290fd3 // commit: 7b90c29d0cb5cd7b9c41084f6cb3781a955adeba
// last fetched Fri Jan 10 2020 17:17:33 GMT-0500 (Eastern Standard Time) // last fetched Thu Jan 23 2020 11:10:31 GMT-0500 (Eastern Standard Time)
package protocol package protocol
// Code generated (see typescript/README.md) DO NOT EDIT. // Code generated (see typescript/README.md) DO NOT EDIT.
@ -44,6 +44,111 @@ type ApplyWorkspaceEditResponse struct {
FailedChange float64 `json:"failedChange,omitempty"` FailedChange float64 `json:"failedChange,omitempty"`
} }
/**
* Represents an incoming call, e.g. a caller of a method or constructor.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyIncomingCall struct {
/**
* The item that makes the call.
*/
From CallHierarchyItem `json:"from"`
/**
* The range at which at which the calls appears. This is relative to the caller
* denoted by [`this.from`](#CallHierarchyIncomingCall.from).
*/
FromRanges []Range `json:"fromRanges"`
}
/**
* The parameter of a `callHierarchy/incomingCalls` request.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyIncomingCallsParams struct {
Item CallHierarchyItem `json:"item"`
WorkDoneProgressParams
PartialResultParams
}
/**
* Represents programming constructs like functions or constructors in the context
* of call hierarchy.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyItem struct {
/**
* The name of this item.
*/
Name string `json:"name"`
/**
* The kind of this item.
*/
Kind SymbolKind `json:"kind"`
/**
* Tags for this item.
*/
Tags []SymbolTag `json:"tags,omitempty"`
/**
* More detail for this item, e.g. the signature of a function.
*/
Detail string `json:"detail,omitempty"`
/**
* The resource identifier of this item.
*/
URI DocumentURI `json:"uri"`
/**
* The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code.
*/
Range Range `json:"range"`
/**
* The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function.
* Must be contained by the [`range`](#CallHierarchyItem.range).
*/
SelectionRange Range `json:"selectionRange"`
}
/**
* Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyOutgoingCall struct {
/**
* The item that is called.
*/
To CallHierarchyItem `json:"to"`
/**
* The range at which this item is called. This is the range relative to the caller, e.g the item
* passed to [`provideCallHierarchyOutgoingCalls`](#CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls)
* and not [`this.to`](#CallHierarchyOutgoingCall.to).
*/
FromRanges []Range `json:"fromRanges"`
}
/**
* The parameter of a `callHierarchy/outgoingCalls` request.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyOutgoingCallsParams struct {
Item CallHierarchyItem `json:"item"`
WorkDoneProgressParams
PartialResultParams
}
/**
* The parameter of a `textDocument/prepareCallHierarchy` request.
*
* @since 3.16.0 - Proposed state
*/
type CallHierarchyPrepareParams struct {
TextDocumentPositionParams
WorkDoneProgressParams
}
type CancelParams struct { type CancelParams struct {
/** /**
* The request id to cancel. * The request id to cancel.
@ -70,20 +175,23 @@ type ClientCapabilities = struct {
* Text document specific client capabilities. * Text document specific client capabilities.
*/ */
TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"` TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
/** Window struct {
* Window specific client capabilities. /**
*/ * Window specific client capabilities.
Window interface{} `json:"window,omitempty"` */
Window interface{} `json:"window,omitempty"`
/**
* Whether client supports handling progress notifications. If set servers are allowed to
* report in `workDoneProgress` property in the request specific server capabilities.
*
* Since 3.15.0
*/
WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
}
/** /**
* Experimental client capabilities. * Experimental client capabilities.
*/ */
Experimental interface{} `json:"experimental,omitempty"` Experimental interface{} `json:"experimental,omitempty"`
/**
* Whether implementation supports dynamic registration for selection range providers. If this is set to `true`
* the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server
* capability as well.
*/
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
} }
/** /**
@ -541,7 +649,8 @@ type CompletionItem struct {
InsertText string `json:"insertText,omitempty"` InsertText string `json:"insertText,omitempty"`
/** /**
* The format of the insert text. The format applies to both the `insertText` property * The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`. * and the `newText` property of a provided `textEdit`. If ommitted defaults to
* `InsertTextFormat.PlainText`.
*/ */
InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"`
/** /**
@ -631,6 +740,9 @@ type CompletionOptions struct {
* if clients don't support individual commmit characters per completion item. See * if clients don't support individual commmit characters per completion item. See
* `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport`
* *
* If a server provides both `allCommitCharacters` and commit characters on an individual
* completion item the ones on the completion item win.
*
* @since 3.2.0 * @since 3.2.0
*/ */
AllCommitCharacters []string `json:"allCommitCharacters,omitempty"` AllCommitCharacters []string `json:"allCommitCharacters,omitempty"`
@ -934,10 +1046,6 @@ type DidChangeConfigurationParams struct {
Settings interface{} `json:"settings"` Settings interface{} `json:"settings"`
} }
type DidChangeConfigurationRegistrationOptions struct {
Section []string /*string | string[]*/ `json:"section,omitempty"`
}
/** /**
* The change text document notification's parameters. * The change text document notification's parameters.
*/ */
@ -950,8 +1058,16 @@ type DidChangeTextDocumentParams struct {
TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
/** /**
* The actual content changes. The content changes describe single state changes * The actual content changes. The content changes describe single state changes
* to the document. So if there are two content changes c1 and c2 for a document * to the document. So if there are two content changes c1 (at array index 0) and
* in state S then c1 move the document to S' and c2 to S''. * c2 (at array index 1) for a document in state S then c1 moves the document from
* S to S' and c2 from S' to S''. So c1 is computed on the state S and c2 is computed
* on the state S'.
*
* To mirror the content of a document using change events use the following approach:
* - start with the same initial content
* - apply the 'textDocument/didChange' notifications in the order you recevie them.
* - apply the `TextDocumentContentChangeEvent`s in a single notification in the order
* you receive them.
*/ */
ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"`
} }
@ -1040,10 +1156,6 @@ type DocumentColorClientCapabilities struct {
} }
type DocumentColorOptions struct { type DocumentColorOptions struct {
/**
* Code lens has a resolve provider as well.
*/
ResolveProvider bool `json:"resolveProvider,omitempty"`
WorkDoneProgressOptions WorkDoneProgressOptions
} }
@ -2134,6 +2246,13 @@ type PublishDiagnosticsClientCapabilities struct {
*/ */
ValueSet []DiagnosticTag `json:"valueSet"` ValueSet []DiagnosticTag `json:"valueSet"`
} `json:"tagSupport,omitempty"` } `json:"tagSupport,omitempty"`
/**
* Whether the client interprets the version property of the
* `textDocument/publishDiagnostics` notification`s parameter.
*
* @since 3.15.0
*/
VersionSupport bool `json:"versionSupport,omitempty"`
} }
/** /**
@ -2392,6 +2511,90 @@ type SelectionRangeRegistrationOptions struct {
StaticRegistrationOptions StaticRegistrationOptions
} }
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokens struct {
/**
* An optional result id. If provided and clients support delta updating
* the client will include the result id in the next semantic token request.
* A server can then instead of computing all sematic tokens again simply
* send a delta.
*/
ResultID string `json:"resultId,omitempty"`
/**
* The actual tokens. For a detailed description about how the data is
* structured pls see
* https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L71
*/
Data []float64 `json:"data"`
}
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokensEdit struct {
Start float64 `json:"start"`
DeleteCount float64 `json:"deleteCount"`
Data []float64 `json:"data,omitempty"`
}
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokensEdits struct {
ResultID string `json:"resultId,omitempty"`
/**
* For a detailed description how these edits are structured pls see
* https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L131
*/
Edits []SemanticTokensEdit `json:"edits"`
}
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokensEditsParams struct {
/**
* The text document.
*/
TextDocument TextDocumentIdentifier `json:"textDocument"`
/**
* The previous result id.
*/
PreviousResultID string `json:"previousResultId"`
WorkDoneProgressParams
PartialResultParams
}
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokensParams struct {
/**
* The text document.
*/
TextDocument TextDocumentIdentifier `json:"textDocument"`
WorkDoneProgressParams
PartialResultParams
}
/**
* @since 3.16.0 - Proposed state
*/
type SemanticTokensRangeParams struct {
/**
* The text document.
*/
TextDocument TextDocumentIdentifier `json:"textDocument"`
/**
* The range the semantic tokens are requested for.
*/
Range Range `json:"range"`
WorkDoneProgressParams
PartialResultParams
}
type ServerCapabilities = struct { type ServerCapabilities = struct {
/** /**
* Defines how text documents are synced. Is either a detailed structure defining each notification or * Defines how text documents are synced. Is either a detailed structure defining each notification or
@ -2749,15 +2952,10 @@ type SymbolInformation struct {
type SymbolKind float64 type SymbolKind float64
/** /**
* Describe options to be used when registered for text document change events. * Symbol tags are extra annotations that tweak the rendering of a symbol.
* @since 3.15
*/ */
type TextDocumentChangeRegistrationOptions struct { type SymbolTag float64
/**
* How documents are synced to the server.
*/
SyncKind TextDocumentSyncKind `json:"syncKind"`
TextDocumentRegistrationOptions
}
/** /**
* Text document specific client capabilities. * Text document specific client capabilities.
@ -2867,23 +3065,28 @@ type TextDocumentClientCapabilities struct {
* An event describing a change to a text document. If range and rangeLength are omitted * An event describing a change to a text document. If range and rangeLength are omitted
* the new text is considered to be the full content of the document. * the new text is considered to be the full content of the document.
*/ */
type TextDocumentContentChangeEvent struct { type TextDocumentContentChangeEvent = struct {
/** /**
* The range of the document that changed. * The range of the document that changed.
*/ */
Range *Range `json:"range,omitempty"` Range *Range `json:"range,omitempty"`
/** /**
* The length of the range that got replaced. * The optional length of the range that got replaced.
*
* @deprecated use range instead.
*/ */
RangeLength float64 `json:"rangeLength,omitempty"` RangeLength float64 `json:"rangeLength,omitempty"`
/** /**
* The new text of the document. * The new text for the provided range.
*/ */
Text string `json:"text"` Text string `json:"text"`
} }
/** /**
* Describes textual changes on a text document. * Describes textual changes on a text document. A TextDocumentEdit describes all changes
* on a document version Si and after they are applied move the document to version Si+1.
* So the creator of a TextDocumentEdit doesn't need to sort the array of edits or do any
* kind of ordering. However the edits must be non overlapping.
*/ */
type TextDocumentEdit struct { type TextDocumentEdit struct {
/** /**
@ -2961,14 +3164,6 @@ type TextDocumentRegistrationOptions struct {
*/ */
type TextDocumentSaveReason float64 type TextDocumentSaveReason float64
/**
* Save registration options.
*/
type TextDocumentSaveRegistrationOptions struct {
TextDocumentRegistrationOptions
SaveOptions
}
type TextDocumentSyncClientCapabilities struct { type TextDocumentSyncClientCapabilities struct {
/** /**
* Whether text document synchronization supports dynamic registration. * Whether text document synchronization supports dynamic registration.
@ -3126,6 +3321,35 @@ type WillSaveTextDocumentParams struct {
Reason TextDocumentSaveReason `json:"reason"` Reason TextDocumentSaveReason `json:"reason"`
} }
type WorkDoneProgressCancelParams struct {
/**
* The token to be used to report progress.
*/
Token ProgressToken `json:"token"`
}
type WorkDoneProgressClientCapabilities struct {
/**
* Window specific client capabilities.
*/
Window struct {
/**
* Whether client supports handling progress notifications. If set servers are allowed to
* report in `workDoneProgress` property in the request specific server capabilities.
*
* Since 3.15.0
*/
WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
} `json:"window,omitempty"`
}
type WorkDoneProgressCreateParams struct {
/**
* The token to be used to report progress.
*/
Token ProgressToken `json:"token"`
}
type WorkDoneProgressOptions struct { type WorkDoneProgressOptions struct {
WorkDoneProgress bool `json:"workDoneProgress,omitempty"` WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
} }
@ -3222,7 +3446,7 @@ type WorkspaceFolder struct {
URI string `json:"uri"` URI string `json:"uri"`
/** /**
* The name of the workspace folder. Used to refer to this * The name of the workspace folder. Used to refer to this
* workspace folder in thge user interface. * workspace folder in the user interface.
*/ */
Name string `json:"name"` Name string `json:"name"`
} }
@ -3374,7 +3598,17 @@ const (
* Base kind for an organize imports source action: `source.organizeImports` * Base kind for an organize imports source action: `source.organizeImports`
*/ */
SourceOrganizeImports CodeActionKind = "source.organizeImports" SourceOrganizeImports CodeActionKind = "source.organizeImports"
/**
* Base kind for auto-fix source actions: `source.fixAll`.
*
* Fix all actions automatically fix errors that have a clear fix that do not require user input.
* They should not suppress errors or perform unsafe fixes such as generating new types or classes.
*
* @since 3.15.0
*/
SourceFixAll CodeActionKind = "source.fixAll"
TextCompletion CompletionItemKind = 1 TextCompletion CompletionItemKind = 1
MethodCompletion CompletionItemKind = 2 MethodCompletion CompletionItemKind = 2
FunctionCompletion CompletionItemKind = 3 FunctionCompletion CompletionItemKind = 3
@ -3493,7 +3727,7 @@ const (
TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional"
/** /**
* The client tries to undo the operations already executed. But there is no * The client tries to undo the operations already executed. But there is no
* guaruntee that this is succeeding. * guarantee that this is succeeding.
*/ */
Undo FailureHandlingKind = "undo" Undo FailureHandlingKind = "undo"
@ -3634,6 +3868,11 @@ const (
Event SymbolKind = 24 Event SymbolKind = 24
Operator SymbolKind = 25 Operator SymbolKind = 25
TypeParameter SymbolKind = 26 TypeParameter SymbolKind = 26
/**
* Render a symbol as obsolete, usually using a strike-out.
*/
DeprecatedSymbol SymbolTag = 1
/** /**
* Manually triggered, e.g. by the user pressing save, by starting debugging, * Manually triggered, e.g. by the user pressing save, by starting debugging,
* or by an API call. * or by an API call.

View File

@ -2,8 +2,8 @@ package protocol
// Package protocol contains data types and code for LSP jsonrpcs // Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node // generated automatically from vscode-languageserver-node
// commit: 635ab1fe6f8c57ce9402e573d007f24d6d290fd3 // commit: 7b90c29d0cb5cd7b9c41084f6cb3781a955adeba
// last fetched Fri Jan 10 2020 17:17:33 GMT-0500 (Eastern Standard Time) // last fetched Thu Jan 23 2020 11:10:31 GMT-0500 (Eastern Standard Time)
// Code generated (see typescript/README.md) DO NOT EDIT. // Code generated (see typescript/README.md) DO NOT EDIT.
@ -18,6 +18,7 @@ import (
type Server interface { type Server interface {
DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error
WorkDoneProgressCancel(context.Context, *WorkDoneProgressCancelParams) error
Initialized(context.Context, *InitializedParams) error Initialized(context.Context, *InitializedParams) error
Exit(context.Context) error Exit(context.Context) error
DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error
@ -37,6 +38,7 @@ type Server interface {
FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange /*FoldingRange[] | null*/, error) FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange /*FoldingRange[] | null*/, error)
Declaration(context.Context, *DeclarationParams) (Declaration /*Declaration | DeclarationLink[] | null*/, error) Declaration(context.Context, *DeclarationParams) (Declaration /*Declaration | DeclarationLink[] | null*/, error)
SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange /*SelectionRange[] | null*/, error) SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange /*SelectionRange[] | null*/, error)
WorkDoneProgressCreate(context.Context, *WorkDoneProgressCreateParams) error
Initialize(context.Context, *ParamInitialize) (*InitializeResult, error) Initialize(context.Context, *ParamInitialize) (*InitializeResult, error)
Shutdown(context.Context) error Shutdown(context.Context) error
WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error) WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error)
@ -60,6 +62,12 @@ type Server interface {
Rename(context.Context, *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) Rename(context.Context, *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error)
PrepareRename(context.Context, *PrepareRenameParams) (interface{} /* Range | struct{; Range Range`json:"range"`; Placeholder string`json:"placeholder"`; } | nil*/, error) PrepareRename(context.Context, *PrepareRenameParams) (interface{} /* Range | struct{; Range Range`json:"range"`; Placeholder string`json:"placeholder"`; } | nil*/, error)
ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /*any | null*/, error) ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /*any | null*/, error)
PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error)
IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error)
OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error)
SemanticTokens(context.Context, *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error)
SemanticTokensEdits(context.Context, *SemanticTokensEditsParams) (interface{} /* SemanticTokens | SemanticTokensEdits | nil*/, error)
SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error)
NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)
} }
@ -83,6 +91,16 @@ func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, deliver
log.Error(ctx, "", err) log.Error(ctx, "", err)
} }
return true return true
case "window/workDoneProgress/cancel": // notif
var params WorkDoneProgressCancelParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
if err := h.server.WorkDoneProgressCancel(ctx, &params); err != nil {
log.Error(ctx, "", err)
}
return true
case "initialized": // notif case "initialized": // notif
var params InitializedParams var params InitializedParams
if err := json.Unmarshal(*r.Params, &params); err != nil { if err := json.Unmarshal(*r.Params, &params); err != nil {
@ -275,6 +293,17 @@ func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, deliver
log.Error(ctx, "", err) log.Error(ctx, "", err)
} }
return true return true
case "window/workDoneProgress/create": // req
var params WorkDoneProgressCreateParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
err := h.server.WorkDoneProgressCreate(ctx, &params)
if err := r.Reply(ctx, nil, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "initialize": // req case "initialize": // req
var params ParamInitialize var params ParamInitialize
if err := json.Unmarshal(*r.Params, &params); err != nil { if err := json.Unmarshal(*r.Params, &params); err != nil {
@ -527,6 +556,72 @@ func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, deliver
log.Error(ctx, "", err) log.Error(ctx, "", err)
} }
return true return true
case "textDocument/prepareCallHierarchy": // req
var params CallHierarchyPrepareParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.PrepareCallHierarchy(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "callHierarchy/incomingCalls": // req
var params CallHierarchyIncomingCallsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.IncomingCalls(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "callHierarchy/outgoingCalls": // req
var params CallHierarchyOutgoingCallsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.OutgoingCalls(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "textDocument/semanticTokens": // req
var params SemanticTokensParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.SemanticTokens(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "textDocument/semanticTokens/edits": // req
var params SemanticTokensEditsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.SemanticTokensEdits(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
case "textDocument/semanticTokens/range": // req
var params SemanticTokensRangeParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
resp, err := h.server.SemanticTokensRange(ctx, &params)
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true
default: default:
var params interface{} var params interface{}
if err := json.Unmarshal(*r.Params, &params); err != nil { if err := json.Unmarshal(*r.Params, &params); err != nil {
@ -550,6 +645,10 @@ func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params
return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params) return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params)
} }
func (s *serverDispatcher) WorkDoneProgressCancel(ctx context.Context, params *WorkDoneProgressCancelParams) error {
return s.Conn.Notify(ctx, "window/workDoneProgress/cancel", params)
}
func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error { func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error {
return s.Conn.Notify(ctx, "initialized", params) return s.Conn.Notify(ctx, "initialized", params)
} }
@ -653,6 +752,10 @@ func (s *serverDispatcher) SelectionRange(ctx context.Context, params *Selection
return result, nil return result, nil
} }
func (s *serverDispatcher) WorkDoneProgressCreate(ctx context.Context, params *WorkDoneProgressCreateParams) error {
return s.Conn.Call(ctx, "window/workDoneProgress/create", params, nil) // Call, not Notify
}
func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) { func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) {
var result InitializeResult var result InitializeResult
if err := s.Conn.Call(ctx, "initialize", params, &result); err != nil { if err := s.Conn.Call(ctx, "initialize", params, &result); err != nil {
@ -833,6 +936,54 @@ func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCo
return result, nil return result, nil
} }
func (s *serverDispatcher) PrepareCallHierarchy(ctx context.Context, params *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error) {
var result []CallHierarchyItem /*CallHierarchyItem[] | null*/
if err := s.Conn.Call(ctx, "textDocument/prepareCallHierarchy", params, &result); err != nil {
return nil, err
}
return result, nil
}
func (s *serverDispatcher) IncomingCalls(ctx context.Context, params *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error) {
var result []CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/
if err := s.Conn.Call(ctx, "callHierarchy/incomingCalls", params, &result); err != nil {
return nil, err
}
return result, nil
}
func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error) {
var result []CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/
if err := s.Conn.Call(ctx, "callHierarchy/outgoingCalls", params, &result); err != nil {
return nil, err
}
return result, nil
}
func (s *serverDispatcher) SemanticTokens(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) {
var result SemanticTokens /*SemanticTokens | null*/
if err := s.Conn.Call(ctx, "textDocument/semanticTokens", params, &result); err != nil {
return nil, err
}
return &result, nil
}
func (s *serverDispatcher) SemanticTokensEdits(ctx context.Context, params *SemanticTokensEditsParams) (interface{} /* SemanticTokens | SemanticTokensEdits | nil*/, error) {
var result interface{} /* SemanticTokens | SemanticTokensEdits | nil*/
if err := s.Conn.Call(ctx, "textDocument/semanticTokens/edits", 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 := s.Conn.Call(ctx, "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) { func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
var result interface{} var result interface{}
if err := s.Conn.Call(ctx, method, params, &result); err != nil { if err := s.Conn.Call(ctx, method, params, &result); err != nil {

View File

@ -19,15 +19,15 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as ts from 'typescript'; import * as ts from 'typescript';
import * as u from './util'; import * as u from './util';
import {constName, getComments, goName, loc, strKind} from './util'; import { constName, getComments, goName, loc, strKind } from './util';
var program: ts.Program; var program: ts.Program;
function parse() { function parse() {
// this won't complain if some fnames don't exist // this won't complain if some fnames don't exist
program = ts.createProgram( program = ts.createProgram(
u.fnames, u.fnames,
{target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.CommonJS}); { target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.CommonJS });
program.getTypeChecker(); // finish type checking and assignment program.getTypeChecker(); // finish type checking and assignment
} }
@ -35,61 +35,73 @@ function parse() {
let req = new Map<string, ts.NewExpression>(); // requests let req = new Map<string, ts.NewExpression>(); // requests
let not = new Map<string, ts.NewExpression>(); // notifications let not = new Map<string, ts.NewExpression>(); // notifications
let ptypes = new Map<string, [ts.TypeNode, ts.TypeNode]>(); // req, resp types let ptypes = new Map<string, [ts.TypeNode, ts.TypeNode]>(); // req, resp types
let receives = new Map<string, 'server'|'client'>(); // who receives it let receives = new Map<string, 'server' | 'client'>(); // who receives it
let rpcTypes = new Set<string>(); // types seen in the rpcs let rpcTypes = new Set<string>(); // types seen in the rpcs
// walk the AST finding Requests and Notifications function findRPCs(node: ts.Node) {
function findNews(node: ts.Node) { if (!ts.isModuleDeclaration(node)) {
if (!ts.isNewExpression(node)) { return
ts.forEachChild(node, findNews) } if (!ts.isIdentifier(node.name)) {
return; throw new Error(`expected Identifier, got ${strKind(node.name)} at ${loc(node)}`)
} }
const wh = node.expression.getText(); let reqnot = req
// We only need the bare ones and the ones ending with 0 let v = node.name.getText()
if (wh != 'RequestType' && wh != 'RequestType0' && wh != 'NotificationType' && if (v.endsWith('Notification')) reqnot = not;
wh != 'NotificationType0') else if (!v.endsWith('Request')) return;
return;
if (!node.arguments || node.arguments.length != 1 || if (!ts.isModuleBlock(node.body)) {
!ts.isStringLiteral(node.arguments[0])) { throw new Error(`expected ModuleBody got ${strKind(node.body)} at ${loc(node)}`)
throw new Error(`expected n.arguments ${loc(node)}`)
} }
// RequestType<useful>=new RequestTYpe('foo') let x: ts.ModuleBlock = node.body
if (!node.typeArguments) { // The story is to expect const method = 'textDocument/implementation'
node.typeArguments = lookUp(node); // const type = new ProtocolRequestType<...>(method)
} // but the method may be an explicit string
// new RequestType<useful> let rpc: string = '';
let s = node.arguments[0].getText(); let newNode: ts.NewExpression;
// Request or Notification for (let i = 0; i < x.statements.length; i++) {
const v = wh[0] == 'R' ? req : not; const uu = x.statements[i]
s = s.substring(1, s.length - 1); // remove quoting (e.g., saw 'exit') if (!ts.isVariableStatement(uu)) continue;
v.set(s, node); const dl: ts.VariableDeclarationList = uu.declarationList
// Summary: if (dl.declarations.length != 1) throw new Error(`expected a single decl at ${loc(dl)}`)
// node.expression == 'RequestType', typeArg[0] is request type, const decl: ts.VariableDeclaration = dl.declarations[0]
// typeArg[1] is response type, and args[0] is the rpc name const name = decl.name.getText()
// node.espression == 'RequestType0', typeArgs[0] is the response type, // we want the initializers
// the request type is null, and args[0] is the rpc name if (name == 'method') { // StringLiteral
// node.expression == 'NotificationType', typeArgs[0] the request, args[0] the if (!ts.isStringLiteral(decl.initializer)) throw new Error(`expect StringLiteral at ${loc(decl)}`)
// rpc NotificationType0 is the same (but it's always void) rpc = decl.initializer.getText()
const nm = node.expression.getText(); } else if (name == 'type') { // NewExpression
const rpc = node.arguments[0].getText(); if (!ts.isNewExpression(decl.initializer)) throw new Error(`expecte new at ${loc(decl)}`)
if (nm == 'RequestType' || nm == 'NotificationType') { const nn: ts.NewExpression = decl.initializer
ptypes.set(rpc, [node.typeArguments[0], node.typeArguments[1]]); newNode = nn
} else if (nm == 'RequestType0') { const mtd = nn.arguments[0]
ptypes.set(rpc, [node.typeArguments[0], node.typeArguments[1]]); if (ts.isStringLiteral(mtd)) rpc = mtd.getText();
// that looks the same, but it's a way of getting VoidKeyword switch (nn.typeArguments.length) {
} else if (nm == 'NotificationType0') { case 1: // exit
ptypes.set(rpc, [node.typeArguments[0], node.typeArguments[0]]) ptypes.set(rpc, [nn.typeArguments[0], null])
// both VoidKeyword break;
} else { case 2: // notifications
throw new Error(`FATAL: ${nm} not an expected RPC type`) ptypes.set(rpc, [nn.typeArguments[0], null])
break;
case 4:// request with no parameters
ptypes.set(rpc, [null, nn.typeArguments[0]])
break;
case 5: // request req, resp, partial(?)
ptypes.set(rpc, [nn.typeArguments[0], nn.typeArguments[1]])
break;
default: throw new Error(`${nn.typeArguments.length} at ${loc(nn)}`)
}
}
} }
if (rpc == '') throw new Error(`no name found at ${loc(x)}`)
// remember the implied types // remember the implied types
const [a, b] = ptypes.get(rpc); const [a, b] = ptypes.get(rpc);
const add = function(n: ts.Node) { const add = function (n: ts.Node) {
rpcTypes.add(goName(n.getText())) rpcTypes.add(goName(n.getText()))
}; };
underlying(a, add); underlying(a, add);
underlying(b, add); underlying(b, add);
rpc = rpc.substring(1, rpc.length - 1) // 'exit'
reqnot.set(rpc, newNode)
} }
// handle missing typeArguments // handle missing typeArguments
@ -110,8 +122,8 @@ function setReceives() {
// it would be nice to have some independent check on this // it would be nice to have some independent check on this
// (this logic fails if the server ever sends $/canceRequest // (this logic fails if the server ever sends $/canceRequest
// or $/progress) // or $/progress)
req.forEach((_, k) => {receives.set(k, 'server')}); req.forEach((_, k) => { receives.set(k, 'server') });
not.forEach((_, k) => {receives.set(k, 'server')}); not.forEach((_, k) => { receives.set(k, 'server') });
receives.set('window/showMessage', 'client'); receives.set('window/showMessage', 'client');
receives.set('window/showMessageRequest', 'client'); receives.set('window/showMessageRequest', 'client');
receives.set('window/logMessage', 'client'); receives.set('window/logMessage', 'client');
@ -146,22 +158,22 @@ interface Data {
function newData(n: ts.Node, nm: string): Data { function newData(n: ts.Node, nm: string): Data {
return { return {
me: n, name: goName(nm), me: n, name: goName(nm),
generics: ts.createNodeArray<ts.TypeParameterDeclaration>(), as: ts.createNodeArray<ts.HeritageClause>(), generics: ts.createNodeArray<ts.TypeParameterDeclaration>(), as: ts.createNodeArray<ts.HeritageClause>(),
properties: ts.createNodeArray<ts.TypeElement>(), alias: undefined, properties: ts.createNodeArray<ts.TypeElement>(), alias: undefined,
statements: ts.createNodeArray<ts.Statement>(), statements: ts.createNodeArray<ts.Statement>(),
enums: ts.createNodeArray<ts.EnumMember>(), enums: ts.createNodeArray<ts.EnumMember>(),
members: ts.createNodeArray<ts.PropertyDeclaration>(), members: ts.createNodeArray<ts.PropertyDeclaration>(),
} }
} }
// for debugging, produce a skeleton description // for debugging, produce a skeleton description
function strData(d: Data): string { function strData(d: Data): string {
const f = function(na: ts.NodeArray<any>): number { const f = function (na: ts.NodeArray<any>): number {
return na.length return na.length
}; };
return `D(${d.name}) g;${f(d.generics)} a:${f(d.as)} p:${f(d.properties)} s:${ return `D(${d.name}) g;${f(d.generics)} a:${f(d.as)} p:${f(d.properties)} s:${
f(d.statements)} e:${f(d.enums)} m:${f(d.members)} ${ f(d.statements)} e:${f(d.enums)} m:${f(d.members)} ${
d.alias != undefined}` d.alias != undefined}`
} }
let data = new Map<string, Data>(); // parsed data types let data = new Map<string, Data>(); // parsed data types
@ -172,16 +184,16 @@ let extraTypes = new Map<string, string[]>(); // to avoid struct params
function genTypes(node: ts.Node) { function genTypes(node: ts.Node) {
// Ignore top-level items that can't produce output // Ignore top-level items that can't produce output
if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) || if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
ts.isImportDeclaration(node) || ts.isVariableStatement(node) || ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
ts.isExportDeclaration(node) || ts.isEmptyStatement(node) || ts.isExportDeclaration(node) || ts.isEmptyStatement(node) ||
node.kind == ts.SyntaxKind.EndOfFileToken) { node.kind == ts.SyntaxKind.EndOfFileToken) {
return; return;
} }
if (ts.isInterfaceDeclaration(node)) { if (ts.isInterfaceDeclaration(node)) {
const v: ts.InterfaceDeclaration = node; const v: ts.InterfaceDeclaration = node;
// need to check the members, many of which are disruptive // need to check the members, many of which are disruptive
let mems: ts.TypeElement[] = []; let mems: ts.TypeElement[] = [];
const f = function(t: ts.TypeElement) { const f = function (t: ts.TypeElement) {
if (ts.isPropertySignature(t)) { if (ts.isPropertySignature(t)) {
mems.push(t); mems.push(t);
} else if (ts.isMethodSignature(t) || ts.isCallSignatureDeclaration(t)) { } else if (ts.isMethodSignature(t) || ts.isCallSignatureDeclaration(t)) {
@ -196,7 +208,7 @@ function genTypes(node: ts.Node) {
}; };
v.members.forEach(f); v.members.forEach(f);
if (mems.length == 0 && !v.heritageClauses && if (mems.length == 0 && !v.heritageClauses &&
v.name.getText() != 'InitializedParams') { v.name.getText() != 'InitializedParams') {
return // really? (Don't seem to need any of these) return // really? (Don't seem to need any of these)
}; };
// Found one we want // Found one we want
@ -220,7 +232,7 @@ function genTypes(node: ts.Node) {
// (at the top level) // (at the top level)
// Unfortunately this is false for TraceValues // Unfortunately this is false for TraceValues
if (ts.isUnionTypeNode(v.type) && if (ts.isUnionTypeNode(v.type) &&
v.type.types.every((n: ts.TypeNode) => ts.isLiteralTypeNode(n))) { v.type.types.every((n: ts.TypeNode) => ts.isLiteralTypeNode(n))) {
if (x.name != 'TraceValues') return; if (x.name != 'TraceValues') return;
} }
if (v.typeParameters) { if (v.typeParameters) {
@ -239,15 +251,15 @@ function genTypes(node: ts.Node) {
const b: ts.ModuleBlock = v.body; const b: ts.ModuleBlock = v.body;
var s: ts.Statement[] = []; var s: ts.Statement[] = [];
// we don't want most of these // we don't want most of these
const fx = function(x: ts.Statement) { const fx = function (x: ts.Statement) {
if (ts.isFunctionDeclaration(x)) { if (ts.isFunctionDeclaration(x)) {
return return
}; };
if (ts.isTypeAliasDeclaration(x)) { if (ts.isTypeAliasDeclaration(x) || ts.isModuleDeclaration(x)) {
return return
}; };
if (!ts.isVariableStatement(x)) if (!ts.isVariableStatement(x))
throw new Error(`${loc(x)} ${strKind(x)}`); throw new Error(`expected VariableStatment ${loc(x)} ${strKind(x)} ${x.getText()}`);
if (hasNewExpression(x)) { if (hasNewExpression(x)) {
return return
}; };
@ -273,7 +285,7 @@ function genTypes(node: ts.Node) {
const v: ts.ClassDeclaration = node; const v: ts.ClassDeclaration = node;
var d: ts.PropertyDeclaration[] = []; var d: ts.PropertyDeclaration[] = [];
// look harder at the PropertyDeclarations. // look harder at the PropertyDeclarations.
const wanted = function(c: ts.ClassElement): string { const wanted = function (c: ts.ClassElement): string {
if (ts.isConstructorDeclaration(c)) { if (ts.isConstructorDeclaration(c)) {
return '' return ''
}; };
@ -309,7 +321,7 @@ function genTypes(node: ts.Node) {
} }
if (data.has(c.name)) if (data.has(c.name))
throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`) throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`)
data.set(c.name, c); data.set(c.name, c);
} else { } else {
throw new Error(`unexpected ${strKind(node)} ${loc(node)} `) throw new Error(`unexpected ${strKind(node)} ${loc(node)} `)
} }
@ -322,20 +334,26 @@ function dataMerge(a: Data, b: Data): Data {
if (at == bt) { if (at == bt) {
return a; return a;
} }
const ax = `(${a.statements.length},${a.properties.length})`
const bx = `(${b.statements.length},${b.properties.length})`
//console.log(`397 ${a.name}${ax}${bx}\n${a.me.getText()}\n${b.me.getText()}\n`)
switch (a.name) { switch (a.name) {
case 'InitializeError': case 'InitializeError':
case 'MessageType': case 'MessageType':
case 'CompletionItemTag': case 'CompletionItemTag':
case 'SymbolTag':
case 'CodeActionKind': case 'CodeActionKind':
// want the Module // want the Module
return a.statements.length > 0 ? a : b; return a.statements.length > 0 ? a : b;
case 'CancellationToken': case 'CancellationToken':
// want the Interface // want the Interface
return a.properties.length > 0 ? a : b; return a.properties.length > 0 ? a : b;
case 'TextDocumentContentChangeEvent': // almost the same
return a;
} }
console.log( console.log(
`${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`) `${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
throw new Error(`Fix dataMerge`) throw new Error(`Fix dataMerge for ${a.name}`)
} }
// is a node an ancestor of a NewExpression // is a node an ancestor of a NewExpression
@ -357,19 +375,19 @@ function checkOnce() {
// helper function to find underlying types // helper function to find underlying types
function underlying(n: ts.Node, f: (n: ts.Node) => void) { function underlying(n: ts.Node, f: (n: ts.Node) => void) {
if (!n) return; if (!n) return;
const ff = function(n: ts.Node) { const ff = function (n: ts.Node) {
underlying(n, f) underlying(n, f)
}; };
if (ts.isIdentifier(n)) { if (ts.isIdentifier(n)) {
f(n) f(n)
} else if ( } else if (
n.kind == ts.SyntaxKind.StringKeyword || n.kind == ts.SyntaxKind.StringKeyword ||
n.kind == ts.SyntaxKind.NumberKeyword || n.kind == ts.SyntaxKind.NumberKeyword ||
n.kind == ts.SyntaxKind.AnyKeyword || n.kind == ts.SyntaxKind.AnyKeyword ||
n.kind == ts.SyntaxKind.NullKeyword || n.kind == ts.SyntaxKind.NullKeyword ||
n.kind == ts.SyntaxKind.BooleanKeyword || n.kind == ts.SyntaxKind.BooleanKeyword ||
n.kind == ts.SyntaxKind.ObjectKeyword || n.kind == ts.SyntaxKind.ObjectKeyword ||
n.kind == ts.SyntaxKind.VoidKeyword) { n.kind == ts.SyntaxKind.VoidKeyword) {
// nothing to do // nothing to do
} else if (ts.isTypeReferenceNode(n)) { } else if (ts.isTypeReferenceNode(n)) {
f(n.typeName) f(n.typeName)
@ -390,8 +408,8 @@ function underlying(n: ts.Node, f: (n: ts.Node) => void) {
} else if (ts.isParenthesizedTypeNode(n)) { } else if (ts.isParenthesizedTypeNode(n)) {
underlying(n.type, f) underlying(n.type, f)
} else if ( } else if (
ts.isLiteralTypeNode(n) || ts.isVariableStatement(n) || ts.isLiteralTypeNode(n) || ts.isVariableStatement(n) ||
ts.isTupleTypeNode(n)) { ts.isTupleTypeNode(n)) {
// we only see these in moreTypes, but they are handled elsewhere // we only see these in moreTypes, but they are handled elsewhere
return; return;
} else if (ts.isEnumMember(n)) { } else if (ts.isEnumMember(n)) {
@ -406,31 +424,34 @@ function underlying(n: ts.Node, f: (n: ts.Node) => void) {
// Simplest way to the transitive closure is to stabilize the size of seenTypes // Simplest way to the transitive closure is to stabilize the size of seenTypes
// but it is slow // but it is slow
function moreTypes() { function moreTypes() {
const extra = function(s: string) { const extra = function (s: string) {
if (!data.has(s)) throw new Error(`moreTypes needs ${s}`) if (!data.has(s)) throw new Error(`moreTypes needs ${s}`)
seenTypes.set(s, data.get(s)) seenTypes.set(s, data.get(s))
}; };
rpcTypes.forEach(extra); // all the types needed by the rpcs rpcTypes.forEach(extra); // all the types needed by the rpcs
// needed in enums.go (or elsewhere) // needed in enums.go (or elsewhere)
extra('InitializeError') extra('InitializeError')
extra('WatchKind') extra('WatchKind')
extra('FoldingRangeKind') extra('FoldingRangeKind')
// not sure why these weren't picked up
extra('FileSystemWatcher')
extra('DidChangeWatchedFilesRegistrationOptions')
let old = 0 let old = 0
do { do {
old = seenTypes.size old = seenTypes.size
const m = new Map<string, Data>(); const m = new Map<string, Data>();
const add = function(n: ts.Node) { const add = function (n: ts.Node) {
const nm = goName(n.getText()); const nm = goName(n.getText());
if (seenTypes.has(nm) || m.has(nm)) return; if (seenTypes.has(nm) || m.has(nm)) return;
// For generic parameters, this might set it to undefined // For generic parameters, this might set it to undefined
m.set(nm, data.get(nm)); m.set(nm, data.get(nm));
}; };
// expect all the heritage clauses have single Identifiers // expect all the heritage clauses have single Identifiers
const h = function(n: ts.Node) { const h = function (n: ts.Node) {
underlying(n, add); underlying(n, add);
}; };
const f = function(x: ts.NodeArray<ts.Node>) { const f = function (x: ts.NodeArray<ts.Node>) {
x.forEach(h) x.forEach(h)
}; };
seenTypes.forEach((d: Data) => d && f(d.as)) seenTypes.forEach((d: Data) => d && f(d.as))
@ -459,11 +480,11 @@ function toGo(d: Data, nm: string) {
} else if (d.enums.length > 0) { } else if (d.enums.length > 0) {
goEnum(d, nm); goEnum(d, nm);
} else if ( } else if (
d.properties.length > 0 || d.as.length > 0 || nm == 'InitializedParams') { d.properties.length > 0 || d.as.length > 0 || nm == 'InitializedParams') {
goInterface(d, nm); goInterface(d, nm);
} else } else
throw new Error( throw new Error(
`more cases in toGo ${nm} ${d.as.length} ${d.generics.length} `) `more cases in toGo ${nm} ${d.as.length} ${d.generics.length} `)
} }
// these fields need a * // these fields need a *
@ -478,7 +499,7 @@ function goInterface(d: Data, nm: string) {
let ans = `type ${goName(nm)} struct {\n`; let ans = `type ${goName(nm)} struct {\n`;
// generate the code for each member // generate the code for each member
const g = function(n: ts.TypeElement) { const g = function (n: ts.TypeElement) {
if (!ts.isPropertySignature(n)) if (!ts.isPropertySignature(n))
throw new Error(`expected PropertySignature got ${strKind(n)} `); throw new Error(`expected PropertySignature got ${strKind(n)} `);
ans = ans.concat(getComments(n)); ans = ans.concat(getComments(n));
@ -495,7 +516,7 @@ function goInterface(d: Data, nm: string) {
d.properties.forEach(g) d.properties.forEach(g)
// heritage clauses become embedded types // heritage clauses become embedded types
// check they are all Identifiers // check they are all Identifiers
const f = function(n: ts.ExpressionWithTypeArguments) { const f = function (n: ts.ExpressionWithTypeArguments) {
if (!ts.isIdentifier(n.expression)) if (!ts.isIdentifier(n.expression))
throw new Error(`Interface ${nm} heritage ${strKind(n.expression)} `); throw new Error(`Interface ${nm} heritage ${strKind(n.expression)} `);
ans = ans.concat(goName(n.expression.getText()), '\n') ans = ans.concat(goName(n.expression.getText()), '\n')
@ -518,7 +539,7 @@ function goModule(d: Data, nm: string) {
// They are VariableStatements with x.declarationList having a single // They are VariableStatements with x.declarationList having a single
// VariableDeclaration // VariableDeclaration
let isNumeric = false; let isNumeric = false;
const f = function(n: ts.Statement, i: number) { const f = function (n: ts.Statement, i: number) {
if (!ts.isVariableStatement(n)) { if (!ts.isVariableStatement(n)) {
throw new Error(` ${nm} ${i} expected VariableStatement, throw new Error(` ${nm} ${i} expected VariableStatement,
got ${strKind(n)}`); got ${strKind(n)}`);
@ -545,7 +566,7 @@ function goModule(d: Data, nm: string) {
// generate Go code for an enum. Both types and named constants // generate Go code for an enum. Both types and named constants
function goEnum(d: Data, nm: string) { function goEnum(d: Data, nm: string) {
let isNumeric = false let isNumeric = false
const f = function(v: ts.EnumMember, j: number) { // same as goModule const f = function (v: ts.EnumMember, j: number) { // same as goModule
if (!v.initializer) if (!v.initializer)
throw new Error(`goEnum no initializer ${nm} ${j} ${v.name.getText()}`); throw new Error(`goEnum no initializer ${nm} ${j} ${v.name.getText()}`);
isNumeric = strKind(v.initializer) == 'NumericLiteral'; isNumeric = strKind(v.initializer) == 'NumericLiteral';
@ -566,7 +587,7 @@ function goTypeAlias(d: Data, nm: string) {
if (d.as.length != 0 || d.generics.length != 0) { if (d.as.length != 0 || d.generics.length != 0) {
if (nm != 'ServerCapabilities') if (nm != 'ServerCapabilities')
throw new Error(`${nm} has extra fields(${d.as.length},${ throw new Error(`${nm} has extra fields(${d.as.length},${
d.generics.length}) ${d.me.getText()}`); d.generics.length}) ${d.me.getText()}`);
} }
typesOut.push(getComments(d.me)) typesOut.push(getComments(d.me))
// d.alias doesn't seem to have comments // d.alias doesn't seem to have comments
@ -592,7 +613,7 @@ function goType(n: ts.TypeNode, nm: string): string {
return 'interface{}'; return 'interface{}';
} else if (strKind(n) == 'NullKeyword') { } else if (strKind(n) == 'NullKeyword') {
return 'nil' return 'nil'
} else if (strKind(n) == 'VoidKeyword') { } else if (strKind(n) == 'VoidKeyword' || strKind(n) == 'NeverKeyword') {
return 'void' return 'void'
} else if (strKind(n) == 'ObjectKeyword') { } else if (strKind(n) == 'ObjectKeyword') {
return 'interface{}' return 'interface{}'
@ -608,9 +629,9 @@ function goType(n: ts.TypeNode, nm: string): string {
return v return v
} else if (ts.isTupleTypeNode(n)) { } else if (ts.isTupleTypeNode(n)) {
if (n.getText() == '[number, number]') return '[]float64' if (n.getText() == '[number, number]') return '[]float64'
throw new Error(`goType undexpected Tuple ${n.getText()}`) throw new Error(`goType unexpected Tuple ${n.getText()}`)
} }
throw new Error(`${strKind(n)} goType unexpected ${n.getText()}`) throw new Error(`${strKind(n)} goType unexpected ${n.getText()} for ${nm}`)
} }
// The choice is uniform interface{}, or some heuristically assigned choice, // The choice is uniform interface{}, or some heuristically assigned choice,
@ -641,11 +662,13 @@ function goUnionType(n: ts.UnionTypeNode, nm: string): string {
return `${goType(n.types[0], 'b')} ${help}` return `${goType(n.types[0], 'b')} ${help}`
} }
if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}` if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}`
if (a == 'TypeReference' && a == b) return `interface{} ${help}` if (a == 'TypeReference' && a == b) return `interface{} ${help}`
if (a == 'StringKeyword') // too gross if (a == 'StringKeyword') return `string ${help}`;
return `string ${help}`; if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
throw new Error(`612 ${strKind(n.types[0])} ${strKind(n.types[1])}`) return `${goType(n.types[0], nm)}`
case 3: const aa = strKind(n.types[0]) }
throw new Error(`724 ${a} ${b} ${n.getText()} ${loc(n)}`)
case 3: const aa = strKind(n.types[0])
const bb = strKind(n.types[1]) const bb = strKind(n.types[1])
const cc = strKind(n.types[2]) const cc = strKind(n.types[2])
if (nm == 'DocumentFilter') { if (nm == 'DocumentFilter') {
@ -668,7 +691,7 @@ function goUnionType(n: ts.UnionTypeNode, nm: string): string {
return `${goType(n.types[1], 'f')} ${help}` return `${goType(n.types[1], 'f')} ${help}`
} }
if (aa == 'LiteralType' && bb == aa && cc == aa) return `string ${help}` if (aa == 'LiteralType' && bb == aa && cc == aa) return `string ${help}`
break; break;
case 4: case 4:
if (nm == 'documentChanges') return `TextDocumentEdit ${help} `; if (nm == 'documentChanges') return `TextDocumentEdit ${help} `;
default: default:
@ -708,7 +731,7 @@ function goIntersectionType(n: ts.IntersectionTypeNode, nm: string): string {
if (nm == 'ServerCapabilities') return expandIntersection(n); if (nm == 'ServerCapabilities') return expandIntersection(n);
let inner = ''; let inner = '';
n.types.forEach( n.types.forEach(
(t: ts.TypeNode) => {inner = inner.concat(goType(t, nm), '\n')}); (t: ts.TypeNode) => { inner = inner.concat(goType(t, nm), '\n') });
return `struct{ \n${inner}} ` return `struct{ \n${inner}} `
} }
@ -717,7 +740,7 @@ function goIntersectionType(n: ts.IntersectionTypeNode, nm: string): string {
// of them by name. The names that occur once can be output. The names // of them by name. The names that occur once can be output. The names
// that occur more than once need to be combined. // that occur more than once need to be combined.
function expandIntersection(n: ts.IntersectionTypeNode): string { function expandIntersection(n: ts.IntersectionTypeNode): string {
const bad = function(n: ts.Node, s: string) { const bad = function (n: ts.Node, s: string) {
return new Error(`expandIntersection ${strKind(n)} ${s}`) return new Error(`expandIntersection ${strKind(n)} ${s}`)
}; };
let props = new Map<string, ts.PropertySignature[]>(); let props = new Map<string, ts.PropertySignature[]>();
@ -751,9 +774,12 @@ function expandIntersection(n: ts.IntersectionTypeNode): string {
if (!ts.isPropertySignature(b)) throw bad(b, 'D'); if (!ts.isPropertySignature(b)) throw bad(b, 'D');
ans = ans.concat(getComments(b)); ans = ans.concat(getComments(b));
ans = ans.concat( ans = ans.concat(
goName(b.name.getText()), ' ', goType(b.type, 'a'), u.JSON(b), '\n') goName(b.name.getText()), ' ', goType(b.type, 'a'), u.JSON(b), '\n')
} else if (a.type.kind == ts.SyntaxKind.ObjectKeyword) {
ans = ans.concat(getComments(a))
ans = ans.concat(goName(a.name.getText()), ' ', 'interface{}', u.JSON(a), '\n')
} else { } else {
throw bad(a.type, 'E') throw bad(a.type, `E ${a.getText()} in ${goName(k)} at ${loc(a)}`)
} }
} }
ans = ans.concat('}\n'); ans = ans.concat('}\n');
@ -764,13 +790,18 @@ function expandIntersection(n: ts.IntersectionTypeNode): string {
function goTypeLiteral(n: ts.TypeLiteralNode, nm: string): string { function goTypeLiteral(n: ts.TypeLiteralNode, nm: string): string {
let ans: string[] = []; // in case we generate a new extra type let ans: string[] = []; // in case we generate a new extra type
let res = 'struct{\n' let res = 'struct{\n' // the actual answer usually
const g = function(nx: ts.TypeElement) { const g = function (nx: ts.TypeElement) {
// add the json, as in goInterface(). Strange inside union types. // add the json, as in goInterface(). Strange inside union types.
if (ts.isPropertySignature(nx)) { if (ts.isPropertySignature(nx)) {
const json = u.JSON(nx); let json = u.JSON(nx);
const typ = goType(nx.type, nx.name.getText()) let typ = goType(nx.type, nx.name.getText())
const v = getComments(nx) || ''; const v = getComments(nx) || '';
starred.forEach(([a, b]) => {
if (a != nm || b != typ.toLowerCase()) return;
typ = '*' + typ;
json = json.substring(0, json.length - 2) + ',omitempty"`'
})
res = res.concat(`${v} ${goName(nx.name.getText())} ${typ}`, json, '\n') res = res.concat(`${v} ${goName(nx.name.getText())} ${typ}`, json, '\n')
ans.push(`${v}${goName(nx.name.getText())} ${typ} ${json}\n`) ans.push(`${v}${goName(nx.name.getText())} ${typ} ${json}\n`)
} else if (ts.isIndexSignatureDeclaration(nx)) { } else if (ts.isIndexSignatureDeclaration(nx)) {
@ -894,20 +925,23 @@ function goNot(side: side, m: string) {
function goReq(side: side, m: string) { function goReq(side: side, m: string) {
const n = req.get(m); const n = req.get(m);
const nm = methodName(m); const nm = methodName(m);
if (nm.indexOf('/') >= 0) {
console.log(`980 ${m} ${n.getText()} ${loc(n)} `)
}
let a = goType(n.typeArguments[0], m); let a = goType(n.typeArguments[0], m);
let b = goType(n.typeArguments[1], m); let b = goType(n.typeArguments[1], m);
if (n.getText().includes('Type0')) { if (n.getText().includes('Type0')) {
b = a; b = a;
a = ''; // workspace/workspaceFolders and shutdown a = ''; // workspace/workspaceFolders and shutdown
} }
u.prb(`${side.name} req ${a != ''},${b != ''} ${nm} ${m} ${loc(n)}`) u.prb(`${side.name} req ${a != ''}, ${b != ''} ${nm} ${m} ${loc(n)} `)
side.methods.push(sig(nm, a, b)); side.methods.push(sig(nm, a, b));
const caseHdr = `case "${m}": // req`; const caseHdr = `case "${m}": // req`;
let case1 = notNil; let case1 = notNil;
if (a != '') { if (a != '') {
if (extraTypes.has('Param' + nm)) a = 'Param' + nm if (extraTypes.has('Param' + nm)) a = 'Param' + nm
case1 = `var params ${a} case1 = `var params ${a}
if err := json.Unmarshal(*r.Params, &params); err != nil { if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err) sendParseError(ctx, r, err)
return true return true
@ -941,7 +975,7 @@ function goReq(side: side, m: string) {
if (indirect(b)) theRet = '&result'; if (indirect(b)) theRet = '&result';
callBody = `var result ${b} callBody = `var result ${b}
if err := s.Conn.Call(ctx, "${m}", ${ if err := s.Conn.Call(ctx, "${m}", ${
p2}, &result); err != nil { p2}, &result); err != nil {
return nil, err return nil, err
} }
return ${theRet}, nil return ${theRet}, nil
@ -956,11 +990,20 @@ function goReq(side: side, m: string) {
// make sure method names are unique // make sure method names are unique
let seenNames = new Set<string>(); let seenNames = new Set<string>();
function methodName(m: string): string { function methodName(m: string): string {
const i = m.indexOf('/'); let i = m.indexOf('/');
let s = m.substring(i + 1); let s = m.substring(i + 1);
let x = s[0].toUpperCase() + s.substring(1); let x = s[0].toUpperCase() + s.substring(1);
const j = x.indexOf('/')
if (j >= 0) {
let suffix = x.substring(j + 1)
suffix = suffix[0].toUpperCase() + suffix.substring(1)
let prefix = x.substring(0, j)
x = prefix + suffix
}
if (seenNames.has(x)) { if (seenNames.has(x)) {
x += m[0].toUpperCase() + m.substring(1, i); // Resolve, ResolveCodeLens, ResolveDocumentLink
if (!x.startsWith('Resolve')) throw new Error(`expected Resolve, not ${x}`)
x += m[0].toUpperCase() + m.substring(1, i)
} }
seenNames.add(x); seenNames.add(x);
return x; return x;
@ -971,7 +1014,7 @@ function indirect(s: string): boolean {
if (s == '' || s == 'void') return false; if (s == '' || s == 'void') return false;
const skip = (x: string) => s.startsWith(x); const skip = (x: string) => s.startsWith(x);
if (skip('[]') || skip('interface') || skip('Declaration') || if (skip('[]') || skip('interface') || skip('Declaration') ||
skip('Definition') || skip('DocumentSelector')) skip('Definition') || skip('DocumentSelector'))
return false; return false;
return true return true
} }
@ -1011,7 +1054,7 @@ function output(side: side) {
side.outputFile = `ts${side.name}.go`; side.outputFile = `ts${side.name}.go`;
side.fd = fs.openSync(side.outputFile, 'w'); side.fd = fs.openSync(side.outputFile, 'w');
} }
const f = function(s: string) { const f = function (s: string) {
fs.writeSync(side.fd, s); fs.writeSync(side.fd, s);
fs.writeSync(side.fd, '\n'); fs.writeSync(side.fd, '\n');
}; };
@ -1028,10 +1071,10 @@ function output(side: side) {
`); `);
const a = side.name[0].toUpperCase() + side.name.substring(1) const a = side.name[0].toUpperCase() + side.name.substring(1)
f(`type ${a} interface {`); f(`type ${a} interface {`);
side.methods.forEach((v) => {f(v)}); side.methods.forEach((v) => { f(v) });
f('}\n'); f('}\n');
f(`func (h ${ f(`func (h ${
side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
if delivered { if delivered {
return false return false
} }
@ -1041,7 +1084,7 @@ function output(side: side) {
return true return true
} }
switch r.Method {`); switch r.Method {`);
side.cases.forEach((v) => {f(v)}); side.cases.forEach((v) => { f(v) });
f(` f(`
} }
}`); }`);
@ -1050,7 +1093,7 @@ function output(side: side) {
*jsonrpc2.Conn *jsonrpc2.Conn
} }
`); `);
side.calls.forEach((v) => {f(v)}); side.calls.forEach((v) => { f(v) });
} }
// Handling of non-standard requests, so we can add gopls-specific calls. // Handling of non-standard requests, so we can add gopls-specific calls.
@ -1084,16 +1127,15 @@ function nonstandardRequests() {
function main() { function main() {
if (u.gitHash != u.git()) { if (u.gitHash != u.git()) {
throw new Error( throw new Error(
`git hash mismatch, wanted\n${u.gitHash} but source is at\n${u.git()}`); `git hash mismatch, wanted\n${u.gitHash} but source is at\n${u.git()}`);
} }
u.createOutputFiles() u.createOutputFiles()
parse() parse()
u.printAST(program) u.printAST(program)
// visit every sourceFile in the program, collecting the New // find the Requests and Nofificatations
// nodes that encapsulate the protocol
for (const sourceFile of program.getSourceFiles()) { for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) { if (!sourceFile.isDeclarationFile) {
ts.forEachChild(sourceFile, findNews) ts.forEachChild(sourceFile, findRPCs)
} }
} }
// separate RPCs into client and server // separate RPCs into client and server
@ -1112,11 +1154,13 @@ function main() {
// 2. func (h *serverHandler) Deliver(...) { switch r.method } // 2. func (h *serverHandler) Deliver(...) { switch r.method }
// 3. func (x *xDispatcher) Method(ctx, parm) // 3. func (x *xDispatcher) Method(ctx, parm)
not.forEach( // notifications not.forEach( // notifications
(v, k) => { (v, k) => {
receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k)}); receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k)
});
req.forEach( // requests req.forEach( // requests
(v, k) => { (v, k) => {
receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k)}); receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k)
});
nonstandardRequests(); nonstandardRequests();
// find all the types implied by seenTypes and rpcs to try to avoid // find all the types implied by seenTypes and rpcs to try to avoid
// generating types that aren't used // generating types that aren't used

File diff suppressed because it is too large Load Diff

View File

@ -1,521 +0,0 @@
import * as fs from 'fs';
import * as ts from 'typescript';
// generate tsclient.go and tsserver.go, which are the definitions and stubs for
// supporting the LPS protocol. These files have 3 sections:
// 1. define the Client or Server type
// 2. fill out the clientHandler or serveHandler which is basically a large
// switch on the requests and notifications received by the client/server.
// 3. The methods corresponding to these. (basically parse the request,
// call something, and perhaps send a response.)
let dir = process.env['HOME'];
let fnames = [
`/vscode-languageserver-node/protocol/src/protocol.ts`,
`/vscode-languageserver-node/jsonrpc/src/main.ts`
];
let fda: number, fdy: number; // file descriptors
function createOutputFiles() {
fda = fs.openSync('/tmp/ts-a', 'w') // dump of AST
fdy = fs.openSync('/tmp/ts-c', 'w') // unused, for debugging
}
function pra(s: string) {
return (fs.writeSync(fda, s))
}
function prb(s: string) {
return (fs.writeSync(fdy, s + '\n'))
}
let program: ts.Program;
function generate(files: string[], options: ts.CompilerOptions): void {
program = ts.createProgram(files, options);
program.getTypeChecker();
dumpAST(); // for debugging
// visit every sourceFile in the program, collecting information
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
ts.forEachChild(sourceFile, genStuff)
}
}
// when 4 args, they are param, result, error, registration options, e.g.:
// RequestType<TextDocumentPositionParams, Definition | DefinitionLink[] |
// null,
// void, TextDocumentRegistrationOptions>('textDocument/implementation');
// 3 args is RequestType0('shutdown')<void, void, void>
// and RequestType0('workspace/workspaceFolders)<WorkspaceFolder[]|null, void,
// void>
// the two args are the notification data and the registration data
// except for textDocument/selectionRange and a NotificationType0('exit')
// selectionRange is the following, but for now do it by hand, special case.
// RequestType<TextDocumentPositionParams, SelectionRange[] | null, any, any>
// = new RequestType('textDocument/selectionRange')
// and foldingRange has the same problem.
setReceives(); // distinguish client and server
// for each of Client and Server there are 3 parts to the output:
// 1. type X interface {methods}
// 2. func (h *serverHandler) Deliver(...) { switch r.method }
// 3. func (x *xDispatcher) Method(ctx, parm)
not.forEach(
(v, k) => {receives.get(k) == 'client' ? goNot(client, k) :
goNot(server, k)});
req.forEach(
(v, k) => {receives.get(k) == 'client' ? goReq(client, k) :
goReq(server, k)});
// and print the Go code
output(client);
output(server);
return;
}
// Go signatures for methods.
function sig(nm: string, a: string, b: string, names?: boolean): string {
if (a != '') {
if (names)
a = ', params *' + a;
else
a = ', *' + a;
}
let ret = 'error';
if (b != '') {
b.startsWith('[]') || b.startsWith('interface') || (b = '*' + b);
ret = `(${b}, error)`;
}
let start = `${nm}(`;
if (names) {
start = start + 'ctx ';
}
return `${start}context.Context${a}) ${ret}`;
}
const notNil = `if r.Params != nil {
r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
return true
}`;
// Go code for notifications. Side is client or server, m is the request method
function goNot(side: side, m: string) {
const n = not.get(m);
let a = goType(side, m, n.typeArguments[0]);
// let b = goType(m, n.typeArguments[1]); These are registration options
const nm = methodName(m);
side.methods.push(sig(nm, a, ''));
const caseHdr = `case "${m}": // notif`;
let case1 = notNil;
if (a != '') {
case1 = `var params ${a}
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}
if err := h.${side.name}.${nm}(ctx, &params); err != nil {
log.Error(ctx, "", err)
}
return true`;
} else {
case1 = `if err := h.${side.name}.${nm}(ctx); err != nil {
log.Error(ctx, "", err)
}
return true`;
}
side.cases.push(`${caseHdr}\n${case1}`);
const arg3 = a == '' ? 'nil' : 'params';
side.calls.push(`
func (s *${side.name}Dispatcher) ${sig(nm, a, '', true)} {
return s.Conn.Notify(ctx, "${m}", ${arg3})
}`);
}
// Go code for requests.
function goReq(side: side, m: string) {
const n = req.get(m);
const nm = methodName(m);
let a = goType(side, m, n.typeArguments[0]);
let b = goType(side, m, n.typeArguments[1]);
if (n.getText().includes('Type0')) {
b = a;
a = ''; // workspace/workspaceFolders and shutdown
}
prb(`${side.name} req ${a != ''},${b != ''} ${nm} ${m} ${loc(n)}`)
side.methods.push(sig(nm, a, b));
const caseHdr = `case "${m}": // req`;
let case1 = notNil;
if (a != '') {
case1 = `var params ${a}
if err := json.Unmarshal(*r.Params, &params); err != nil {
sendParseError(ctx, r, err)
return true
}`;
}
const arg2 = a == '' ? '' : ', &params';
let case2 = `if err := h.${side.name}.${nm}(ctx${arg2}); err != nil {
log.Error(ctx, "", err)
}`;
if (b != '') {
case2 = `resp, err := h.${side.name}.${nm}(ctx${arg2})
if err := r.Reply(ctx, resp, err); err != nil {
log.Error(ctx, "", err)
}
return true`;
} else { // response is nil
case2 = `err := h.${side.name}.${nm}(ctx${arg2})
if err := r.Reply(ctx, nil, err); err != nil {
log.Error(ctx, "", err)
}
return true`
}
side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
const callHdr = `func (s *${side.name}Dispatcher) ${sig(nm, a, b, true)} {`;
let callBody = `return s.Conn.Call(ctx, "${m}", nil, nil)\n}`;
if (b != '') {
const p2 = a == '' ? 'nil' : 'params';
let theRet = `result`;
!b.startsWith('[]') && !b.startsWith('interface') && (theRet = '&result');
callBody = `var result ${b}
if err := s.Conn.Call(ctx, "${m}", ${
p2}, &result); err != nil {
return nil, err
}
return ${theRet}, nil
}`;
} else if (a != '') {
callBody = `return s.Conn.Call(ctx, "${m}", params, nil) // Call, not Notify
}`
}
side.calls.push(`${callHdr}\n${callBody}\n`);
}
// make sure method names are unique
let seenNames = new Set<string>();
function methodName(m: string): string {
const i = m.indexOf('/');
let s = m.substring(i + 1);
let x = s[0].toUpperCase() + s.substring(1);
if (seenNames.has(x)) {
x += m[0].toUpperCase() + m.substring(1, i);
}
seenNames.add(x);
return x;
}
function output(side: side) {
if (side.outputFile === undefined) side.outputFile = `ts${side.name}.go`;
side.fd = fs.openSync(side.outputFile, 'w');
const f = function(s: string) {
fs.writeSync(side.fd, s);
fs.writeSync(side.fd, '\n');
};
f(`package protocol`);
f(`// Code generated (see typescript/README.md) DO NOT EDIT.\n`);
f(`
import (
"context"
"encoding/json"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/telemetry/log"
"golang.org/x/tools/internal/xcontext"
)
`);
const a = side.name[0].toUpperCase() + side.name.substring(1)
f(`type ${a} interface {`);
side.methods.forEach((v) => {f(v)});
f('}\n');
f(`func (h ${
side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
if delivered {
return false
}
if ctx.Err() != nil {
ctx := xcontext.Detach(ctx)
r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
return true
}
switch r.Method {`);
side.cases.forEach((v) => {f(v)});
f(`
default:
return false
}
}`);
f(`
type ${side.name}Dispatcher struct {
*jsonrpc2.Conn
}
`);
side.calls.forEach((v) => {f(v)});
if (side.name == 'server')
f(`
type CancelParams struct {
/**
* The request id to cancel.
*/
ID jsonrpc2.ID \`json:"id"\`
}`);
f(`// Types constructed to avoid structs as formal argument types`)
side.ourTypes.forEach((val, key) => f(`type ${val} ${key}`));
}
interface side {
methods: string[];
cases: string[];
calls: string[];
ourTypes: Map<string, string>;
name: string; // client or server
goName: string; // Client or Server
outputFile?: string;
fd?: number
}
let client: side = {
methods: [],
cases: [],
calls: [],
name: 'client',
goName: 'Client',
ourTypes: new Map<string, string>()
};
let server: side = {
methods: [],
cases: [],
calls: [],
name: 'server',
goName: 'Server',
ourTypes: new Map<string, string>()
};
let req = new Map<string, ts.NewExpression>(); // requests
let not = new Map<string, ts.NewExpression>(); // notifications
let receives = new Map<string, 'server'|'client'>(); // who receives it
function setReceives() {
// mark them all as server, then adjust the client ones.
// it would be nice to have some independent check
req.forEach((_, k) => {receives.set(k, 'server')});
not.forEach((_, k) => {receives.set(k, 'server')});
receives.set('window/logMessage', 'client');
receives.set('telemetry/event', 'client');
receives.set('client/registerCapability', 'client');
receives.set('client/unregisterCapability', 'client');
receives.set('window/showMessage', 'client');
receives.set('window/showMessageRequest', 'client');
receives.set('workspace/workspaceFolders', 'client');
receives.set('workspace/configuration', 'client');
receives.set('workspace/applyEdit', 'client');
receives.set('textDocument/publishDiagnostics', 'client');
// a small check
receives.forEach((_, k) => {
if (!req.get(k) && !not.get(k)) throw new Error(`missing ${k}}`);
if (req.get(k) && not.get(k)) throw new Error(`dup ${k}`);
})
}
function goType(side: side, m: string, n: ts.Node): string {
if (n === undefined) return '';
if (ts.isTypeReferenceNode(n)) return n.typeName.getText();
if (n.kind == ts.SyntaxKind.VoidKeyword) return '';
if (n.kind == ts.SyntaxKind.AnyKeyword) return 'interface{}';
if (ts.isArrayTypeNode(n)) return '[]' + goType(side, m, n.elementType);
// special cases, before we get confused
switch (m) {
case 'textDocument/completion':
return 'CompletionList';
case 'textDocument/documentSymbol':
return '[]DocumentSymbol';
case 'textDocument/prepareRename':
return 'Range';
case 'textDocument/codeAction':
return '[]CodeAction';
}
if (ts.isUnionTypeNode(n)) {
let x: string[] = [];
n.types.forEach(
(v) => {v.kind != ts.SyntaxKind.NullKeyword &&
x.push(goType(side, m, v))});
if (x.length == 1) return x[0];
prb(`===========${m} ${x}`)
// Because we don't fully resolve types, we don't know that
// Definition is Location | Location[]
if (x[0] == 'Definition') return '[]Location';
if (x[1] == '[]' + x[0] + 'Link') return x[1];
throw new Error(`${m}, ${x} unexpected types`)
}
if (ts.isIntersectionTypeNode(n)) {
// we expect only TypeReferences, and put out a struct with embedded types
// This is not good, as it uses a struct where a type name ought to be.
let x: string[] = [];
n.types.forEach((v) => {
// expect only TypeReferences
if (!ts.isTypeReferenceNode(v)) {
throw new Error(
`expected only TypeReferences in Intersection ${getText(n)}`)
}
x.push(goType(side, m, v));
x.push(';')
})
x.push('}')
let ans = 'struct {'.concat(...x);
// If ans does not have a type, create it
if (side.ourTypes.get(ans) == undefined) {
side.ourTypes.set(ans, 'Param' + getText(n).substring(0, 6))
}
// Return the type
return side.ourTypes.get(ans)
}
return '?';
}
// walk the AST finding Requests and Notifications
function genStuff(node: ts.Node) {
if (!ts.isNewExpression(node)) {
ts.forEachChild(node, genStuff)
return;
}
// process the right kind of new expression
const wh = node.expression.getText();
if (wh != 'RequestType' && wh != 'RequestType0' && wh != 'NotificationType' &&
wh != 'NotificationType0')
return;
if (node.arguments === undefined || node.arguments.length != 1 ||
!ts.isStringLiteral(node.arguments[0])) {
throw new Error(`missing n.arguments ${loc(node)}`)
}
// RequestType<useful>=new RequestTYpe('foo')
if (node.typeArguments === undefined) {
node.typeArguments = lookUp(node);
}
// new RequestType<useful>
let s = node.arguments[0].getText();
// Request or Notification
const v = wh[0] == 'R' ? req : not;
s = s.substring(1, s.length - 1); // remove quoting
if (s == '$/cancelRequest') return; // special case in output
v.set(s, node);
}
// find the text of a node
function getText(node: ts.Node): string {
let sf = node.getSourceFile();
let start = node.getStart(sf)
let end = node.getEnd()
return sf.text.substring(start, end)
}
function lookUp(n: ts.NewExpression): ts.NodeArray<ts.TypeNode> {
// parent should be VariableDeclaration. its children should be
// Identifier('type') ???
// TypeReference: [Identifier('RequestType1), ]
// NewExpression (us)
const p = n.parent;
if (!ts.isVariableDeclaration(p)) throw new Error(`not variable decl`);
const tr = p.type;
if (!ts.isTypeReferenceNode(tr)) throw new Error(`not TypeReference`);
return tr.typeArguments;
}
function dumpAST() {
// dump the ast, for debugging
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
// walk the tree to do stuff
ts.forEachChild(sourceFile, describe);
}
}
}
// some tokens have the wrong default name
function strKind(n: ts.Node): string {
const x = ts.SyntaxKind[n.kind];
switch (x) {
default:
return x;
case 'FirstAssignment':
return 'EqualsToken';
case 'FirstBinaryOperator':
return 'LessThanToken';
case 'FirstCompoundAssignment':
return 'PlusEqualsToken';
case 'FirstContextualKeyword':
return 'AbstractKeyword';
case 'FirstLiteralToken':
return 'NumericLiteral';
case 'FirstNode':
return 'QualifiedName';
case 'FirstTemplateToken':
return 'NoSubstitutionTemplateLiteral';
case 'LastTemplateToken':
return 'TemplateTail';
case 'FirstTypeNode':
return 'TypePredicate';
}
}
function describe(node: ts.Node) {
if (node === undefined) {
return
}
let indent = '';
function f(n: ts.Node) {
if (ts.isIdentifier(n)) {
pra(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
} else if (ts.isPropertySignature(n) || ts.isEnumMember(n)) {
pra(`${indent} ${loc(n)} ${strKind(n)} \n`)
} else if (ts.isTypeLiteralNode(n)) {
let m = n.members
pra(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`)
} else {
pra(`${indent} ${loc(n)} ${strKind(n)} \n`)
};
indent += ' '
ts.forEachChild(n, f)
indent = indent.slice(0, indent.length - 2)
}
f(node)
}
// string version of the location in the source file
function loc(node: ts.Node): string {
const sf = node.getSourceFile();
const start = node.getStart()
const x = sf.getLineAndCharacterOfPosition(start)
const full = node.getFullStart()
const y = sf.getLineAndCharacterOfPosition(full)
let fn = sf.fileName
const n = fn.search(/-node./)
fn = fn.substring(n + 6)
return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
y.character + 1})`
}
// ad hoc argument parsing: [-d dir] [-o outputfile], and order matters
function main() {
let args = process.argv.slice(2) // effective command line
if (args.length > 0) {
let j = 0;
if (args[j] == '-d') {
dir = args[j + 1]
j += 2
}
if (j != args.length) throw new Error(`incomprehensible args ${args}`)
}
let files: string[] = [];
for (let i = 0; i < fnames.length; i++) {
files.push(`${dir}${fnames[i]}`)
}
createOutputFiles()
generate(
files, {target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS});
}
main()

View File

@ -10,10 +10,11 @@ import * as ts from 'typescript';
let dir = process.env['HOME']; let dir = process.env['HOME'];
const srcDir = '/vscode-languageserver-node' const srcDir = '/vscode-languageserver-node'
export const fnames = [ export const fnames = [
`${dir}${srcDir}/protocol/src/protocol.ts`, //`${dir}${srcDir}/protocol/src/protocol.ts`, // why isn't this main.ts?
`${dir}/${srcDir}/protocol/src/main.ts`,
`${dir}${srcDir}/types/src/main.ts`, `${dir}${srcDir}/jsonrpc/src/main.ts` `${dir}${srcDir}/types/src/main.ts`, `${dir}${srcDir}/jsonrpc/src/main.ts`
]; ];
export const gitHash = '635ab1fe6f8c57ce9402e573d007f24d6d290fd3'; export const gitHash = '7b90c29d0cb5cd7b9c41084f6cb3781a955adeba';
let outFname = 'tsprotocol.go'; let outFname = 'tsprotocol.go';
let fda: number, fdb: number, fde: number; // file descriptors let fda: number, fdb: number, fde: number; // file descriptors
@ -65,10 +66,10 @@ export function computeHeader(pkgDoc: boolean): string {
} }
} }
const a = const a =
`// Package protocol contains data types and code for LSP jsonrpcs\n` + `// Package protocol contains data types and code for LSP jsonrpcs\n` +
`// generated automatically from vscode-languageserver-node\n` + `// generated automatically from vscode-languageserver-node\n` +
`// commit: ${gitHash}\n` + `// commit: ${gitHash}\n` +
`// last fetched ${lastDate}\n` `// last fetched ${lastDate}\n`
const b = 'package protocol\n' const b = 'package protocol\n'
const c = `\n// Code generated (see typescript/README.md) DO NOT EDIT.\n\n` const c = `\n// Code generated (see typescript/README.md) DO NOT EDIT.\n\n`
if (pkgDoc) { if (pkgDoc) {
@ -85,7 +86,7 @@ export function goName(s: string): string {
if (s.charAt(0) == '_') { if (s.charAt(0) == '_') {
ans = 'Inner' + s.substring(1) ans = 'Inner' + s.substring(1)
} }
else {ans = s.substring(0, 1).toUpperCase() + s.substring(1)}; else { ans = s.substring(0, 1).toUpperCase() + s.substring(1) };
ans = ans.replace(/Uri$/, 'URI') ans = ans.replace(/Uri$/, 'URI')
ans = ans.replace(/Id$/, 'ID') ans = ans.replace(/Id$/, 'ID')
return ans return ans
@ -94,7 +95,7 @@ export function goName(s: string): string {
// Generate JSON tag for a struct field // Generate JSON tag for a struct field
export function JSON(n: ts.PropertySignature): string { export function JSON(n: ts.PropertySignature): string {
const json = `\`json:"${n.name.getText()}${ const json = `\`json:"${n.name.getText()}${
n.questionToken != undefined ? ',omitempty' : ''}"\``; n.questionToken != undefined ? ',omitempty' : ''}"\``;
return json return json
} }
@ -107,12 +108,13 @@ export function constName(nm: string, type: string): string {
['SignatureHelpTriggerKind', 'Sig'], ['CompletionItemTag', 'Compl'] ['SignatureHelpTriggerKind', 'Sig'], ['CompletionItemTag', 'Compl']
]) // typeName->prefix ]) // typeName->prefix
let suff = new Map<string, string>([ let suff = new Map<string, string>([
['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat'] ['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat'],
['SymbolTag', 'Symbol']
]) ])
let ans = nm; let ans = nm;
if (pref.get(type)) ans = pref.get(type) + ans; if (pref.get(type)) ans = pref.get(type) + ans;
if (suff.has(type)) ans = ans + suff.get(type) if (suff.has(type)) ans = ans + suff.get(type)
return ans return ans
} }
// Find the comments associated with an AST node // Find the comments associated with an AST node
@ -129,7 +131,7 @@ export function getComments(node: ts.Node): string {
export function printAST(program: ts.Program) { export function printAST(program: ts.Program) {
// dump the ast, for debugging // dump the ast, for debugging
const f = function(n: ts.Node) { const f = function (n: ts.Node) {
describe(n, pra) describe(n, pra)
}; };
for (const sourceFile of program.getSourceFiles()) { for (const sourceFile of program.getSourceFiles()) {
@ -171,7 +173,7 @@ function describe(node: ts.Node, pr: (s: string) => any) {
else if (ts.isStringLiteral(n)) { else if (ts.isStringLiteral(n)) {
pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`) pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
} }
else {pr(`${indent} ${loc(n)} ${strKind(n)} \n`)}; else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`) };
indent += ' .' indent += ' .'
ts.forEachChild(n, f) ts.forEachChild(n, f)
indent = indent.slice(0, indent.length - 2) indent = indent.slice(0, indent.length - 2)
@ -191,7 +193,7 @@ export function loc(node: ts.Node): string {
const n = fn.search(/-node./) const n = fn.search(/-node./)
fn = fn.substring(n + 6) fn = fn.substring(n + 6)
return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${ return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
y.character + 1})` y.character + 1})`
} }
// --- various string stuff // --- various string stuff
@ -199,7 +201,7 @@ export function loc(node: ts.Node): string {
// as part of printing the AST tree // as part of printing the AST tree
function kinds(n: ts.Node): string { function kinds(n: ts.Node): string {
let res = 'Seen ' + strKind(n); let res = 'Seen ' + strKind(n);
function f(n: ts.Node): void{res += ' ' + strKind(n)}; function f(n: ts.Node): void { res += ' ' + strKind(n) };
ts.forEachChild(n, f) ts.forEachChild(n, f)
return res return res
} }

View File

@ -100,6 +100,10 @@ func (s *Server) Implementation(ctx context.Context, params *protocol.Implementa
return s.implementation(ctx, params) return s.implementation(ctx, params)
} }
func (s *Server) IncomingCalls(context.Context, *protocol.CallHierarchyIncomingCallsParams) ([]protocol.CallHierarchyIncomingCall, error) {
return nil, notImplemented("IncomingCalls")
}
func (s *Server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) { func (s *Server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
return s.initialize(ctx, params) return s.initialize(ctx, params)
} }
@ -120,6 +124,14 @@ func (s *Server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeForma
return nil, notImplemented("OnTypeFormatting") return nil, notImplemented("OnTypeFormatting")
} }
func (s *Server) OutgoingCalls(context.Context, *protocol.CallHierarchyOutgoingCallsParams) ([]protocol.CallHierarchyOutgoingCall, error) {
return nil, notImplemented("OutgoingCalls")
}
func (s *Server) PrepareCallHierarchy(context.Context, *protocol.CallHierarchyPrepareParams) ([]protocol.CallHierarchyItem, error) {
return nil, notImplemented("PrepareCallHierarchy")
}
func (s *Server) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (interface{}, error) { func (s *Server) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (interface{}, error) {
return s.prepareRename(ctx, params) return s.prepareRename(ctx, params)
} }
@ -156,6 +168,18 @@ func (s *Server) SelectionRange(context.Context, *protocol.SelectionRangeParams)
return nil, notImplemented("SelectionRange") return nil, notImplemented("SelectionRange")
} }
func (s *Server) SemanticTokens(context.Context, *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
return nil, notImplemented("SemanticTokens")
}
func (s *Server) SemanticTokensEdits(context.Context, *protocol.SemanticTokensEditsParams) (interface{}, error) {
return nil, notImplemented("SemanticTokensEdits")
}
func (s *Server) SemanticTokensRange(context.Context, *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
return nil, notImplemented("SemanticTokensRange")
}
func (s *Server) SetTraceNotification(context.Context, *protocol.SetTraceParams) error { func (s *Server) SetTraceNotification(context.Context, *protocol.SetTraceParams) error {
return notImplemented("SetTraceNotification") return notImplemented("SetTraceNotification")
} }
@ -183,3 +207,11 @@ func (s *Server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams)
func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) { func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("WillSaveWaitUntil") return nil, notImplemented("WillSaveWaitUntil")
} }
func (s *Server) WorkDoneProgressCancel(context.Context, *protocol.WorkDoneProgressCancelParams) error {
return notImplemented("WorkDoneProgressCancel")
}
func (s *Server) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error {
return notImplemented("WorkDoneProgressCreate")
}