Ember Language Server in Emacs
Tags: ember, emacs, productivity
Published: 2023-10-05 04:13:45


In this post we are going to see how we can set up the language server for Ember in Emacs.

A language server is a program that provides language-specific features for code editors or integrated development environments (IDEs). It acts as a server, interacting with the editor to offer services such as code completion, error checking, and other language-specific features. In summary, language servers enhance the development experience by providing consistent language support, intelligent features, and improved productivity across various code editors and IDEs.




## Ember.js

Ember.js stands out as a robust and proven JavaScript framework designed to facilitate the development of contemporary web applications. Equipped with comprehensive tools, it empowers developers to create feature-rich user interfaces that seamlessly function across various devices. Widely recognized as a framework tailored for ambitious web developers, Ember.js offers a productive environment for building sophisticated web applications.

## Emacs

GNU Emacs stands as a freely available text editor crafted by Richard Stallman, the founder of the GNU Project. Originating from the Emacs editor designed for Unix operating systems, GNU Emacs has played a pivotal role in the GNU project and serves as a flagship initiative within the free software movement. Its defining characteristic is encapsulated in the tagline "the extensible self-documenting text editor."


## Ember Language Server

The Ember Language Server (ELS) implements the Language Server Protocol for Ember.js projects. ELS enables editors to provide features like auto complete, goto definition and diagnostics. To get these features, you have to install the plugin for your editor.

