A preview of support for the Language Server Protocol is now available for Visual Studio for Mac 7.4 as a separate extension.
A Language Server can provide support for programming language features such as:
- Code completion
- Find references
- Go to definition
- Quick fixes
- Method signature help
- Errors and warnings diagnostics
- Documentation on hover
- Document formatting
- Rename refactoring
The Language Server Protocol provides a way for a client application, such as Visual Studio for Mac, to communicate with the Language Server in order to use the programming language features it supports.
Visual Studio Code supports the Language Server Protocol.
A preview of Language Server Protocol support in Visual Studio 2017 on Windows has also been announced.
Currently there is no support for debugging. Whilst there is a debugging protocol support for this is not currently available in Visual Studio for Mac.
The Language Server Protocol does not provide any support for compiling the code. This would need to be provided by the extension that implements the language client.
More detailed information about the Language Server Protocol can be found on the Language Server Protocol site.
The Creating a Language Client section below will take a more detailed look at how to implement a custom Language Server Client extension in Visual Studio for Mac.
Supports
- MonoDevelop or Visual Studio Mac 7.4 or later.
Source Code
Installation
The Language Server Client extension is available to download from GitHub.
To install the extension open the Extensions Manager by selecting Extensions… from the main menu. Click the Install from file button. Select the .mpack file and then click the Open button.
The extension is also available in the Extensions Manager from the Visual Studio Extension Repository (Beta channel).
Creating a Language Client
The API provided by the Language Server Client extension follows the API defined by the Language Server Protocol extension used by Visual Studio on Windows as closely as possible.
More detailed documentation on the Language Server Client API provided by Visual Studio on Windows is available from the Microsoft docs site.
Prerequisites
The Language Server Client Extension for Visual Studio for Mac should be installed.
Also ensure you have the Addin Maker extension installed. This can be installed by selecting Extensions… from the main menu top open the Extensions Manager. Select the Gallery tab and use the text box at the top right to search for the Addin Maker extension.
Click the Install button to install the Addin Maker extension.
The Addin Maker will be used to create the language client extension.
Creating an IDE Extension project
The starting point is to create an IDE Extension project. This project template is provided by the Addin Maker extension and is available from the New Project dialog, in the Other – IDE Extensions category.
After creating the IDE Extensions project, expand the Dependencies folder to show the Extensions folder. Right click the Extensions folder and select Add Addin Reference.
Find the MonoDevelop.LanguageServer.Client in the Add Extension Reference dialog. Select it to toggle its check box and then click the Add button to reference it.
There are three parts to implementing a language client:
- Enabling Managed Extensibility Framework (MEF) support
- Defining the supported content types
- Implementing the ILanguageClient interface.
First we will take a look at enabling MEF support.
Enabling Managed Extensibility Framework Support
Registration of the language client with Visual Studio for Mac is done using the Managed Extensibility Framework (MEF) which is supported by Visual Studio for Mac.
In order to use MEF in your language client extension you need to indicate to Visual Studio for Mac that it should scan your extension for dependencies. This can be done in the extension’s .addin.xml file by adding the filename of your assembly.
1 2 3 |
|
Content Type Definition
Your language client needs to indicate what files are supported. This is done by defining a content type definition.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
The above content definition indicates that the .foo file extension is supported by the language client.
You can also define a filename, instead of a file extension, as supported by the language client by using the FileName attribute instead of the FileExtension attribute.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
You will need to add a reference to System.ComponentModel.Composition so the Export attribute can be used.
Implementing ILanguageClient
To integrate the Language Server with Visual Studio for Mac the ILanguageClient interface needs to be implemented.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
The class that implements the ILanguageClient interface needs to indicate it supports the content types that you have defined, and it also needs to indicate that it implements the ILanguageClient by using the Export attribute.
1 2 |
|
The language client should return a unique description for the ILanguageClient.Name property.
The two methods that should be implemented are:
OnLoadedAsync is called when Visual Studio for Mac has loaded your extension. To activate your Language Server you need to call StartAsync. The simplest approach is to call StartAsync in the OnLoadedAsync method.
1 2 3 4 |
|
Calling StartAsync will cause Visual Studio for Mac to call the ILanguageClient’s ActivateAsync method. This is where you can start the Language Server.
The ActivateAsync method should return a Connection object with the streams that will be used to communicate with the Language Server. Standard input and output streams are supported as well as sockets.
1
|
|
The first time you try to run and debug your language client extension you will see an error in the Application Output similar to:
1 2 3 |
|
The Addin Maker runs your language client extension using its own extension database. The MonoDevelop.LanguageServer.Client extension needs to be installed into this separate database in order to be able to run and debug your language client extension.
To do this run your language client extension project in Visual Studio for Mac and install the Language Server Client extension using the Extensions Manager in the instance of Visual Studio for Mac that is started. Then stop debugging and re-run your extension project again, and your language client should now be used when you open a supported file.
The content type registration and the ILanguageClient implementation is the minimum that is needed in order to integrate your Language Server with Visual Studio for Mac. Now let us take a look at other optional things that can be implemented.
Receiving Custom Messages
Your Language Server may support messages that are not part of the standard Language Server Protocol.
In order to support receiving custom messages the class that implements ILanguageClient should also implement the ILanguageClientCustomMessage interface. The CustomMessageTarget property should return an object that will receive the messages.
1 2 3 4 5 6 7 |
|
An example CustomMessageTarget class is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Sending Custom Messages
To send custom messages to the Language Server the class that implements the ILanguageClient interface should also implement the ILanguageClientCustomMessage interface.
The AttachForCustomMessageAsync method should save the JsonRpc object passed and then you can use the JsonRpc object to send custom messages to the Language Server.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Middle Layer
The ILanguageClientCustomMessage defines a MiddleLayer property. An object returned from the MiddleLayer property can be used to intercept messages sent to and received from the Language Server. The MiddleLayer object can implement the following interfaces:
- ILanguageClientCompletionProvider
- ILanguageClientExecuteCommandProvider
- ILanguageClientWorkspaceSymbolProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Note that currently not all the Language Server messages can be intercepted.
The full source code to the sample Language Server Client Extension created whilst writing this blog post is available on GitHub
There are other examples included with the Language Server Client extension source code on GitHub, however these are not standalone and are compiled with the Language Server Client extension.