internal/lsp: generate boilerplate stubs for type Server

go generate server.go generates server_gen.go which contains 44 boilerplate
stubs implementing the LSP protocol. The Server methods are defined
in protocol/tsserver.go and implemented in the lsp package. server_gen.go
ties the two together.

The generator is in helper/helper.go, and described in helper/README.md.

Change-Id: I120bc658e55a91ec5b07c0c0cf8c188882f0be66
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215979
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-23 10:56:00 -05:00
parent 5c352bb417
commit 9778d966fa
5 changed files with 471 additions and 184 deletions

View File

@ -0,0 +1,33 @@
# Generate server_gen.go
`helper` generates boilerplate code for server.go by processing the
generated code in `protocol/tsserver.go`.
First, build `helper` in this directore (`go build .`).
In directory `lsp`, executing `go generate server.go` generates the stylized file
`server_gen.go` that contains stubs for type `Server`.
It decides what stubs are needed and their signatures
by looking at the `Server` interface (`-t` flag). These all look somewhat like
`Resolve(context.Context, *CompletionItem) (*CompletionItem, error)`.
It then parses the `lsp` directory (`-u` flag) to see if there is a corresponding
implementation function (which in this case would be named `resolve`). If so
it discovers the parameter names needed, and generates (in `server_gen.go`) code
like
``` go
func (s *Server) resolve(ctx context.Context, params *protocol.CompletionItem) (*protocol.CompletionItem, error) {
return s.resolve(ctx, params)
}
```
If `resolve` is not defined (and it is not), then the body of the generated function is
```go
return nil, notImplemented("resolve")
```
So to add a capability currently not implemented, just define it somewhere in `lsp`.
In this case, just define `func (s *Server) resolve(...)` and re-generate `server_gen.go`.

View File

