/img/full/sc-lysningsrommet.jpg SuperCollider-NeoVim setup in use at the wonderful Lysningsrommet at the art academy in Stockholm

SuperCollider is my favourite programming language for music and sound and I use it all the time. The programming language comes with an IDE that is quite capable, but I for one prefer doing all of my coding in one editor (sometimes I write C++, lua and SuperCollider in one and the same project) and that editor for me is NeoVim. The advantages of (Neo)Vim are many but for me the primary ones are that it is a keyboard centric tool, it’s lightweight, it is highly configurable and it works everywhere.

This in turn makes me a faster coder and makes the code itself better and more readable. When I started out with SuperCollider, the only option was to use classic vim with scvim - and while this was nice, a lot of things were missing for me and/or buggy. Then came NeoVim and in time, David Granström wrote the SCNvim plugin for NeoVim and I have been using this for all my SuperCollider coding since it came out.

I get a lot of questions about setting up NeoVim for SuperCollider usage and so this post is a collection of advice, configuration and links to help you get started with using NeoVim and SuperCollider together.

A slight detour: New to (Neo)Vim?

If you are new to using (Neo)Vim, I would strongly recommend spending time learning the basics of the program before diving into all of this stuff. It will help you immensely and make it much more fun to use NeoVim as a SuperCollider IDE, so here is some advice for the newbie Vim user:

Vim comes with a fantastic help system with a lot of documentation. The way to use it is to type :h <something> where you replace <something> with your search term. Whenever in doubt, try looking at the help system for help.

Vim comes with a small tutorial that I recommend all new users go through. You can access it by running :Tutor inside of Vim.

The vim manual can be accessed by running :h usr_toc.txt or if you want a nicely formatted version of it online: vim.help. Vim also has a reference manual which is more in the style of cheat sheets or extensive lists of commands and key bindings. My favourite of these and the one I recommend you open from time to time is :h motions.txt - reading this from time to time will turbo charge your editing speed.

This blog series is another great resource for newbies and seasoned users alike (I read it frequently).

If you prefer videos, there are a gazillion video tutorials on YouTube and the like.

A final but very important note to new vim users is: Don’t blindly copy-paste configurations and plugins. A common mistake is to install tons of plugins and copy configurations from other people, but it is really important that you understand what each plugin does or what each line of configuration is doing to your system - otherwise you will end up with a bloated system with a ton of features and configuration that you do not know exist, let alone how to use them (I made all of these mistakes when I started out!). An additional advantage of installing things piece by piece is you can more easily see when/where problems or conflicts occur when you build your system.

Configuring NeoVim using lua

NeoVim is a fork of Vim, and I won’t get into the history of that here, but in terms of configuration this has a slightly confusing but important consequence. Originally, vim was configured using a language called vimscript (this is a nice resource for learning that language). This works perfectly fine in NeoVim, but it is frankly a nightmare to use that language for anything other than basic configuration. NeoVim solves this by allowing users to write all of their configurations and plugins using lua - a tiny, fast and frankly FUN programming language that you could potentially learn in 15 minutes (this is an overstatement but it’s good marketing).

For NeoVim you can either call your configuration file init.vim or init.lua depending on which scripting language you use. It is common to break up the configuration into different files, but this is mostly to keep it clean and maintanable (this is a nice way to organize your NeoVim configuration). The manual says this about where to put it:

The config file is located at:
	Unix		~/.config/nvim/init.vim (or init.lua)
	Windows		~/AppData/Local/nvim/init.vim (or init.lua)
or if |$XDG_CONFIG_HOME| is defined:
			$XDG_CONFIG_HOME/nvim/init.vim (or init.lua)

If you are starting a new configuration with NeoVim with this, consult :h init.lua for more information on where to put the config file and what to call it. I’d recommend going for init.lua and predominantly write the thing in lua.

For a guide on lua and neovim, see this.

