LSP Client demo - (ba)sh language server

Saturday August 17, 2019

Below is a scenario by Jan Lahoda, the creator of LSP integration for Apache NetBeans, for how to integrate the bash language server with Apache NetBeans, including syntax highlighting.

Setting Up

  1. Install npm (and node.js). On Ubuntu, e.g., do "apt install npm", though something different will be needed on Mac OS X.

  2. Create a directory in which we are going to work, have a terminal opened in that directory.

  3. Install the bash-language-server:

        npm install bash-language-server

    On Mac OSX:

        npm install bash-language-server --unsafe-perm

    This will install the server into the current directory.

  4. Try the bash server:

        ./node_modules/bash-language-server/bin/main.js --help

    You should see something like this:

        Usage:
          bash-language-server start
          bash-language-server -h | --help
          bash-language-server -v | --version
  5. Create a NetBeans module. Create a File Type (Module Development/File Type), mime type: text/sh, file extension: sh

Syntax Coloring via TextMate

  1. Download the TextMate grammar file here, and put it alongside the newly created DataObject: https://raw.githubusercontent.com/microsoft/vscode/master/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json

  2. Add "TextMate Lexer" as a dependency of the module.

  3. Into the DataObject add this annotation:

        @GrammarRegistration(grammar="shell-unix-bash.tmLanguage.json", mimeType="text/sh")

    GrammarRegistration is:

        import org.netbeans.modules.textmate.lexer.api.GrammarRegistration;

This should lead to syntax highlighted source for .sh bash files taken from the TextMate grammar file.

Language Support via the Language Server

Next, we need to add language support using the language server.

  1. Add "LSP Client" and "MIME Lookup API" as dependencies of the module.

  2. Create a new class, ShellClient, in the module, put this into it, (replacing " " with the absolute path to "node_modules/bash-language-server"):

        import java.io.IOException;
        import org.netbeans.api.editor.mimelookup.MimeRegistration;
        import org.netbeans.modules.lsp.client.spi.LanguageServerProvider;
        import org.openide.util.Exceptions;
        import org.openide.util.Lookup;
    
        @MimeRegistration(mimeType="text/sh", service=LanguageServerProvider.class)
        public class ShellClient implements LanguageServerProvider {
    
           @Override
           public LanguageServerDescription startServer(Lookup lkp) {
               try {
                   Process p = new ProcessBuilder("/bin/main.js", "start").start();
                   return LanguageServerDescription.create(p.getInputStream(), p.getOutputStream(), p);
               } catch (IOException ex) {
                   Exceptions.printStackTrace(ex);
                   return null;
               }
           }
    
        }

    You may need to explicitly call node in the above code, i.e., as follows:

        Process p = new ProcessBuilder(
                "/usr/local/bin/node",
                "/bin/main.js",
                "start").start();
  3. Build and start the module.

Caveat: the language server is started only for files that are inside a project, so create (any) new project, and inside the project, put a shell file. E.g. copy "bin/netbeans" as "test.sh" into the project. Open it in the editor - there should be syntax highlighting, Navigator, and code completion should show something, etc.