Tree-sitter support for SuperCollider

I set this up in emacs (linux) over the weekend. I’ll add some notes here, in case it’s helpful to anyone.

Basic steps:

  1. clone the tree-sitter-supercollider repo and install/configure tree-sitter CLI
  2. compile a shared library from the src files (parser.c & scanner.c)
  3. register the grammar with the emacs tree-sitter package

Pre-requisites:

  1. node
  2. a C compiler
  3. emacs with scel and tree-sitter installed

Initial tree-sitter set up

This seems straight forward, but there are a couple of typical little traps that can waste your time. So I’ll go into detail here.

Cloning the repo and running npm install from inside the tree-sitter-supercollider directory will install the tree-sitter-cli package.

Next, configure the tree-sitter CLI with tree-sitter init-config.
This will create a directory at ~/.tree-sitter/bin
And a config file at ~/.tree-sitter/config.json

Open the ~/.tree-sitter/config.json file and edit the "parser-directories" array, so that the CLI can find the tree-sitter-supercollider directory.

Run tree-sitter generate from within the tree-sitter-supercollider directory.

tree-sitter dump-languages should now list the supercollider parser.

Try out the highlight & parse commands on the example.scd file in the directory
tree-sitter parse example.scd
tree-sitter highlight example.scd

info for systems with an existing tree-sitter binary (explicit install or a dep for other packages)

The emacs tree-sitter package requires grammars that were compiled with a version of tree-sitter before 0.20. The tree-sitter-supercollider project does specify a compatible version (^0.19.4) in the package.json.

But if there’s a tree-sitter binary already installed, there’s a chance it’s more recent (> 0.20) and also taking precedence over the version that’s installed to the project locally.

which tree-sitter will show which tree-sitter binary is being used.

I just went with installing a specific version of the npm package, globally as suggested here:

A note on the tree-sitter init-config command.

The C binary package will create a directory in ~/.config/tree-sitter, the npm package will use ~/.tree-sitter and the rust package probably does something different also with rustup etc.

If tree-sitter parse isn’t working or tree-sitter dump-languages isn’t showing the supercollider parser, then that’s a sign that the config.json file doesn’t have the correct path in "parser-directories" array.

If the parser-directories array looks good but the parser is still not showing up, that’s a sign that the wrong config.json file was edited. For example, I was editing the ~/.config/tree-sitter/config.json which belongs to the binary package, but using the npm cli package which looks in ~/.tree-sitter/config.json.

Create a shared library (.so) file

The emacs tree-sitter package needs the tree-sitter grammar to be compiled into a shared library. I’m not fully across the inner workings of static/dynamic linking and shared libs in C, or qualified to be offering up commands. But here is what worked for me after some searching/reading on the topic.

From within the tree-sitter-supercollider/src directory, run:
gcc -shared -o supercollider.so -fPIC scanner.c parser.c

There should now be a supercollider.so file, in the src directory.
Move it to ~/.tree-sitter/bin .

My reference for the above command is here:
http://www.microhowto.info/howto/build_a_shared_library_using_gcc.html

Register the supercollider grammar with the emacs tree-sitter package

This step assumes that the scel and tree-sitter packages are already installed and configured in emacs. I’m not using a config framework like Doom/Spacemacs. So there might be some slight differences.

Evaluating the following line in a buffer is probably a good start. It will confirm where the emacs tree-sitter package will look for compiled grammars on the system. This should be precisely where the supercollider.so file was moved to, in the previous step.

(tree-sitter-cli-bin-directory)

Adding the following line to your config will let the tree-sitter package know to look for a grammar called ‘supercollider’ and register it with sclang-mode.

(add-to-list 'tree-sitter-major-mode-language-alist '(sclang-mode . supercollider))

If you run into any issues, checking the value of that list is a good start.

Either via ‘Describe Symbol’ C-h o tree-sitter-major-mode-language-alist or with M-x customize-option [ Return ] tree-sitter-major-mode-language-alist, which will display a GUI for the list. A list entry can be easily deleted/edited from there.

The next step is to pull in the highlights.scm file that’s located at tree-sitter-supercollider/queries/highlights.scm . The tree-sitter package will use it for syntax highlighting. Here it is alongside the line above.

(add-to-list 'tree-sitter-major-mode-language-alist '(sclang-mode . supercollider))

(defun import-supercollider-highlights ()
  (with-temp-buffer
    ;; add the actual path on your system below
    (insert-file-contents "PATH/TO/tree-sitter-supercollider/queries/highlights.scm")
    (goto-char (point-max))
    (insert "\n")
    (buffer-string)))

(defun set-supercollider-default-patterns ()
  (setq tree-sitter-hl-default-patterns (import-supercollider-highlights)))

(add-hook 'sclang-mode-hook 'tree-sitter-hl-mode)
(add-hook 'sclang-mode-hook 'set-supercollider-default-patterns)

This is the exact block of code that is working for me currently. It’s a bit naive but easy to get the gist of. You could chuck the multiple hooks into a function, but this suits me for now while I’m establishing some other sclang-mode related functionality in emacs.

here is an example of what looks like a pretty robust approach to a more detailed config.

link to the emacs tree-sitter docs

The finer grained syntax highlighting makes a huge difference and I’m looking forward to adding some additional functionality now that I have tree-sitter set up.

Thanks very much @madskjeldgaard for all your hard work on this. I’m definitely up for helping out with the project and am slowly starting to get my head around how it works.

4 Likes