In this post I will write all configuration using lua. To get the most out of the lua api of NeoVim, it is important that you always have the latest version of NeoVim installed (at least version 0.5 which was a major release for the lua stuff).

Plugin management

In this post I won’t go into details on how to install each plugin. This is done using a plugin manager - I highly recommend packer.nvim but you can use whatever you like.

SCNvim - A NeoVim front end for SuperCollider

SCNvim is the plugin that makes it possible for us to work with SuperCollider inside of NeoVim.

SCNvim serves two purposes. First of all, it adds most of the features we need to have a nice working environment for writing SuperCollider code. But the second, more subtle purpose is as a lua api for SuperCollider, making it really easy to execute any SuperCollider code from keybindings and functions in Neovim.

For information on how to configure scnvim, see :h scnvim-configuration and the scnvim wiki. Also check out :h scnvim-mappings to see the default key bindings for SCNvim.

Here are some basic options you can configure for SCNvim that will take care of some of the basics.

-- vim.g.scnvim_sclang_options = {'-l', "/home/mads/.config/SuperCollider/sclang_conf_development.yaml"}

vim.g.scnvim_postwin_syntax_hl = 1

-- scnvim neovim docs !
vim.g.scnvim_scdoc = 1

-- vertical 'v' or horizontal 'h' split
vim.g.scnvim_postwin_orientation = 'v'

-- position of the post window 'right' or 'left'
vim.g.scnvim_postwin_direction = 'right'

-- default is half the terminal size for vertical and a third for horizontal
vim.g.scnvim_postwin_size = 50

-- automatically open post window on a SuperCollider error
vim.g.scnvim_postwin_auto_toggle = 1

--	-- duration of the highlight
vim.g.scnvim_eval_flash_duration = 100

-- number of flashes. A value of 0 disables this feature.
vim.g.scnvim_eval_flash_repeats = 2

-- set this variable if you don't want the "echo args" feature
-- vim.g.scnvim_echo_args = 0

-- Configure the color of the evaluation flash
vim.cmd([[ highlight SCNvimEval guifg=black guibg=white ctermfg=black ctermbg=white ]])

-- Uncomment to not use default keybindings
-- vim.g.scnvim_no_mappings = 1

Most of the stuff following this point is optional to get SuperCollider working in NeoVim, but highly recommend to enhance your experience.

Post installation

After installing SCNvim, make sure to run :call scnvim#install() so that your SuperCollider class library gets a link to the SCNvim SuperCollider classes needed for all of this to work.

To make use of the help system, tags (for definitions) and snippets (when you’ve set it up), you will need to run :SCNvimTags from time to time. This will basically map your SuperCollider class library and extensions and make NeoVim conscious of them (which also means you need to rerun this command when you add/remove classes or packages from SuperCollider to keep NeoVim up to date with those changes).

Quick tip: Go to definition

If you’ve installed SCNvim and run the SCNvimTags command and restarted NeoVim, you can easily look up definitions for SuperCollider classes. This is really useful in a lot of situations. To do this, put your cursor on a class in a SuperCollider file and press g] or C-] to open it up.

Press C-o to jump backwards to where you came from (this is an easy way to get lost in rabbit holes, be warned).

Snippets: LuaSnip

One of the productivity boosters of SCNvim is it’s ability to automatically generate code snippets. SCNvim itself supports a range of snippet engines, but these days I prefer to use LuaSnip. After you’ve installed and set up LuaSnip, add this to your scnvim configuration:

-- Use the LuaSnip snippet engine
-- SCNvim supports "ultisnips", "luasnip" and "snippets.nvim"
vim.g.scnvim_snippet_format = 'luasnip'

And then, in your LuaSnip snippet table, add the SCNvim snippets like this:

-- Add SCNvim snippets to LuaSnip
-- Note: This replaces any supercollider snippets already present in LuaSnip
require'luasnip'.snippets.supercollider = require'scnvim/utils'.get_snippets()

