79788

How to implement quickfix via a language server

Question:

I've implemented a language server which provides some linting. The linter checks for required properties and issues 'missing property' errors. I would like to have corresponding 'insert missing property' quickfixes for these errors.

I think the general area of the LSP protocol meant for this is:

<a href="https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_codeAction" rel="nofollow">textDocument/codeAction</a>

With this, the server could return a 'insert missing property' command for a 'missing property' diagnostic marker.

But how does the server implement the 'insert missing property' command itself?

Looking through the lsp spec, I can't find anything that lets the server register commands.

I did find some info about vscode apis for registering commands on the client side here: <a href="https://code.visualstudio.com/docs/extensionAPI/vscode-api" rel="nofollow">https://code.visualstudio.com/docs/extensionAPI/vscode-api</a>

So I suppose I could register and implement the 'insert missing properties' on the client side, but...

The client side is only a 'dumb' wrapper delegating most work to the server. As such it doesn't really understand the document structure and isn't a very good place to implement transformations of the document that require understanding that structure.

It seems my best option is to add some 'custom' protocol to my language server so that I can implement the 'insert missing properties' command on the client side, but delegate the hard part of computing the edits for the quickfix back to the server.

Or... is there a better way?

Answer1:

Yes there is a better way which does not require any custom protocol extensions. These are roughly the steps:

First make sure that your vscode extension's package.json has an up-to-date language-server-client. Mine uses version 3.2.x. I also needed to update vscode engine version to 1.6.x. Here's an <a href="https://github.com/spring-projects/sts4/blob/cc5b70a4f7ee711c52f422fae3d607ea5e2b82cf/vscode-extensions/vscode-concourse/package.json" rel="nofollow">example package.json</a>

Now we can use <a href="https://github.com/Microsoft/language-server-protocol/blob/20cf6216a7e30da2afcafea32e5e2593ebe7c65e/protocol.md" rel="nofollow">Version 3</a> (only a draft at the time of this writing, but already usable) of the language-server protocol. These are the interesting pieces:

<ul><li>

textDocument/codeAction: implement this on the server-side to compute a list of commands that represent the quickfixes.

</li> <li>

workspace/executeCommand: implement this in the server-side to execute the commands. It can make use of workspace/applyEdit to send a request to the client to perform changes to the documents in the workspace.

</li> <li>

client/registerCapability: The server can call this with a ExecuteCommandRegistrationOptions object. This registers your server-side command(s) with the client, so that it knows to execute them via the workspace/executeCommand handler implemented in the previous step.

</li> <li>

initialize: Alternatively to using client/registerCapability you can also register server-side commands by returning a WorkspaceCapabilities object with an appropriate setting for its executeCommandProvider attribute. This method is slightly less complex (but can only be used if you don't require registering/unregistering of commands dynamically).

</li> </ul>

See also this <a href="https://github.com/Microsoft/language-server-protocol/issues/219" rel="nofollow">vscode issue ticket</a> about the topic of implementing quickfixes.

<strong>Important note</strong>: Version 3.2.0 of the language-server client implementation has a <a href="https://github.com/Microsoft/vscode-languageserver-node/issues/199" rel="nofollow">bug</a>, it uses the wrong name client/registerFeature instead of client/registerCapability so you may have to work around that until that bug is fixed. If you use the initialize method then you are not affected by this bug.

Recommend

  • How to create simple IIS site to redirect all calls to another service?
  • Facebook API: I can't post a message to friend's wall or an other's wall
  • Call order of constructors
  • UIView Subview for Animation
  • How to run two instances of GitBash on Windows 8?
  • Javascript: Unexpected Token ILLEGAL Error with appendChild()
  • Selecting an integer from each of the given n sets of integers such that the sum of their pairwise d
  • Java SOCKS proxy
  • Extending enums
  • Polygon infowindows in Gmaps4Rails
  • Request map direct me to Login page in Grails
  • Parse an XML fragment stored in a string into nodes in XSLT with SAXON for Java
  • reduce/reduce conflicts using ocamlyacc
  • Table striping rows in CSS Grid
  • GWT Widget.addHandler
  • Avoid Inheriting Super Class Tests in ScalaTest
  • Python: sending key press events over SSH
  • Error in making a socket connection
  • ZipList with Scalaz
  • What is the difference between a “service account” and an “installed application”?
  • Problem with Django using Apache2 (mod_wsgi), Occassionally is “unable to import from module” for no
  • Python pandas melting data to multiple columns and coulmn names in another column
  • Can I have a variable number of URI parameters or key-value pairs in Laravel 4?
  • abstracting over a collection
  • what makes a request a new request in asp.net C#
  • as3-flash: any way to access all the instances placed in different frames from document class?
  • Spring Cloud Microservice Architecture Confusion
  • Zurb Foundation _global.scss meta styles for js?
  • Does it make sense to call System.gc() and Thread.sleep() when working on Bitmaps?
  • Caching attributes in superclass
  • Transactional Create with Validation in ServiceStack Redis Client
  • How to run “Deployd” on port 80 instead of port 5000 in webserver.
  • Handling un-mapped Rest path
  • PHP - How to update data to MySQL when click a radio button
  • QuartzCore.framework for Mono Develop
  • R: gsub and capture
  • RestKit - RKRequestDelegate does not exist
  • jqPlot EnhancedLegendRenderer plugin does not toggle series for Pie charts
  • Comma separated Values
  • How to load view controller without button in storyboard?