AdaCore / ada_language_server

Server implementing the Microsoft Language Protocol for Ada and SPARK
GNU General Public License v3.0
240 stars 55 forks source link

Ada Language Server

Build binaries GitHub tag (latest by date) VS Marketplace Open VSX Registry Gitpod ready-to-code

This repository contains an implementation of the Microsoft Language Server Protocol for Ada/SPARK and GPR project files.

Current features (general):

For Ada/SPARK, we provide the following:

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.

Table of Contents

Install

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.

Dependencies

To build the language server you need:

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.

Usage

The ada_language_server doesn't require any command line options, but it understands these options:

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.

Memory Consumption

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.

Supported LSP Server Requests

See WiKi page for the list of supported requests.

Protocol Extensions

The Ada Language Server supports some features that are not in the official Language Server Protocol specification. See corresponding document.

VS Code Extension

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.

Getting Started

Here are some links that will help you get familiar with the VS Code extension for Ada & SPARK:

Configuration

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
}

Refactoring

See a dedicated document with the list of available refactorings.

Tasks

The extension provides the following auto-detected tasks (under /Terminal/Run Task... menu):

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"
}

Task Customization

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"
      }
   ]
}

Tasks for Project Mains

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:

GNATtest Support

If you install GNATtest, the Ada & SPARK extension for VS Code will provide the following functionalities:

GNATtest support has the following known limitations:

ALIRE Support

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.

Commands and Shortcuts

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.

Ada: Go to other file

This command switches between specification and implementation Ada files. The default shortcut is Alt+O.

Ada: Add subprogram box

This command inserts a comment box before the current subprogram body. The default shortcut is Alt+Shift+B.

Ada: Reload project

This command reloads the current project. The default shortcut is None.

Tasks with keyboard shortcuts

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"
}

Bug Reporting

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.)

Limitations and Differences with GNAT Studio

The VS Code extension has a few limitations and some differences compared to GNAT Studio:

Integration with other editors and IDEs

Integration with Coc.NVim

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"
      ]
    }
  }
}

Integration with vim-lsp

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

Integration with LanguageClient-Neovim

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.

Integration with Neovim's built-in LSP client

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:

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

Integration with emacs lsp-mode

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.

Integration with QtCreator

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.

Refactoring Tools

See corresponding document.

Authors & Contributors

Contribute

Feel free to dive in! Read the developer's guide.

Don't hesitate to open an issue or submit PRs.

License

GPL-3