Next step is to open up a SuperCollider file and run :SCNvimTags to regenerate the snippet system. Now, restart NeoVim, open up a SuperCollider buffer and type for example SinOsc.ar and press your LuaSnip expansion keybinding (probably Control-k).

Combining auto generated snippets with custom snippets

/img/custom-sc-snips.gif

This is a more advanced tip and not mandatory for setting up SCNvim snippets. But we’re all nerds here so why not:

Instead of completely replacing all of LuaSnip’s supercollider table, it is possible to combine it with your own custom SuperCollider snippets using a bit of lua code.

-- This table contains all of your custom SuperCollider snippets
local my_custom_sc_snipz = {

	-- Ndef
	s("ndef",{
		t("Ndef("),
		t("\\"), i(1, "name"), t(",{|"), i(2, "freq=100, amp=0.5"), t({"|", ""}),
		t("\t"), i(2, "SinOsc.ar(freq) * amp;"), t({"", ""}),
		t({"", "})"}),
		i(3, ".play"),
	}),

	-- SynthDef
	s("synthdef",{
		t("SynthDef("),
		t("\\"), i(1, "name"), t(",{|"), i(2, "out=0, amp=0.5"), t({"|", ""}),
		t("\t"), i(3, "var sig = SinOsc.ar;"), t({"", ""}),
		t("\t"), i(4, "Out.ar(out, sig)"),
		t({"", "})"}),
		i(5, ".add"),
	}),

	-- Value pattern shorthands
	s("pwh", { t("Pwhite("), c(1, {t("0.0"), rnd()}), t(", "), c(1, {t("1.0"), rnd()}), t(")")}),
	s("pbr", { t("Pbrown("), i(1, "0.0"), t(", "), i(2, "1.0"), t(", "), i(3, "0.125"), t(")")}),
	s("ps", { t("Pseq(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pr", { t("Prand(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pxr", { t("Prand(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pw", { t("Pwrand(["), i(1, "0.5, 0.5"), t("], ["), i(2, "50.0, 100.0"), t("], "), i(3, "inf"), t(")")}),
	s("pseg", { t("Pseg("), t("["),i(1, "1.0,0.0"), t("],"), i(2, "16"), t(","), i(3, "\\lin"), t(","), i(4,"inf"), t(")")}),
	s("pstep", { t("Pstep("), t("["),i(1, "1.0,0.0"), t("],"), i(2, "16"), t(","), i(3,"inf"), t(")")}),

	-- Misc Pattern
	s("pf", { t("Pfunc({|ev| "), i(1, ""), t(" })")}),
	s("pp", { t("Ppar(["), i(1, "p,o"), t("], "), i(2, "inf"), t(")")}),
	s("ptp", { t("Ptpar(["), i(1, "0.0, p, 2.0, o"), t("], "), i(2, "inf"), t(")")}),
}

-- Now append custom snippets to table of scnvim snippets
local scnvim_snippets = require'scnvim.utils'.get_snippets()
require'luasnip'.snippets.supercollider = vim.tbl_extend('force', {}, scnvim_snippets, my_custom_sc_snipz)

Statusline

/img/sc-statusline.gif

There are many cool choices for status line plugins for NeoVim. A popular one seems to be lualine. I haven’t compared all of the NeoVim status line plugins but I use Galaxyline at the moment.

SCNvim comes with the ability to display the server status in the statusline of NeoVim.

To get statuslines to work, you first need to edit your SuperCollider installation’s startup.scd file and add the following:

// scnvim
if (\SCNvim.asClass.notNil) {
    Server.default.doWhenBooted {
        \SCNvim.asClass.updateStatusLine(1, \SCNvim.asClass.port);
    }
}

And then in Galaxyline, this is a way to get the information from SCNvim displayed:

gls.left[5] = {
		Scnvim = {
				provider = function()
						local scstatus = vim.api.nvim_call_function("scnvim#statusline#server_status", {})
						return scstatus
				end,
				condition = function()
						if vim.api.nvim_get_option("filetype") == "supercollider" then
								return true
						else
								return false
						end
				end,
				icon = ' 📡 ',
				separator = '',
				separator_highlight = {colors.blue, colors.bg},
				highlight = {colors.blue, colors.bg},
		}
	}

Advanced syntax highlighting: Treesitter

Treesitter is an awesome “code parser” that has great support in NeoVim. It’s basically a program that can quickly and precisely parse your code while you type it and understands scopes and hierarchies and can use that to make informed decisions. The main (but not only) use case for this is to have advanced syntax highlighting. What this means in practice is that treesitter understands for example what variables are local to functions, which variables are environment variables. It knows the difference between class and instance variables etc etc.

There is a tree-sitter grammar for SuperCollider(I could really use some help in maintaining this if anyone is up for it) that enables treesitter to understand SuperCollider code. To use it in NeoVim, first install neovim-treesitter, restart NeoVim and run TSInstall supercollider. This will download and compile the SuperCollider grammar.

The only configuration I recommend specifically for SuperCollider and treesitter is to disable treesitter’s indentation for SuperCollider - at the moment it is behaving strangely so best to just disable that by adding the following to your nvim-tree-sitter configuration:

indent = {
	enable = {},
	disable = {"supercollider"}
}

It goes inside of the setup table for nvim-treesitter like this:

-- Setup nvim-treesitter
require'nvim-treesitter.configs'.setup {
  ensure_installed = "all",
  indent = {
	  enable = {},
	  disable = {"supercollider"}
  },
  highlight = {
    enable = true,              -- false will disable the whole extension
  }
}

Side note To get the most out of tree-sitter’s delicious code highlighting, I recommend installing one of the modern, neovim-specific color schemes that have full support for it. Eg gruvbox.nvim, gruvbox-flat.nvim or onedark.nvim.

Fuzzy finding

/img/quarksinstall.gif

Fuzzy finding is in my opinion one of the greatest inventions in modern programming tooling. If you’ve never used fuzzy finding before it will surely blow your mind when you use it for the first time. I highly recommend installing fzf-lua or fzf.vim to get use of this in vim. This will for example enable commands to help you quickly search for files and specific words in all of your code files.

I wrote a small plugin that uses fzf to fuzzy search through a bunch of SuperCollider things like Quarks, Scales, help files, Node proxies, Synthdefs etc. It also contains a small API for creating your own fuzzy searchers for SuperCollider.

Additional SuperCollider plugins

If you want to get wild with your SuperCollider setup in NeoVim, I’ve created a plugin for assorted hacks. Some of them are silly and some are really nice. I personally use the command :SCNewQuark all the time from this plugin to generate a new SuperCollider package (this is why I am able to make and publish so/too many SuperCollider packages).

Misc plugins

I highly recommend exploring the world of NeoVim-plugins. Things are moving extremely fast at the moment because the lua API of NeoVim has made it a lot easier to create plugins. As a consequence, there’s a gazillion NeoVim plugins out there and I recommend finding and trying the ones that suit you. That said, here are a few that I use a lot with SuperCollider programming that aren’t really specific to SuperCollider:

  • reaper-nvim - If you’re a Reaper user, this lets your control Reaper from NeoVim.
  • toggleterm - NeoVim already has a great terminal you can open with :term but this plugin makes it even better.
  • nvim-cmp - A fantastic completion engine that integrates well with LuaSnip and treesitter.
  • lir.nvim - File manager
  • vim-fugitive - A great Git integration for vim
  • gitsigns.nvim - Shows a small gutter in NeoVim highlighting what lines are changes, added etc.

Got questions?

If you have any questions about SuperCollider, SCNvim or generally on using SuperCollider with NeoVim, I recommend asking on the scsynth.org forum. There’s a thread on SCNvim here but also try giving the forum a search, and if you cannot find an appropriate thread for your questions simply start a new one (I’m really crap at answering emails so don’t expect support from me if you go that route). For scnvim specific things, I’d recommend asking on the discussion forum of scnvim.