golang.org/x/tools/gopls@v0.15.3/internal/protocol/generate/README.md (about)

     1  # LSP Support for gopls
     2  
     3  ## The protocol
     4  
     5  The LSP protocol exchanges json-encoded messages between the client and the server.
     6  (gopls is the server.) The messages are either Requests, which require Responses, or
     7  Notifications, which generate no response. Each Request or Notification has a method name
     8  such as "textDocument/hover" that indicates its meaning and determines which function in the server will handle it.
     9  The protocol is described in a
    10  [web page](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/),
    11  in words, and in a json file (metaModel.json) available either linked towards the bottom of the
    12  web page, or in the vscode-languageserver-node repository. This code uses the latter so the
    13  exact version can be tied to a githash. By default, the command will download the `github.com/microsoft/vscode-languageserver-node` repository to a temporary directory.
    14  
    15  The specification has five sections
    16  
    17  1. Requests, which describe the Request and Response types for request methods (e.g., *textDocument/didChange*),
    18  2. Notifications, which describe the Request types for notification methods,
    19  3. Structures, which describe named struct-like types,
    20  4. TypeAliases, which describe type aliases,
    21  5. Enumerations, which describe named constants.
    22  
    23  Requests and Notifications are tagged with a Method (e.g., `"textDocument/hover"`).
    24  The specification does not specify the names of the functions that handle the messages. These
    25  names are specified by the `methodNames` map. Enumerations generate Go `const`s, but
    26  in Typescript they are scoped to namespaces, while in Go they are scoped to a package, so the Go names
    27  may need to be modified to avoid name collisions. (See the `disambiguate` map, and its use.)
    28  
    29  Finally, the specified types are Typescript types, which are quite different from Go types.
    30  
    31  ### Optionality
    32  
    33  The specification can mark fields in structs as Optional. The client distinguishes between missing
    34  fields and `null` fields in some cases. The Go translation for an optional type
    35  should be making sure the field's value
    36  can be `nil`, and adding the json tag `,omitempty`. The former condition would be satisfied by
    37  adding `*` to the field's type if the type is not a reference type.
    38  
    39  ### Types
    40  
    41  The specification uses a number of different types, only a few of which correspond directly to Go types.
    42  The specification's types are "base", "reference", "map", "literal", "stringLiteral", "tuple", "and", "or".
    43  The "base" types correspond directly to Go types, although some Go types needs to be chosen for `URI` and `DocumentUri`. (The "base" types`RegExp`, `BooleanLiteral`, `NumericLiteral` never occur.)
    44  
    45  "reference" types are the struct-like types in the Structures section of the specification. The given
    46  names are suitable for Go to use, except the code needs to change names like `_Initialze` to `XInitialize` so
    47  they are exported for json marshaling and unmarshaling.
    48  
    49  "map" types are just like Go. (The key type in all of them is `DocumentUri`.)
    50  
    51  "stringLiteral" types are types whose type name and value are a single string. The chosen Go equivalent
    52  is to make the type `string` and the value a constant. (The alternative would be to generate a new
    53  named type, which seemed redundant.)
    54  
    55  "literal" types are like Go anonymous structs, so they have to be given a name. (All instances
    56  of the remaining types have to be given names. One approach is to construct the name from the components
    57  of the type, but this leads to misleading punning, and is unstable if components are added. The other approach
    58  is to construct the name from the context of the definition, that is, from the types it is defined within.
    59  For instance `Lit__InitializeParams_clientInfo` is the "literal" type at the
    60  `clientInfo` field in the `_InitializeParams`
    61  struct. Although this choice is sensitive to the ordering of the components, the code uses this approach,
    62  presuming that reordering components is an unlikely protocol change.)
    63  
    64  "tuple" types are generated as Go structs. (There is only one, with two `uint32` fields.)
    65  
    66  "and" types are Go structs with embedded type names. (There is only one, `And_Param_workspace_configuration`.)
    67  
    68  "or" types are the most complicated. There are a lot of them and there is no simple Go equivalent.
    69  They are defined as structs with a single `Value interface{}` field and custom json marshaling
    70  and unmarshaling code. Users can assign anything to `Value` but the type will be checked, and
    71  correctly marshaled, by the custom marshaling code. The unmarshaling code checks types, so `Value`
    72  will have one of the permitted types. (`nil` is always allowed.) There are about 40 "or" types that
    73  have a single non-null component, and these are converted to the component type.
    74  
    75  ## Processing
    76  
    77  The code parses the json specification file, and scans all the types. It assigns names, as described
    78  above, to the types that are unnamed in the specification, and constructs Go equivalents as required.
    79  (Most of this code is in typenames.go.)
    80  
    81  There are four output files. tsclient.go and tsserver.go contain the definition and implementation
    82  of the `protocol.Client` and `protocol.Server` types and the code that dispatches on the Method
    83  of the Request or Notification. tsjson.go contains the custom marshaling and unmarshaling code.
    84  And tsprotocol.go contains the type and const definitions.
    85  
    86  ### Accommodating gopls
    87  
    88  As the code generates output, mostly in generateoutput.go and main.go,
    89  it makes adjustments so that no changes are required to the existing Go code.
    90  (Organizing the computation this way makes the code's structure simpler, but results in
    91  a lot of unused types.)
    92  There are three major classes of these adjustments, and leftover special cases.
    93  
    94  The first major
    95  adjustment is to change generated type names to the ones gopls expects. Some of these don't change the
    96  semantics of the type, just the name.
    97  But for historical reasons a lot of them replace "or" types by a single
    98  component of the type. (Until fairly recently Go only saw or used only one of components.)
    99  The `goplsType` map in tables.go controls this process.
   100  
   101  The second major adjustment is to the types of fields of structs, which is done using the
   102  `renameProp` map in tables.go.
   103  
   104  The third major adjustment handles optionality, controlling `*` and `,omitempty` placement when
   105  the default rules don't match what gopls is expecting. (The map is `goplsStar`, also in tables.go)
   106  (If the intermediate components in expressions of the form `A.B.C.S` were optional, the code would need
   107  a lot of useless checking for nils. Typescript has a language construct to avoid most checks.)
   108  
   109  Then there are some additional special cases. There are a few places with adjustments to avoid
   110  recursive types. For instance `LSPArray` is `[]LSPAny`, but `LSPAny` is an "or" type including `LSPArray`.
   111  The solution is to make `LSPAny` an `interface{}`. Another instance is `_InitializeParams.trace`
   112  whose type is an "or" of 3 stringLiterals, which just becomes a `string`.
   113  
   114  ### Checking
   115  
   116  `TestAll(t *testing.T)` checks that there are no unexpected fields in the json specification.
   117  
   118  While the code is executing, it checks that all the entries in the maps in tables.go are used.
   119  It also checks that the entries in `renameProp` and `goplsStar` are not redundant.
   120  
   121  As a one-time check on the first release of this code, diff-ing the existing and generated tsclient.go
   122  and tsserver.go code results in only whitespace and comment diffs. The existing and generated
   123  tsprotocol.go differ in whitespace and comments, and in a substantial number of new type definitions
   124  that the older, more heuristic, code did not generate. (And the unused type `_InitializeParams` differs
   125  slightly between the new and the old, and is not worth fixing.)
   126  
   127  ### Some history
   128  
   129  The original stub code was written by hand, but with the protocol under active development, that
   130  couldn't last. The web page existed before the json specification, but it lagged the implementation
   131  and was hard to process by machine. So the earlier version of the generating code was written in Typescript, and
   132  used the Typescript compiler's API to parse the protocol code in the repository.
   133  It then used a set of heuristics
   134  to pick out the elements of the protocol, and another set of overlapping heuristics to create the Go code.
   135  The output was functional, but idiosyncratic, and the code was fragile and barely maintainable.
   136  
   137  ### The future
   138  
   139  Most of the adjustments using the maps in tables.go could be removed by making changes, mostly to names,
   140  in the gopls code. Using more "or" types in gopls requires more elaborate, but stereotyped, changes.
   141  But even without all the adjustments, making this its own module would face problems; a number of
   142  dependencies would have to be factored out. And, it is fragile. The custom unmarshaling code knows what
   143  types it expects. A design that return an 'any' on unexpected types would match the json
   144  'ignore unexpected values' philosophy better, but the Go code would need extra checking.