Ranjith Hegde has added support for SuperCollider in the textobjects plugin for Neovim, allowing really cool syntax specific manipulation !!!
Hey
Thanks for working on this! I am using nvim and I have treesitter and cmp working. It is all working for Rust for example but in SC I donāt get autocompletion options like you do here (I only get a popup with type ātextā). Do you mind sharing your config?
Thanks!
Sure, here is mine:
-- Setup nvim-cmp.
local cmp = require'cmp'
-- local lspkind = require "lspkind"
-- lspkind.init()
vim.opt.completeopt = { "menu", "menuone", "noselect" }
-- Don't show the dumb matching stuff.
vim.opt.shortmess:append "c"
cmp.setup({
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
-- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
end,
},
mapping = {
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
},
sources = {
{ name = 'path' },
{ name = 'luasnip' }, -- For luasnip users.
{ name = 'nvim_lsp' },
{ name = 'tags' },
-- { name = 'nvim_lua' },
{ name = 'treesitter' },
-- { name = 'spell' },
{ name = 'buffer' , keyword_length=5}, -- dont complete until at 5 chars
},
formatting = {
-- set up nice formatting for your sources.
-- format = lspkind.cmp_format {
-- with_text = true,
-- menu = {
-- nvim_lsp = "[LSP]",
-- nvim_lua = "[api]",
-- path = "[path]",
-- luasnip = "[snip]",
-- -- gh_issues = "[issues]",
-- rg = "[ripgrep]",
-- tags = "[tags]",
-- buffer = "[buf]",
-- },
-- },
},
view = {
entries = "native",
},
experimental = {
-- native_menu = true,
ghost_text = true
}
})
And then the sources I have installed as plugins:
use {'hrsh7th/nvim-cmp',
disable = false,
requires = {
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer',
'quangnguyen30192/cmp-nvim-tags',
'saadparwaiz1/cmp_luasnip',
'hrsh7th/cmp-nvim-lua',
-- 'f3fora/cmp-spell',
'ray-x/cmp-treesitter',
'hrsh7th/cmp-path',
-- 'onsails/lspkind-nvim',
-- 'lukas-reineke/cmp-rg'
}, config = function()
require"plugins/cmp"
end}
I seem to have a similar problem to Dionysis even with your config Mads! I see Snippet completions and Text but no luck with Tags or Treesitter. Just been lamely copypasting so apologies if Iāve missed something - any tips appreciated!
Did you install these plugins ?
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer',
'quangnguyen30192/cmp-nvim-tags',
'saadparwaiz1/cmp_luasnip',
'hrsh7th/cmp-nvim-lua',
-- 'f3fora/cmp-spell',
'ray-x/cmp-treesitter',
'hrsh7th/cmp-path',
You also need to generate snippets and tags in SCNvim to see those. See the SCnVim wiki for more info as well as luasnip setup
Thank you! I finally got it working. That was not easy But I am very disapointed that I am not getting the help file in the popup
Just kidding, so happy to have argument completion! I had tried to implement this in the past during the times when
vimscript
was the only option and had failed⦠This is luxury!
@semiquaver Donāt give up. There is kind of crazy amount of plugins involved to get the full experience! Here is my configuration in case it helps (Make sure you commit your current setup before experimenting with any cut and paste of my mess thoughā¦).
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
Plug 'nvim-treesitter/nvim-treesitter-refactor'
Plug 'nvim-treesitter/playground'
Plug 'haorenW1025/completion-nvim'
Plug 'nvim-treesitter/completion-treesitter'
Plug 'nvim-treesitter/nvim-treesitter-textobjects'
Plug 'neovim/nvim-lspconfig'
Plug 'williamboman/nvim-lsp-installer'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-cmdline'
Plug 'hrsh7th/nvim-cmp'
Plug 'ray-x/cmp-treesitter'
Plug 'quangnguyen30192/cmp-nvim-tags'
Plug 'hrsh7th/cmp-nvim-lua'
" For vsnip users.
Plug 'hrsh7th/cmp-vsnip'
" Snippets {{{
Plug 'rafamadriz/friendly-snippets'
Plug 'hrsh7th/vim-vsnip'
Plug 'L3MON4D3/LuaSnip'
Plug 'saadparwaiz1/cmp_luasnip'
let g:scnvim_snippet_format = "luasnip"
" Jump forward or backward in vsnip
imap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>'
smap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>'
imap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>'
smap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>'
" and here for luasnip
imap <silent><expr> <Tab> luasnip#expand_or_jumpable() ? '<Plug>luasnip-expand-or-jump' : '<Tab>'
" -1 for jumping backwards.
inoremap <silent> <S-Tab> <cmd>lua require'luasnip'.jump(-1)<Cr>
snoremap <silent> <Tab> <cmd>lua require('luasnip').jump(1)<Cr>
snoremap <silent> <S-Tab> <cmd>lua require('luasnip').jump(-1)<Cr>
"}}}
set completeopt=menu,menuone,noselect
and for lua
:
-- SuperCollider
require("luasnip").add_snippets("supercollider", require("scnvim/utils").get_snippets())
-- Rust
require('rust-tools').setup({})
require("nvim-treesitter.configs").setup {
-- ensure_installed = {"supercollider", "rust", "html", "javascript"},
ensure_installed = "maintained",
highlight = {
enable = true, additional_vim_regex_highlighting = true,
-- disable = { "supercollider"},
},
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<CR>",
scope_incremental = "<CR>",
node_incremental = "<TAB>",
node_decremental = "<S-TAB>",
},
},
indent = { enable = true },
matchup = { enable = true },
autopairs = { enable = true },
playground = {
enable = true,
disable = {},
updatetime = 25,
persist_queries = false,
keybindings = {
toggle_query_editor = "o",
toggle_hl_groups = "i",
toggle_injected_languages = "t",
toggle_anonymous_nodes = "a",
toggle_language_display = "I",
focus_language = "f",
unfocus_language = "F",
update = "R",
goto_node = "<cr>",
show_help = "?",
},
},
rainbow = {
enable = true,
extended_mode = true, -- Highlight also non-parentheses delimiters
max_file_lines = 1000,
},
refactor = {
smart_rename = { enable = true, keymaps = { smart_rename = "grr" } },
highlight_definitions = { enable = true },
navigation = {
enable = true,
keymaps = {
goto_definition_lsp_fallback = "gnd",
-- use telescope for these lists
-- list_definitions = "gnD",
-- list_definitions_toc = "gO",
-- @TODOUA: figure out if I need both below
goto_next_usage = "<a-*>", -- is this redundant?
goto_previous_usage = "<a-#>", -- also this one?
},
disable = { "supercollider"},
},
-- highlight_current_scope = {enable = true}
},
textobjects = {
lsp_interop = {
enable = true,
border = "none",
peek_definition_code = {
["df"] = "@function.outer",
["dF"] = "@class.outer",
},
},
move = {
enable = true,
set_jumps = true, -- whether to set jumps in the jumplist
goto_next_start = {
["]m"] = "@function.outer",
["]]"] = "@call.outer",
},
goto_next_end = {
["]M"] = "@function.outer",
["]["] = "@call.outer",
},
goto_previous_start = {
["[m"] = "@function.outer",
["[["] = "@call.outer",
},
goto_previous_end = {
["[M"] = "@function.outer",
["[]"] = "@call.outer",
},
},
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@call.outer",
["ic"] = "@call.inner",
},
},
swap = {
enable = true,
swap_next = {
[",a"] = "@parameter.inner",
},
swap_previous = {
[",A"] = "@parameter.inner",
},
},
},
}
vim.opt.foldmethod = "expr"
vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
local cmp = require'cmp'
vim.opt.completeopt = { "menu", "menuone", "noselect" }
vim.opt.shortmess:append "c"
cmp.setup {
snippet = {
-- REQUIRED - you must specify a snippet engine
expand = function(args)
vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
-- require('snippy').expand_snippet(args.body) -- For `snippy` users.
-- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
end,
},
mapping = {
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
},
sources = {
{ name = 'path' },
{ name = 'vsnip' },
{ name = 'luasnip' },
{ name = 'nvim_lsp' },
{ name = 'tags' },
-- { name = 'nvim_lua' },
{ name = 'treesitter' },
-- { name = 'spell' },
{ name = 'buffer' , keyword_length=5}, -- dont complete until at 5 chars
},
view = {
entries = "native",
},
experimental = {
-- native_menu = true,
ghost_text = true
}
}
-- The nvim-cmp almost supports LSP's capabilities so You should advertise it to LSP servers..
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
-- The following example advertise capabilities to `clangd`.
require'lspconfig'.clangd.setup {
capabilities = capabilities,
}
Good luck!
EDIT: Also check here Additional configuration Ā· davidgranstrom/scnvim Wiki Ā· GitHub
whoop whoop! Congratulations!
Ps all of this dependency handling of plugins is a bit easier with Packer as packagemanager. It allows you to set these as dependencies of the main cmp plugin, which in turn allows you to disable the cmp plugin and then automatically all of the dependendents. Just a tip.
Ah yes, I can see everyone using Packer. It is just that I have a 1000 lines configuration in vimscript and havenāt decided to transition to lua
yet. I literally just added a require for a config.lua
a few days ago to manage treesitter
and cmp
configurations! I do like the idea of moving to lua
though so it is just a matter of time Again thanks for your work!! Great stuff
Bouncing this, since it seems like the right topic!
Got this working in my vim, and now I want to figure out how to do two things:
- Add indentation/auto formatting on save, similar to clang-format. From what I see, this should be possible ? But Iām having a hard time finding any documention on how to begin creating these rules.
- If 1. succeeds, limiting the max number of columns to 80.
Basically, start working on creating a āBlackā for Supercollider code. Iām happy to tinker on it because itās the next piece in my larger puzzle of creating a proper, self-contained projects environment for SC.
I found this:
You can use the existing .so generated for the scnvim tree-parser and this lets you write a python script to handle the code formatting. External formatters (i.e. clang-format and gofmt) are industry standard, so having a python script that parses tree-sitter code and formats it according to code conventions (Code style guidelines Ā· supercollider/supercollider Wiki Ā· GitHub) should be reasonably straightforward.
Iāve written a parser already that traverses an arbitrary supercollider code tree, so now I think itās a matter of hammering out the rules. Itād likely be better to make it as opinionated as possible and follow the existing community guidelines. gofmt is only 500 lines or so (go/gofmt.go at master Ā· golang/go Ā· GitHub) so with the tree parser in hand (EXCELLENT work, btw) Iām hoping this wonāt be so bad ?
Example output of the python call and the tree walk:
$ python ./sclang-format.py -f ../supercollider/sinosc.sc -l ./sclang.so
String: { SinOsc.ar(200, 0, 0.5) }.play;
<Node kind=source_file, start_point=(0, 0), end_point=(2, 0)> b'{ SinOsc.ar(200, 0, 0.5) }.play;\n\n'
<Node kind=function_call, start_point=(0, 0), end_point=(0, 31)> b'{ SinOsc.ar(200, 0, 0.5) }.play'
<Node kind=receiver, start_point=(0, 0), end_point=(0, 26)> b'{ SinOsc.ar(200, 0, 0.5) }'
<Node kind=function_block, start_point=(0, 0), end_point=(0, 26)> b'{ SinOsc.ar(200, 0, 0.5) }'
<Node kind="{", start_point=(0, 0), end_point=(0, 1)> b'{'
<Node kind=function_call, start_point=(0, 2), end_point=(0, 24)> b'SinOsc.ar(200, 0, 0.5)'
<Node kind=receiver, start_point=(0, 2), end_point=(0, 8)> b'SinOsc'
<Node kind=class, start_point=(0, 2), end_point=(0, 8)> b'SinOsc'
<Node kind=method_call, start_point=(0, 8), end_point=(0, 24)> b'.ar(200, 0, 0.5)'
<Node kind=".", start_point=(0, 8), end_point=(0, 9)> b'.'
<Node kind=method_name, start_point=(0, 9), end_point=(0, 11)> b'ar'
<Node kind="(", start_point=(0, 11), end_point=(0, 12)> b'('
<Node kind=parameter_call_list, start_point=(0, 12), end_point=(0, 23)> b'200, 0, 0.5'
<Node kind=argument_calls, start_point=(0, 12), end_point=(0, 15)> b'200'
<Node kind=unnamed_argument, start_point=(0, 12), end_point=(0, 15)> b'200'
<Node kind=literal, start_point=(0, 12), end_point=(0, 15)> b'200'
<Node kind=number, start_point=(0, 12), end_point=(0, 15)> b'200'
<Node kind=integer, start_point=(0, 12), end_point=(0, 15)> b'200'
<Node kind=",", start_point=(0, 15), end_point=(0, 16)> b','
<Node kind=argument_calls, start_point=(0, 17), end_point=(0, 18)> b'0'
<Node kind=unnamed_argument, start_point=(0, 17), end_point=(0, 18)> b'0'
<Node kind=literal, start_point=(0, 17), end_point=(0, 18)> b'0'
<Node kind=number, start_point=(0, 17), end_point=(0, 18)> b'0'
<Node kind=integer, start_point=(0, 17), end_point=(0, 18)> b'0'
<Node kind=",", start_point=(0, 18), end_point=(0, 19)> b','
<Node kind=argument_calls, start_point=(0, 20), end_point=(0, 23)> b'0.5'
<Node kind=unnamed_argument, start_point=(0, 20), end_point=(0, 23)> b'0.5'
<Node kind=literal, start_point=(0, 20), end_point=(0, 23)> b'0.5'
<Node kind=number, start_point=(0, 20), end_point=(0, 23)> b'0.5'
<Node kind=float, start_point=(0, 20), end_point=(0, 23)> b'0.5'
<Node kind=")", start_point=(0, 23), end_point=(0, 24)> b')'
<Node kind="}", start_point=(0, 25), end_point=(0, 26)> b'}'
<Node kind=method_call, start_point=(0, 26), end_point=(0, 31)> b'.play'
<Node kind=".", start_point=(0, 26), end_point=(0, 27)> b'.'
<Node kind=method_name, start_point=(0, 27), end_point=(0, 31)> b'play'
<Node kind=";", start_point=(0, 31), end_point=(0, 32)> b';'
I think thereās a lot here to work with - is there a mailing list for chatter about the sc treesitter code so I donāt clog things up here ?
This is probably the best place for it - maybe start a new thread in the Development category? Good code formatting is an exciting prospect - as soon as you have a proof of concept working, Iāll integrate it into the VSCode / language server quark.
Exciting stuff ! Looking forward to seeing the progress of this.
Note of warning: the indentation queries in the Supercollider TS grammar may not be super fulfilling because I simply donāt understand how to use those queries but if they need to be changed as part of this let me know. But maybe it isnāt even used in this ?
Thanks! I did some investigating on the tree sitter indentation and it seemed neither fully featured nor well documented ; I think for now, at least, having a separate script that manages formatting will return results that more closely match the coding guidelines.
Weāll see how it goes as I get this moving.
This plugin now has tree-sitter-supercollider support:
awesome. Iāve written a proof of concept for the low-hanging fruit. Now Iām going to need to do some treesitter queries.
Gist of where I am is below. Basic idea is that there are a number of formatter classes that parse the tree and the raw data, rebuilding the tree after each formatter does its work.
There are three types of formatters - pre-processing (i.e. stripping duplicate newlines, etc.), inline processing (the bulk), post-processing (i.e. adding the newling at the end of the file.)
Lots to be done, but as soon as I get my head around how to do one query, this should move pretty quickly.
We should also probably have a spirited argument about what the best general case format should be (think things like - are one-liners ever appropriate ?). It wonāt please everyone, but if weāre thinking about it, I think covering the edge cases that arenāt covered in the best-case document would potentially be a good use of time.
I think this is the best inspiration of what the end product should be - GitHub - psf/black: The uncompromising Python code formatter. If youāve never read the manifesto, itād be worth doing. I use this for work and while I donāt agree with everything it does, it does its job and gets out of the way.
Iāll create a repo once I get a query working and a few more of the format classes written out.
Once the formatter is boiled down into rules, we can easily find a best-fit rule set for the existing class library by trying brute-force combinations and finding formatting options that produce the minimum diff with the current classlib. There are comparable tools for clang-format and Iāve had good luck with them.
Brilliant. Iāll figure out how to do a query and ts update, and then Iāll post a repo.
Also, if thereās a good set of transforms that cover the bases, we can create a set of unit tests to verify and do some test driven development.