@ -0,0 +1,244 @@
// Invoke with //go:generate helper/helper -t Server -d protocol/tsserver.go -u lsp -o server_gen.go
// invoke in internal/lsp
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"sort"
"strings"
"text/template"
)
var (
typ = flag.String("t", "Server", "generate code for this type")
def = flag.String("d", "", "the file the type is defined in") // this relies on punning
use = flag.String("u", "", "look for uses in this package")
out = flag.String("o", "", "where to write the generated file")
)
func main() {
log.SetFlags(log.Lshortfile)
flag.Parse()
if *typ == "" || *def == "" || *use == "" || *out == "" {
flag.PrintDefaults()
return
}
// read the type definition and see what methods we're looking for
doTypes()
// parse the package and see which methods are defined
doUses()
output()
}
// replace "\\\n" with nothing before using
var tmpl = `
package lsp
// code generated by helper. DO NOT EDIT.
import (
"context"
"golang.org/x/tools/internal/lsp/protocol"
)
{{range $key, $v := .Stuff}}
func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} {
{{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\
{{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}}
}
{{end}}
`
func output() {
// put in empty param names as needed
for _, t := range types {
if t.paramnames == nil {
t.paramnames = make([]string, len(t.paramtypes))
}
for i, p := range t.paramtypes {
cm := ""
if i > 0 {
cm = ", "
}
t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p)
t.Invoke += fmt.Sprintf("%s%s", cm, t.paramnames[i])
}
if len(t.Results) > 1 {
t.Result = "("
}
for i, r := range t.Results {
cm := ""
if i > 0 {
cm = ", "
}
t.Result += fmt.Sprintf("%s%s", cm, r)
}
if len(t.Results) > 1 {
t.Result += ")"
}
}
fd, err := os.Create(*out)
if err != nil {
log.Fatal(err)
}
t, err := template.New("foo").Parse(tmpl)
if err != nil {
log.Fatal(err)
}
type par struct {
Type string
Stuff []*Function
}
p := par{*typ, types}
if false { // debugging the template
t.Execute(os.Stderr, &p)
}
buf := bytes.NewBuffer(nil)
err = t.Execute(buf, &p)
if err != nil {
log.Fatal(err)
}
ans := bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1)
fd.Write(ans)
}
func doUses() {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, *use, nil, 0)
if err != nil {
log.Fatalf("%q:%v", *use, err)
}
pkg := pkgs["lsp"] // CHECK
files := pkg.Files
for fname, f := range files {
for _, d := range f.Decls {
fd, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
nm := fd.Name.String()
if isExported(nm) {
// we're looking for things like didChange
continue
}
if fx, ok := byname[nm]; ok {
if fx.Found != "" {
log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname)
}
fx.Found = fname
// and the Paramnames
ft := fd.Type
for _, f := range ft.Params.List {
nm := ""
if len(f.Names) > 0 {
nm = f.Names[0].String()
}
fx.paramnames = append(fx.paramnames, nm)
}
}
}
}
if false {
for i, f := range types {
log.Printf("%d %s %s", i, f.Internal, f.Found)
}
}
}
type Function struct {
Name string
Internal string // first letter lower case
paramtypes []string
paramnames []string
Results []string
Param string
Result string // do it in code, easier than in a template
Invoke string
Found string // file it was found in
}
var types []*Function
var byname = map[string]*Function{} // internal names
func doTypes() {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, *def, nil, 0)
if err != nil {
log.Fatal(err)
}
fd, err := os.Create("/tmp/ast")
if err != nil {
log.Fatal(err)
}
ast.Fprint(fd, fset, f, ast.NotNilFilter)
ast.Inspect(f, inter)
sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name })
if false {
for i, f := range types {
log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results)
}
}
}
func inter(n ast.Node) bool {
x, ok := n.(*ast.TypeSpec)
if !ok || x.Name.Name != *typ {
return true
}
m := x.Type.(*ast.InterfaceType).Methods.List
for _, fld := range m {
fn := fld.Type.(*ast.FuncType)
p := fn.Params.List
r := fn.Results.List
fx := &Function{
Name: fld.Names[0].String(),
}
fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:]
for _, f := range p {
fx.paramtypes = append(fx.paramtypes, whatis(f.Type))
}
for _, f := range r {
fx.Results = append(fx.Results, whatis(f.Type))
}
types = append(types, fx)
byname[fx.Internal] = fx
}
return false
}
func whatis(x ast.Expr) string {
switch n := x.(type) {
case *ast.SelectorExpr:
return whatis(n.X) + "." + n.Sel.String()
case *ast.StarExpr:
return "*" + whatis(n.X)
case *ast.Ident:
if isExported(n.Name) {
// these are from package protocol
return "protocol." + n.Name
}
return n.Name
case *ast.ArrayType:
return "[]" + whatis(n.Elt)
case *ast.InterfaceType:
return "interface{}"
default:
log.Fatalf("Fatal %T", x)
return fmt.Sprintf("%T", x)
}
}
func isExported(n string) bool {
return n[0] >= 'A' && n[0] <= 'Z'
}

View File

