This repository contains an implementation of the Microsoft Language Server Protocol for Ada/SPARK and GPR project files.
Current features (general):
- GNAT project files support.
- Basic Alire support.
For Ada/SPARK, we provide the following:
- Code completion for names, keywords, aggregates, etc.
- Code navigation, such as Go to Definition/Declaration, Find All References, Call Hierarchies, etc.
- Code refactoring like insert named associations, auto-add
with
-clauses, etc. - Document/Workspace symbol search.
- Code folding and formatting.
The Ada Language Server now also supports the GPR language, via the
--language-gpr
option, providing support for the most used LSP features
such as navigation, outline and tooltips for GPR files. When this switch is
present, the server will only support GPR files. To support both GPR and
Ada/SPARK, you'll need to launch two instances of the server.
You can refer to the Supported LSP Server Requests
section for more information.
We also provide Visual Studio Code extension at the VS Marketplace and at the Open VSX Registry.
- Ada Language Server
You can build language server from sources. To build it from sources install dependencies and run
make
It will build .obj/server/ada_language_server
file.
To build the language server you need:
- The GNAT compiler (at least GCC 11 or GNAT Community Edition 2021)
- The Libadalang library (it should be built)
- The Libadalang-tools library
- The VSS library
- The gnatdoc library
- The gpr library
- The process spawn library
- The lal-refactor library
- The templates-parser library
Project files of the libraries must be available via the GPR_PROJECT_PATH
environment variable.
If you intend to use VS Code on this workspace, it is recommended to check out
these dependencies under subprojects/
or install them under
subprojects/prefix
. That will make them automatically visible to the VS Code
Ada extension in this workspace.
To run the language server you need gnatls
(part of GNAT installation)
somewhere in the path.
The ada_language_server
doesn't require any command line options,
but it understands these options:
--tracefile=<FILE>
- Full path to a file containing traces configuration--config=<FILE>
- Full path to a JSON file containing the server's configuration--help
- Display supported command like options and exit.
You can turn some debugging and experimental features through the traces file.
The server also gets configuration via workspace/didChangeConfiguration
notification and initializationOptions
of initialize
request.
See more details here. Each LSP
client provides its-own way to set such settings. You can use the --config
option if you want to provide the configuration directly via a JSON file
instead of specifying it via the requests listed just above.
The ada_language_server
relies on Libadalang to compute the cross references.
Most of this computation is done while indexing which will create an internal cache.
The expected memory size of this cache is around 300Mb per 100k lines of Ada code.
Furthermore, 450Mb are necessary for the runtime.
Please note that some Ada structures like generics and tagged types might
increase the memory usage. This is also the case when using aggregate projects.
These measures were taken using both Resident Set Size and Valgrind massif on Ubuntu 22.04LTS.
See WiKi page for the list of supported requests.
The Ada Language Server supports some features that are not in the official Language Server Protocol specification. See corresponding document.
A VS Code extension based on this Ada Language Server is available on the Visual Studio Marketplace. It provides a full set of features including syntax highlighting, navigation, building and debugging.
Here are some links that will help you get familiar with the VS Code extension for Ada & SPARK:
You can configure the extension via the .vscode/settings.json
workspace settings file or the multi-root workspace file.
See the setting list here.
Here is an example config file:
{
"ada.projectFile": "gnatcov.gpr",
"ada.scenarioVariables": {
"BINUTILS_BUILD_DIR": "/null",
"BINUTILS_SRC_DIR": "/null"
},
"ada.defaultCharset": "utf-8",
"ada.enableDiagnostics": false,
"ada.renameInComments": false
}
See a dedicated document with the list of available refactorings.
The extension provides the following auto-detected tasks
(under /Terminal/Run Task...
menu):
ada: Build current project
- launchgprbuild
to build the current GPR projectada: Check current file
- launchgprbuild
to check errors in the current editorada: Clean current project
- launchgprclean
to clean the current GPR projectspark: Examine project
- launchgnatprove
in flow analysis mode on the current GPR projectspark: Examine file
- launchgnatprove
in flow analysis mode on the file in the current editorspark: Examine subprogram
- launchgnatprove
in flow analysis mode on the current subprogram in the current editorspark: Prove project
- launchgnatprove
on the current GPR projectspark: Prove file
- launchgnatprove
on the file in the current editorspark: Prove subprogram
- launchgnatprove
on the current subprogram in the current editorspark: Prove selected region
- launchgnatprove
on the selected region in the current editorspark: Prove line
- launchgnatprove
on the cursor line in the current editorspark: Clean project for proof
- launchgnatprove
on the current GPR project to clean proof artefactsada: Analyze the project with GNAT SAS
ada: Analyze the current file with GNAT SAS
ada: Create a report after a GNAT SAS analysis
ada: Analyze the project with GNAT SAS and produce a report
You can bind keyboard shortcuts to them by adding to the keybindings.json
file:
{
"key": "alt+v",
"command": "workbench.action.tasks.runTask",
"args": "ada: Check current file",
"when": "editorLangId == ada"
}
You can customize auto-detected tasks
by providing extra tool command line options via the args
property of the object in the tasks.json
:
{
"version": "2.0.0",
"tasks": [
{
"type": "ada",
"command": "gprbuild",
"args": [
"${command:ada.gprProjectArgs}",
"-cargs:ada",
"-gnatef",
"-gargs",
"-vh"
],
"problemMatcher": ["$ada"],
"group": "build",
"label": "ada: Build current project"
}
]
}
You can also customize the working directory of the task or the environment variables via the options
property:
{
"version": "2.0.0",
"tasks": [
{
"type": "ada",
"command": "gprbuild",
"args": [
"${command:ada.gprProjectArgs}",
"-cargs:ada",
"-gnatef"
],
"options": {
"cwd": "${workspaceFolder}/my/subdir",
"env": {
"MY_ENV_VAR": "value"
}
},
"problemMatcher": ["$ada"],
"group": "build",
"label": "ada: Build current project"
}
]
}
If your GPR project defines main programs via the project attribute Main
, additional tasks are automatically provided for each defined main.
For example, if the project defines a main1.adb
and main2.adb
located under the src/
source directory, the following tasks will be available:
ada: Build main - src/main1.adb
ada: Run main - src/main1.adb
ada: Build and run main - src/main1.adb
ada: Build main - src/main2.adb
ada: Run main - src/main2.adb
ada: Build and run main - src/main2.adb
If you install GNATtest, the Ada & SPARK extension for VS Code will provide the following functionalities:
-
The task
ada: Create/update test skeletons for the project
will callgnattest
to create test skeletons for your project automatically. You can use standard VS Code task customization to configure command line arguments to your liking in atasks.json
file. -
Tests created with GNATtest will be loaded in the VS Code Testing view as follows.
-
Tests can be executed individually or in batch through the available buttons in the interface, or through the
Test: Run All Tests
command or related commands. -
Test execution results are reflected in the test tree.
GNATtest support has the following known limitations:
-
The extension relies on the default conventions of GNATtest such as the naming, location and object directory of the test harness project. If those aspects are configured or altered manually, the features may no longer work.
-
Test execution always starts with a
gprbuild
call on the test harness project. It is not possible to disable that call or customize its arguments. This limitation will be lifted in future releases. -
Language support such as navigation and auto-completion is limited when editing test sources. A workaround is to modify the
ada.projectFile
setting to point to the test harness project created by GNATtest. That should restore language support when developing tests. -
Sections of test sources delimited by
begin read only
andend read only
comments are not really protected from inadvertant edits. To ensure proper interactions with GNATtest, you must refrain from making edits in those sections.
When the workspace is an ALIRE project (i.e. it contains an alire.toml
file), tasks automatically use standard ALIRE commands.
For example, the ada: Build current project
task uses the command alr build
and the ada: Clean current project
task uses the command alr clean
.
All other tasks use alr exec -- ...
to execute the command in the environment provided by ALIRE.
The extension contributes commands and a few default key bindings.
Below are a few examples, and other commands can be found by searching for Ada:
in the command list.
This command switches between specification and implementation Ada files.
The default shortcut is Alt+O
.
This command inserts a comment box before the current subprogram body.
The default shortcut is Alt+Shift+B
.
This command reloads the current project.
The default shortcut is None
.
The following default shortcuts are provided for tasks:
Task | Shortcut |
---|---|
spark: Prove file |
Meta+Y Meta+F |
spark: Prove subprogram |
Meta+Y Meta+S |
spark: Prove selected region |
Meta+Y Meta+R |
spark: Prove line |
Meta+Y Meta+L |
Meta
= ⌘ on macOS, Win
on Windows, Meta
on Linux
These shortcuts can be customized and new shortcuts can be added for other tasks by using the command Preferences: Open Keyboard Shortcuts (JSON)
and adding entries like the following example:
{
"command": "workbench.action.tasks.runTask",
"args": "ada: Check current file",
"key": "meta+y meta+c",
"when": "editorLangId == ada && editorTextFocus"
}
You can use the VS Code Issue Reporter
to report issues. Just click on the Help -> Report Issue
menu, select An extension
for the File on
entry and Language Support for Ada
for the extension name. Put as many information you can in the description, like steps to reproduce, stacktraces or system information (VS Code automatically includes it by default). This will create a GitHub issue in the Ada Language Server repository.
ALS log files can be found under the ~/.als
directory (%USERPROFILE%/.als
on Windows). Feel free to attach them on the issues, it helps a lot for further investigation, specially when the ALS.IN
and ALS.OUT
traces are enabled (more info about traces configuration can be found here.)
The VS Code extension has a few limitations and some differences compared to GNAT Studio:
-
Indentation/formatting: it does not support automatic indentation when adding a newline and range/document formatting might no succeed on incomplete/illegal code.
-
Tooling support: we currently provide support for some SPARK, GNATtest and GNAT SAS Tasks, but there is no support for tools such as GNATcheck or GNATcoverage yet.
-
Alire support: if the root folder contains an
alire.toml
file and there isalr
executable in thePATH
, then the language server fetches the project's search path, environment variables and the project's file name from the crate description. Tasks are also automatically invoked with ALIRE in this case. -
Project support: there is no
Scenario
view: users should configure scenarios via the ada.scenarioVariables setting (see the settings list available here). Saving the settings file after changing the values will automatically reload the project and update the predefined tasks to take into account the new scenario values.Source directories from imported projects should be added in a workspace file. If you already have a workspace file, the extension will propose you to automatically add all the source directories coming from imported projects to your workspace automatically at startup.
If you want to use the Ada Language Server with Vim/Neovim, you can use the
Coc.NVim. You'll have to
install the Ada Language Server manually somewhere on your
computer. Follow installation instructions on Coc.NVim website and then
configure the Ada Language Server with :CocConfig
:
{
"languageserver": {
"ada": {
"settings": {
"ada": {
"projectFile": "gnat/vss_text.gpr"
}
},
"command": "<path>/ada_language_server",
"filetypes": [
"ads",
"adb",
"ada"
]
}
}
}
If you want to integrate the Ada Language Server into vim, you can use the vim-lsp.
You'll have to install the Ada Language Server manually somewhere on your
computer, and then you can add the following line to your .vimrc
file:
if executable('ada_language_server')
au User lsp_setup call lsp#register_server({
\ 'name': 'ada_language_server',
\ 'cmd': ['ada_language_server'],
\ 'allowlist': ['ada'],
\ 'workspace_config': {'ada': {
\ 'projectFile': "project.gpr",
\ 'scenarioVariables': {"ARCH": "x86_64-pc-linux-gnu"}}},
\ })
endif
If you want to integrate the Ada Language Server into Neovim, you can use the LanguageClient-neovim.
You'll have to install the Ada Language Server manually somewhere on your
computer, and then you can add the following line to your init.vim
file:
" replace the path below with the proper path to the ada_language_server executable
let g:LanguageClient_serverCommands = {
\ 'ada': ['path/to/ada_language_server'],
\ }
" if you already have LanguageClient_serverCommands, just add a line for ada.
To configure the Ada Language Server for a specific workspace/project, you can
use the .vim/settings.json
file. It is mandatory as soon as you want to use a
specific .gpr
project file.
This is the way to specify a project file, eg. you cannot open a project file another way. See the setting list here.
Here is an example of a settings file:
{
"ada.projectFile": "project.gpr",
"ada.scenarioVariables": {
"GLFW_Version": "3",
"GLFW_Lib": "-lglfw",
"Windowing_System": "x11"
}
}
The location where the .vim
folder is located will determine the relative
path of the project file (so no need to prefix with ..
). When vim is opened
in the folder containing this .vim
directory, it will use those settings for
the language server even for files which might have nothing to do with that
specific project, so this needs to be taken into account. Ultimately what this
means is that the configuration is determined by where you open vim.
Neovim 0.5.0 and later have a built-in LSP client which can be used with the Ada Language Server. In order to use it with minimal effort, follow these steps:
- Install the ada language server and make sure it's in your $PATH.
- Use your favorite Neovim plugin manager to add the default set of LSP configuration files to Neovim.
- Add
require('lspconfig').ada_ls.setup{}
to your init.lua in order to enable the Ada Language Server.
If you would rather not have the ada language server in your path, you can give the lsp client an absolute path to the ALS executable:
require('lspconfig').ada_ls.setup{ cmd = "/path/to/als/executable" }
Configuring the language server's settings can be achieved like this:
require('lspconfig').ada_ls.setup{
settings = {
ada = {
projectFile = "project.gpr";
scenarioVariables = { ... };
}
}
}
The Ada Language Server's settings are described here. Configuring neovim to use project-specific settings is described neovim's lspconfig wiki
The configuration for each project can be provided using a .dir-locals.el
file defined at the root of each project.
The scenario variables should be declared in your .emacs
or any loaded
Emacs configuration file.
(defgroup project-build nil
"LSP options for Project"
:group 'ada-mode)
(defcustom project-build-type "Debug"
"Controls the type of build of a project.
Default is Debug, other choices are Release and Coverage."
:type '(choice
(const "Debug")
(const "Coverage")
(const "Release"))
:group 'project-build)
Your .dir-locals.el
in the project root should be similar to:
((ada-mode .
((eval . (lsp-register-custom-settings
'(("ada.scenarioVariables.BINUTILS_SRC_DIR" project-binutils-dir)
("ada.scenarioVariables.BUILD_TYPE" project-build-type "Release"))))
(lsp-ada-project-file . "/home/username/project/project.gpr"))
))
The lsp-mode provides built-in support
for the ada_language_server
and defines default customizable configuration
values in the lsp-ada
group that can be edited similarly to
lsp-ada-project-file
in the example above.
Starting with version 4.9
, QtCreator supports a LSP plugin. Follow
the official documentation
to configure the Ada Language Server in this plugin. Make sure to set Startup behavior
to Start Server per Project
, otherwise QtCreator won't provide the project root to
the Ada Language Server. QtCreator doesn't send any configuration request to the language server, so the only
option to enable project support is to have a single .gpr
file in the QtCreator
project folder. For a projectless configuration, you could also place all Ada sources in
the project root folder, this should work as well.
- Maintained by AdaCore.
- Original author @MaximReznik.
- Support for the Visual Studio Code classifier and snippets contributed by @Entomy.
Feel free to dive in! Read the developer's guide.
Don't hesitate to open an issue or submit PRs.