This repository contains a small Emacs configuration to support Ada development, which includes the installation and configuration of packages for Ada and GNAT Project (GPR) files. This also includes the installation of LSP client packages eglot
and lsp-mode
. The specific LSP client to use is defaulted, but can be overridden in early-init.el
. Emacs 29 is required in order to use the tree-sitter based packages included in this configuration.
The expectation is that this configuration will be used as an example configuration and not the user’s default configuration. Therefore, the following instructions place the configuration in a non-standard location and then specify that location when starting Emacs.
Note: If using ~/.emacs
as your configuration file, use of --init-directory
does not work as expected. Therefore, you’ll need to rename ~/.emacs
so that the configuration in this repository can be used correctly.
git clone https://github.com/brownts/dotemacs-ada ~/.emacs-ada.d
emacs --init-directory=~/.emacs-ada.d
Once started, the packages identified in the configuration (i.e., init.el
) will be downloaded from an ELPA and installed.
By default, the tree-sitter library for Ada and GPR will be downloaded and built from source when the major mode is loaded. If you’d prefer to install a pre-built language library instead, you can obtain these from the following location:
If manually installing, the shard libraries should be placed in the tree-sitter
directory beneath user-emacs-directory
. If using the installation instructions above, that would be ~/.emacs-ada.d/tree-sitter/
.
The Emacs configuration contains several points at which customization may be applied. These user options are specified at the beginning of the init.el
file with a series of defcustom
declarations. The expectation is that any user-specific customization should be applied in the early-init.el
file, thus leaving the init.el
unmodified. The early-init.el
file already contains a number of commented lines for toggling many of the configurations. To override the setting’s default value, it is as simple as removing the comment prefix from the corresponding line. The following describes the available settings and their defaults.
- User Option: init.el/completion-lsp-allow-trigger-chars
-
Whether trigger characters specified by the Language Server can cause the completion UI to be displayed. Typically, these are characters such as “(” and “,” which trigger completion for subroutine parameters, although use of ” ” (i.e., space) is also common. If the Language Server is overzealous, the frequency of the completion UI being displayed can become a nuisance, therefore it may be desirable to disable this feature. Furthermore, trigger characters can allow the completion UI to be displayed without having to meet the
init.el/completion-minimum-prefix-length
specified limit, therefore it’s very common for the UI to be displayed after only having typed a space. (default:t
) - User Option: init.el/completion-lsp-disallowed-contexts
-
Specifies the contexts under which LSP completions are to be avoided. This can be helpful when the Language Server provides nonsensical completion suggestions, such as in comments and strings. (default:
(comment string)
) - User Option: init.el/completion-minimum-prefix-length
-
Specifies the number of characters which should be typed before available completions cause the completion UI to be displayed. This can be increased or decreased as desired and is a personal preference on what constitutes the right balance between helpfulness and annoyance. Completion can always be triggered manually (via
TAB
– seetab-always-indent
documentation for details) when desired. Setting this value to 0 will cause the completion UI to be displayed as soon as any completions are available. (default: 2) - User Option: init.el/completion-quick-access
-
Indicates whether quick access key bindings are active and visible when the completion UI is displayed. When enabled, a numeric index will appear to the left of each candidate in the completion UI, where the index <i> corresponds to the M-<i> key binding. Note that an index of 10 corresponds to a key binding of M-0. Quick access key bindings provide immediate access to a candidate rather than requiring the user to navigate through each intermediate candidate to reach the desired one. (default:
nil
) - User Option: init.el/preferred-completion-ui
-
Indicates the preferred completion framework to use when displaying completion candidates to the user. Emacs supports multiple completion frameworks and this provides a way to choose the preferred framework. (default:
corfu
) - User Option: init.el/preferred-diagnostics-reporter
-
Specifies the diagnostic framework used for reporting warnings, errors, etc. While these frameworks provide their own buffers for displaying diagnostics, an additional convenience package is provided to quickly display, visit and go to diagnostics in the buffer, bound to
M-g M-d
. Where supported,C-u M-g M-d
will display diagnostics for the entire project instead of just the buffer. (default:flymake
) - User Option: init.el/preferred-documentation-ui
-
Typically, the echo area is used to display documentation associated with an item. However, as the amount of documentation increases the constant resizing of Emacs windows in order to show the documentation can quickly become a nuisance. As a solution, another option is to display documentation in a floating child frame (i.e., box) over the Emacs frame, manifesting as a “pop-up window”. This can help to reduce the jarring resizing of Emacs windows. This option controls whether documentation is displayed in the echo area or within a floating child frame box. (default:
box
) - User Option: init.el/preferred-lsp-client
-
Since multiple LSP clients are available within Emacs, not everyone agrees on their preferred client. As a result, the specific LSP client to use is configurable. (default:
lsp-mode
)
As an example of overriding a default configuration, to configure eglot
as the preferred LSP client, use the following in early-init.el
:
(setq init.el/preferred-lsp-client 'eglot)
The lsp-mode
package includes the ability to install the Ada Language Server when lsp-mode
is enabled in an Ada or GPR buffer. If the Ada Language Server is installed in this manner, it will be installed in the .cache/lsp/ada-ls
directory beneath user-emacs-directory
(e.g., ~/.emacs-ada.d/.cache/lsp/ada-ls/
). If the Ada Language Server is found on the path, the server found there will be used instead. If desired, the Language Server can be installed manually by downloading it from the GitHub repository and placing it somewhere on the path.
The eglot
package does not include the ability to install the Ada Language Server. There are two options for installation. The first option is to manually install the Ada Language Server somewhere on the path. The second option is to first use the lsp-mode
package and let it install the server before changing init.el/preferred-lsp-client
from lsp-mode
to eglot
and then restarting Emacs. The Emacs configuration in this repository will add lsp-mode
’s Ada Language Server installation directory to Emacs’ exec-path
so that a previous installation can be used by Eglot.
The following example assumes the Alire tool (i.e., alr
) is already installed. If not, visit alire.ada.dev to download and install it.
alr get gtkada
cd gtkada*
Note: In the above alr get
command, the GtkAda directory name will contain the version and a hash, thus the use of the wildcard when changing into that directory.
Next, create a .dir-locals.el
file with the following contents in the top-level directory of the GtkAda crate directory we just obtained:
((nil . ((lsp-ada-project-file . "src/gtkada.gpr")
(eglot-workspace-configuration . (:ada (:projectFile "src/gtkada.gpr")))
(compile-command . "alr build -- -cargs:ada -gnatef"))))
It should be noted that lsp-ada-project-file
is used by lsp-mode
to inform the Ada Language Server the path to the project file. For Eglot, the same is performed by using the eglot-workspace-configuration
and its associated property list. Refer to the Eglot Project-specific configuration, lsp-mode
Ada Language configuration as well as the Ada Language Server Settings to learn about additional configuration options and the specific formatting of those options required by each LSP client.
Note: Technically it’s not necessary to specify the project file configuration in the .dir-locals.el
if the Ada Language Server can find it in alire.toml
, but the above is used to demonstrate how to manually configure the project file using either of the available Emacs LSP clients, if needed.
The compilation command is also specified here which is used whenever a compilation is performed (e.g., project-compile
via C-x p c
). Since this is an Alire project, an Alire build command is used.
In order to support LSP-based indentation, it is useful to add a Pretty_Printer
section to the GPR file (i.e., in src/gtkada.gpr
) and configure it so that the Language Server formatting engine does not significantly restructure the source.
package Pretty_Printer is
for Default_Switches ("Ada") use ("--source-line-breaks");
end Pretty_Printer;
Open any Ada or GPR file and enjoy! Performing a compilation within Emacs (C-x p c
) will build the project using the compilation command specified in the .dir-locals.el
file created earlier.
emacs --init-directory=~/.emacs-ada.d src/gtkada-application.adb