@ -103,193 +103,15 @@ type sentDiagnostics struct {
snapshotID uint64
}
// General
func (s *Server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
return s.initialize(ctx, params)
}
func (s *Server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
return s.initialized(ctx, params)
}
func (s *Server) Shutdown(ctx context.Context) error {
return s.shutdown(ctx)
}
func (s *Server) Exit(ctx context.Context) error {
return s.exit(ctx)
}
func (s *Server) CancelRequest(ctx context.Context, params *protocol.CancelParams) error {
func (s *Server) cancelRequest(ctx context.Context, params *protocol.CancelParams) error {
return nil
}
// Workspace
func (s *Server) DidChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
return s.changeFolders(ctx, params.Event)
func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
return nil, nil
}
func (s *Server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
return s.updateConfiguration(ctx, params.Settings)
}
func (s *Server) DidChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error {
return s.didChangeWatchedFiles(ctx, params)
}
func (s *Server) Symbol(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
return nil, notImplemented("Symbol")
}
func (s *Server) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
return s.executeCommand(ctx, params)
}
// Text Synchronization
func (s *Server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
return s.didOpen(ctx, params)
}
func (s *Server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
return s.didChange(ctx, params)
}
func (s *Server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
return notImplemented("WillSave")
}
func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("WillSaveWaitUntil")
}
func (s *Server) DidSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error {
return s.didSave(ctx, params)
}
func (s *Server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
return s.didClose(ctx, params)
}
// Language Features
func (s *Server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
return s.completion(ctx, params)
}
func (s *Server) Resolve(ctx context.Context, item *protocol.CompletionItem) (*protocol.CompletionItem, error) {
return nil, notImplemented("completionItem/resolve")
}
func (s *Server) Hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
return s.hover(ctx, params)
}
func (s *Server) SignatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
return s.signatureHelp(ctx, params)
}
func (s *Server) Definition(ctx context.Context, params *protocol.DefinitionParams) (protocol.Definition, error) {
return s.definition(ctx, params)
}
func (s *Server) TypeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) (protocol.Definition, error) {
return s.typeDefinition(ctx, params)
}
func (s *Server) Implementation(ctx context.Context, params *protocol.ImplementationParams) (protocol.Definition, error) {
return s.implementation(ctx, params)
}
func (s *Server) References(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
return s.references(ctx, params)
}
func (s *Server) DocumentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
return s.documentHighlight(ctx, params)
}
func (s *Server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
return s.documentSymbol(ctx, params)
}
func (s *Server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
return s.codeAction(ctx, params)
}
func (s *Server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
return nil, nil // ignore
}
func (s *Server) ResolveCodeLens(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
return nil, notImplemented("ResolveCodeLens")
}
func (s *Server) DocumentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
return s.documentLink(ctx, params)
}
func (s *Server) ResolveDocumentLink(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
return nil, notImplemented("ResolveDocumentLink")
}
func (s *Server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
return nil, notImplemented("DocumentColor")
}
func (s *Server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
return nil, notImplemented("ColorPresentation")
}
func (s *Server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
return s.formatting(ctx, params)
}
func (s *Server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("RangeFormatting")
}
func (s *Server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("OnTypeFormatting")
}
func (s *Server) Rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
return s.rename(ctx, params)
}
func (s *Server) Declaration(context.Context, *protocol.DeclarationParams) (protocol.Declaration, error) {
return nil, notImplemented("Declaration")
}
func (s *Server) FoldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
return s.foldingRange(ctx, params)
}
func (s *Server) LogTraceNotification(context.Context, *protocol.LogTraceParams) error {
return notImplemented("LogtraceNotification")
}
func (s *Server) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (interface{}, error) {
// TODO(suzmue): support sending placeholder text.
return s.prepareRename(ctx, params)
}
func (s *Server) Progress(context.Context, *protocol.ProgressParams) error {
return notImplemented("Progress")
}
func (s *Server) SetTraceNotification(context.Context, *protocol.SetTraceParams) error {
return notImplemented("SetTraceNotification")
}
func (s *Server) SelectionRange(context.Context, *protocol.SelectionRangeParams) ([]protocol.SelectionRange, error) {
return nil, notImplemented("SelectionRange")
}
// Nonstandard requests
func (s *Server) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
paramMap := params.(map[string]interface{})
if method == "gopls/diagnoseFiles" {
for _, file := range paramMap["files"].([]interface{}) {
@ -323,3 +145,5 @@ func (s *Server) NonstandardRequest(ctx context.Context, method string, params i
func notImplemented(method string) *jsonrpc2.Error {
return jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not yet implemented", method)
}
//go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .

185
internal/lsp/server_gen.go Normal file
View File

@ -0,0 +1,185 @@
package lsp
// code generated by helper. DO NOT EDIT.
import (
"context"
"golang.org/x/tools/internal/lsp/protocol"
)
func (s *Server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
return s.codeAction(ctx, params)
}
func (s *Server) CodeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
return s.codeLens(ctx, params)
}
func (s *Server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
return nil, notImplemented("ColorPresentation")
}
func (s *Server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
return s.completion(ctx, params)
}
func (s *Server) Declaration(context.Context, *protocol.DeclarationParams) (protocol.Declaration, error) {
return nil, notImplemented("Declaration")
}
func (s *Server) Definition(ctx context.Context, params *protocol.DefinitionParams) (protocol.Definition, error) {
return s.definition(ctx, params)
}
func (s *Server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
return s.didChange(ctx, params)
}
func (s *Server) DidChangeConfiguration(ctx context.Context, changed *protocol.DidChangeConfigurationParams) error {
return s.didChangeConfiguration(ctx, changed)
}
func (s *Server) DidChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error {
return s.didChangeWatchedFiles(ctx, params)
}
func (s *Server) DidChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
return s.didChangeWorkspaceFolders(ctx, params)
}
func (s *Server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
return s.didClose(ctx, params)
}
func (s *Server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
return s.didOpen(ctx, params)
}
func (s *Server) DidSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error {
return s.didSave(ctx, params)
}
func (s *Server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
return nil, notImplemented("DocumentColor")
}
func (s *Server) DocumentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
return s.documentHighlight(ctx, params)
}
func (s *Server) DocumentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
return s.documentLink(ctx, params)
}
func (s *Server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
return s.documentSymbol(ctx, params)
}
func (s *Server) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
return s.executeCommand(ctx, params)
}
func (s *Server) Exit(ctx context.Context) error {
return s.exit(ctx)
}
func (s *Server) FoldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
return s.foldingRange(ctx, params)
}
func (s *Server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
return s.formatting(ctx, params)
}
func (s *Server) Hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
return s.hover(ctx, params)
}
func (s *Server) Implementation(ctx context.Context, params *protocol.ImplementationParams) (protocol.Definition, error) {
return s.implementation(ctx, params)
}
func (s *Server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
return s.initialize(ctx, params)
}
func (s *Server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
return s.initialized(ctx, params)
}
func (s *Server) LogTraceNotification(context.Context, *protocol.LogTraceParams) error {
return notImplemented("LogTraceNotification")
}
func (s *Server) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
return s.nonstandardRequest(ctx, method, params)
}
func (s *Server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("OnTypeFormatting")
}
func (s *Server) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (interface{}, error) {
return s.prepareRename(ctx, params)
}
func (s *Server) Progress(context.Context, *protocol.ProgressParams) error {
return notImplemented("Progress")
}
func (s *Server) RangeFormatting(context.Context, *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("RangeFormatting")
}
func (s *Server) References(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
return s.references(ctx, params)
}
func (s *Server) Rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
return s.rename(ctx, params)
}
func (s *Server) Resolve(context.Context, *protocol.CompletionItem) (*protocol.CompletionItem, error) {
return nil, notImplemented("Resolve")
}
func (s *Server) ResolveCodeLens(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
return nil, notImplemented("ResolveCodeLens")
}
func (s *Server) ResolveDocumentLink(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
return nil, notImplemented("ResolveDocumentLink")
}
func (s *Server) SelectionRange(context.Context, *protocol.SelectionRangeParams) ([]protocol.SelectionRange, error) {
return nil, notImplemented("SelectionRange")
}
func (s *Server) SetTraceNotification(context.Context, *protocol.SetTraceParams) error {
return notImplemented("SetTraceNotification")
}
func (s *Server) Shutdown(ctx context.Context) error {
return s.shutdown(ctx)
}
func (s *Server) SignatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
return s.signatureHelp(ctx, params)
}
func (s *Server) Symbol(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
return nil, notImplemented("Symbol")
}
func (s *Server) TypeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) (protocol.Definition, error) {
return s.typeDefinition(ctx, params)
}
func (s *Server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
return notImplemented("WillSave")
}
func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
return nil, notImplemented("WillSaveWaitUntil")
}

View File

@ -13,7 +13,8 @@ import (
errors "golang.org/x/xerrors"
)
func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFoldersChangeEvent) error {
func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
event := params.Event
for _, folder := range event.Removed {
view := s.session.View(folder.Name)
if view != nil {
@ -41,7 +42,7 @@ func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source
return s.session.NewView(ctx, name, uri, options)
}
func (s *Server) updateConfiguration(ctx context.Context, changed interface{}) error {
func (s *Server) didChangeConfiguration(ctx context.Context, changed interface{}) error {
// go through all the views getting the config
for _, view := range s.session.Views() {
options := s.session.Options()