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:
- clone the tree-sitter-supercollider repo and install/configure tree-sitter CLI
- compile a shared library from the src files (parser.c & scanner.c)
- register the grammar with the emacs tree-sitter package
Pre-requisites:
- node
- a C compiler
- 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.