For this post we are going to use a particular version of ELS created by [Alex Kanunnikov](https://github.com/lifeart). You can find the code here in [ember-language-server](https://github.com/lifeart/ember-language-server)

You need to clone this in your local machine and build it. Then we use the build artifacts to start the language server automatically from Emacs using the LSP tooling.

```
git clone https://github.com/lifeart/ember-language-server
```
There is also another version of language server provided by the official Ember community which you can found [ember-tooling/ember-language-server](https://github.com/ember-tooling/ember-language-server)

## emacs-lsp-mode

The [lsp-mode](https://emacs-lsp.github.io/lsp-mode/) package provides the Language Server Protocol support for Emacs. It is community driven, fully featured, performant, flexible and easy to configure. It supports all features in Language Server Protocol v3.14. You can choose between full-blown IDE with flashy UI or minimal distraction free. It just works out of the box and automatically upgrades if additional packages are present.

Before configuring the lsp-mode for Ember we need to define a new major mode called `hbs-mode` for editing Handlebars templates. Here's how we do it:

```lisp
(define-derived-mode hbs-mode web-mode "Handlebars mode" "Major mode for handlebars")
(add-to-list 'auto-mode-alist '("\\.hbs\\'" . hbs-mode))
```

In the above code we are defining a new major mode named `hbs-mode` by deriving it from the existing mode `web-mode`. The string "Handlebars mode" is the mode's user-friendly name, and "Major mode for handlebars" is its documentation string. In Emacs, major modes are used to provide specialized editing features for specific types of files. In this case, `hbs-mode` is specifically tailored for editing Handlebars templates.

Next we add an entry to the `auto-mode-alist` variable, which is a list of filename patterns and corresponding major modes. When you open a file, Emacs checks this list to determine which major mode to use based on the file's extension. The added entry specifies that any file with the extension ".hbs" should be opened in `hbs-mode`. The regular expression "\\\\ .hbs\\\\'" is used to match file names ending with ".hbs".


The dot (`.`) in the regular expression is a wildcard that matches any character, and the double backslash (`\\`) is used to escape the dot and match it literally. The single-quote (`'`) in the regular expression matches the end of the string.

In short, this code sets up a new major mode called `hbs-mode` for editing Handlebars templates in Emacs. It also ensures that any file with the extension ".hbs" will automatically use this major mode when opened in Emacs.

You need to add the following code to your Emacs configuration file to get the language server working.
```lisp
    (with-eval-after-load 'lsp-mode
     (add-to-list 'lsp-language-id-configuration
       '(hbs-mode . "hbs"))
     (lsp-register-client
      ;; Git clone language server from https://github.com/lifeart/ember-language-server/tree/component-context-info-origin
      ;; And build it
       (make-lsp-client :new-connection (lsp-stdio-connection (list "node" (expand-file-name "~/www/ember-language-server/lib/start-server.js") "--stdio"))
                        :activation-fn (lsp-activate-on "hbs")
                        :server-id 'ember-language-server)))
```
Let me explain what the above code does.

The macro `(with-eval-after-load 'lsp-mode )` ensures that the enclosed code is executed after the `lsp-mode` package has been loaded. It is used to set up configurations or additional actions related to the `lsp-mode` package.

The line `(add-to-list 'lsp-language-id-configuration &#x2026;)` adds an entry to the `lsp-language-id-configuration` variable, which is used to configure language identifiers for the Language Server Protocol (LSP). In this case, it associates the language identifier `'hbs-mode'` with the string `"hbs"`.

The function call `(lsp-register-client &#x2026;)` registers a new LSP client for handling the Ember Language Server. The `:new-connection` property specifies how to establish a connection to the language server. Here, it uses `lsp-stdio-connection` with a list of arguments. It's starting the server using Node.js and pointing to the `start-server.js` file of the Ember Language Server. The `:activation-fn` property specifies when to activate the language server. It uses `lsp-activate-on` with the argument `"hbs"`, meaning the server should be activated when working with files of type `"hbs"`. The `:server-id` property  associates a unique identifier with this server configuration. In this case, it's set to `'ember-language-server'`.

In summary, this code is configuring Emacs to use the Ember Language Server for Handlebars files (`hbs-mode`). The language server is started as a new process using Node.js, and it will be activated whenever a Handlebars file is being edited. The server configuration is registered with the identifier `'ember-language-server'`.

So this is the final and full code for configuring lsp-mode for Ember handlebars in Emacs
```lisp
    
    (define-derived-mode hbs-mode web-mode "Handlebars mode" "Major mode for handlebars")
    (add-to-list 'auto-mode-alist '("\\.hbs\\'" . hbs-mode))
    
    (with-eval-after-load 'lsp-mode
    (add-to-list 'lsp-language-id-configuration
        '(hbs-mode . "hbs"))
    (lsp-register-client
    ;; Git clone language server from https://github.com/lifeart/ember-language-server/tree/component-context-info-origin
    ;; And build it
        (make-lsp-client :new-connection (lsp-stdio-connection (list "node" (expand-file-name "~/www/ember-language-server/lib/start-server.js") "--stdio"))
                        :activation-fn (lsp-activate-on "hbs")
                        :server-id 'ember-language-server)))

```


## eglot

Next we try to achieve the same with another LSP tool in Emacs called [eglot](https://joaotavora.github.io/eglot/)

Eglot is the Emacs client for the Language Server Protocol (LSP). The name “Eglot” is an acronym that stands for “Emacs Polyglot”. Eglot provides infrastructure and a set of commands for enriching the source code editing capabilities of Emacs via LSP. Eglot itself is completely language-agnostic, but it can support any programming language for which there is a language server and an Emacs major mode.
```
    (add-hook 'hbs-mode-hook 'eglot-ensure)
    (with-eval-after-load 'eglot
      (add-to-list 'eglot-server-programs
                   '(hbs-mode . ("node" "/Users/rajasegarchandran/www/ember-language-server/lib/start-server.js" "--stdio"))))
```
Let's break down the code:

First we add a function (`eglot-ensure`) to the `hbs-mode-hook`. In Emacs, a "hook" is a way to run custom code when a certain mode is activated.In this case, it ensures that the eglot server is started when `hbs-mode` is activated.


Now we delay the execution of the code inside it until after the `eglot` package has been loaded. This is useful because it ensures that the configuration is applied after the `eglot` package is available. Next we add an entry to the `eglot-server-programs` list, which specifies the command to start the language server for a particular mode. Then we associate the `hbs-mode` with the command to start the Ember Language Server for Handlebars. The server is started using the "node" executable, and it runs the specified JavaScript file (`start-server.js`) with the "--stdio" option.


The command `node` and its arguments that will be executed when the language server for Handlebars is started.
It launches the Node.js interpreter with the specified JavaScript file and tells it to use the standard I/O for communication.

We basically ensure that the eglot server is started automatically when `hbs-mode` is activated, and it specifies the command to start the language server for Handlebars.

Now you know how to configure and register the Ember Language Server for handling Handlebars (hbs) files within the Emacs text editor.