552 changed files with 29443 additions and 16 deletions
@ -1,2 +1,28 @@ |
|||||||
set makeprg=golint\ % " Проверка на ошибки и синтаксис |
set makeprg=golangci-lint\ run\ % " Проверка на ошибки и синтаксис |
||||||
|
|
||||||
|
ab ololo fmt.Printf("@@@@ %#v\n", ) |
||||||
|
|
||||||
|
" autocmd BufWritePre * silent! !gofmt -s -w <afile> >/dev/null |
||||||
|
" autocmd BufWritePre * silent! !goimports -w <afile> >/dev/null |
||||||
|
" autocmd BufWritePost * edit |
||||||
|
" autocmd BufWritePost * redraw! |
||||||
|
|
||||||
|
" syntax match goNiceOperator "<-" conceal cchar=← |
||||||
|
" syntax match goNiceOperator "->" conceal cchar=→ |
||||||
|
" syntax match goNiceOperator "<=\ze[^<]" conceal cchar=≤ |
||||||
|
" syntax match goNiceOperator ">=\ze[^>]" conceal cchar=≥ |
||||||
|
" syntax match goNiceOperator "=\@<!===\@!" conceal cchar=≡ |
||||||
|
" syntax match goNiceOperator "!=" conceal cchar=≢ |
||||||
|
|
||||||
|
" syntax match goNiceKeyword "math.Pi" conceal cchar=π |
||||||
|
|
||||||
|
" hi link goNiceOperator Operator |
||||||
|
" hi link goNiceStatement Statement |
||||||
|
" hi link goNiceKeyword Keyword |
||||||
|
|
||||||
|
" hi! link Conceal Operator |
||||||
|
" hi! link Conceal Statement |
||||||
|
" hi! link Conceal Keyword |
||||||
|
|
||||||
|
" setlocal conceallevel=2 |
||||||
|
|
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
|
||||||
|
nnoremap <expr> q &buftype is# "quickfix" ? ":q!<CR>" : "q" |
||||||
|
|
@ -0,0 +1,32 @@ |
|||||||
|
# cmd-parser.nvim |
||||||
|
|
||||||
|
I built this plugin to help other plugin authors to easily parse the command inputted by users and do awesome tricks with it. |
||||||
|
|
||||||
|
Input |
||||||
|
|
||||||
|
```lua |
||||||
|
local parse_cmd = require'cmd-parser'.parse_cmd |
||||||
|
parse_cmd("10+2++,/hello/-3d") |
||||||
|
``` |
||||||
|
|
||||||
|
Output |
||||||
|
|
||||||
|
```lua |
||||||
|
{ ["start_increment_number"] = 4,["end_increment"] = -3,["command"] = d,["start_range"] = 10,["end_increment_number"] = -3,["start_increment"] = +2++,["end_range"] = /hello/,} |
||||||
|
``` |
||||||
|
|
||||||
|
## Installtion |
||||||
|
|
||||||
|
### `Paq.nvim` |
||||||
|
|
||||||
|
```lua |
||||||
|
paq{'winston0410/cmd-parser.nvim'} |
||||||
|
``` |
||||||
|
|
||||||
|
## Testing |
||||||
|
|
||||||
|
This plugin is well tested. To run the test case or help with testing, you need to install lester |
||||||
|
|
||||||
|
```shell |
||||||
|
luarocks install lester |
||||||
|
``` |
@ -0,0 +1,88 @@ |
|||||||
|
local number_range = "^(%d+)" |
||||||
|
local mark_range = "^('[%l><])" |
||||||
|
local forward_search_range = "^(/.*/)" |
||||||
|
local backward_search_range = "^(?.*?)" |
||||||
|
local special_range = "^([%%%.$])" |
||||||
|
|
||||||
|
local command_pattern = "^(%l+)" |
||||||
|
local range_patterns = { |
||||||
|
special_range, number_range, mark_range, forward_search_range, |
||||||
|
backward_search_range |
||||||
|
} |
||||||
|
local range_patterns_type = { |
||||||
|
"special", "number", "mark", "forward_search", "backward_search" |
||||||
|
} |
||||||
|
|
||||||
|
local function get_range(index, cmd) |
||||||
|
local range, type |
||||||
|
for i = 1, #range_patterns do |
||||||
|
local _, end_index, result = string.find(cmd, range_patterns[i], index) |
||||||
|
if end_index then |
||||||
|
index = end_index + 1 |
||||||
|
range = result |
||||||
|
type = range_patterns_type[i] |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
if type == "special" then type = range end |
||||||
|
return range, type, index |
||||||
|
end |
||||||
|
|
||||||
|
local function update_increment(operator, increment, acc_text, acc_num) |
||||||
|
local inc_str = acc_text .. operator .. increment |
||||||
|
if increment == "" then increment = 1 end |
||||||
|
return inc_str, acc_num + tonumber(operator .. increment) |
||||||
|
end |
||||||
|
|
||||||
|
local function get_increment(index, cmd) |
||||||
|
local pattern, inc_text, total, done = "([+-])(%d*)", "", 0, false |
||||||
|
while not done do |
||||||
|
local _, end_index, operator, increment = |
||||||
|
string.find(cmd, pattern, index) |
||||||
|
if not end_index then |
||||||
|
done = true |
||||||
|
break |
||||||
|
end |
||||||
|
inc_text, total = update_increment(operator, increment, inc_text, total) |
||||||
|
index = end_index + 1 |
||||||
|
end |
||||||
|
|
||||||
|
return inc_text, total, index |
||||||
|
end |
||||||
|
|
||||||
|
local function parse_cmd(cmd) |
||||||
|
local result, next_index, comma_index, _ = {}, 1, nil, nil |
||||||
|
local start_range_text |
||||||
|
|
||||||
|
result.start_range, result.start_range_type, next_index = get_range(1, cmd) |
||||||
|
|
||||||
|
comma_index, _, result.separator = string.find(cmd, '[(;,)]', next_index) |
||||||
|
|
||||||
|
if comma_index then |
||||||
|
if not result.start_range then |
||||||
|
result.start_range = "." |
||||||
|
result.start_range_type = "." |
||||||
|
end |
||||||
|
start_range_text = string.sub(cmd, 1, comma_index) |
||||||
|
else |
||||||
|
start_range_text = cmd |
||||||
|
end |
||||||
|
result.start_increment, result.start_increment_number, next_index = |
||||||
|
get_increment(next_index, start_range_text) |
||||||
|
if comma_index then |
||||||
|
-- To offset the comma_index |
||||||
|
next_index = next_index + 1 |
||||||
|
result.end_range, result.end_range_type, next_index = |
||||||
|
get_range(next_index, cmd) |
||||||
|
result.end_increment, result.end_increment_number, next_index = |
||||||
|
get_increment(next_index, cmd) |
||||||
|
end |
||||||
|
|
||||||
|
_, _, result.command = string.find(cmd, command_pattern, next_index) |
||||||
|
|
||||||
|
return result |
||||||
|
end |
||||||
|
|
||||||
|
local function setup() end |
||||||
|
|
||||||
|
return {setup = setup, parse_cmd = parse_cmd} |
@ -0,0 +1,199 @@ |
|||||||
|
local lester = require 'lester' |
||||||
|
local describe, it, expect = lester.describe, lester.it, lester.expect |
||||||
|
local parse_cmd = require'init'.parse_cmd |
||||||
|
|
||||||
|
-- customize lester configuration. |
||||||
|
lester.show_traceback = false |
||||||
|
|
||||||
|
describe('when parse_cmd is called', function() |
||||||
|
local result = parse_cmd("") |
||||||
|
it('should return a table', |
||||||
|
function() expect.equal(type(result), "table") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called without range', function() |
||||||
|
local result = parse_cmd("d") |
||||||
|
it('should return nil start range', |
||||||
|
function() expect.equal(result.start_range, nil) end) |
||||||
|
it('should return nil start range type', |
||||||
|
function() expect.equal(result.start_range_type, nil) end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with shorthand range', function () |
||||||
|
local result = parse_cmd(",20d") |
||||||
|
it('should return start range as .', function () |
||||||
|
expect.equal(result.start_range, ".") |
||||||
|
end) |
||||||
|
it('should return start range type', function () |
||||||
|
expect.equal(result.start_range_type, ".") |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single number range', function() |
||||||
|
local result = parse_cmd("23d") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "23") end) |
||||||
|
it('should return the start range type', |
||||||
|
function() expect.equal(result.start_range_type, "number") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "d") end) |
||||||
|
end) |
||||||
|
-- |
||||||
|
describe('when it is called with single number range', function() |
||||||
|
describe('when it has increment', function() |
||||||
|
local result = parse_cmd("23+5d") |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "d") end) |
||||||
|
it('should return the start increment', |
||||||
|
function() expect.equal(result.start_increment, "+5") end) |
||||||
|
it('should return the start increment number', |
||||||
|
function() expect.equal(result.start_increment_number, 5) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with a complete number range', function() |
||||||
|
local result = parse_cmd("10,20d") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "10") end) |
||||||
|
it('should return the end range', |
||||||
|
function() expect.equal(result.end_range, "20") end) |
||||||
|
it('should return the end range type', |
||||||
|
function() expect.equal(result.end_range_type, "number") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "d") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with a complete number range', function() |
||||||
|
local result = parse_cmd("10+3+++-,20d-8+") |
||||||
|
it('should return the start increment', |
||||||
|
function() expect.equal(result.start_increment, "+3+++-") end) |
||||||
|
it('should return the start increment number', |
||||||
|
function() expect.equal(result.start_increment_number, 5) end) |
||||||
|
it('should return the end increment', |
||||||
|
function() expect.equal(result.end_increment, "-8+") end) |
||||||
|
it('should return the end increment number', |
||||||
|
function() expect.equal(result.end_increment_number, -7) end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single mark range', function() |
||||||
|
local result = parse_cmd("'ad") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "'a") end) |
||||||
|
it('should return the start range type', |
||||||
|
function() expect.equal(result.start_range_type, "mark") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "d") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single mark range', function() |
||||||
|
describe('when it has increments', function() |
||||||
|
local result = parse_cmd("'a+2-5+3d") |
||||||
|
it('should return the start range increment', |
||||||
|
function() expect.equal(result.start_increment, "+2-5+3") end) |
||||||
|
it('should return the start range increment number', |
||||||
|
function() expect.equal(result.start_increment_number, 0) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with a complete mark range', function() |
||||||
|
local result = parse_cmd("'a,'bt") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "'a") end) |
||||||
|
it('should return the end range', |
||||||
|
function() expect.equal(result.end_range, "'b") end) |
||||||
|
it('should return the end range type', |
||||||
|
function() expect.equal(result.end_range_type, "mark") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "t") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with a complete mark range', function() |
||||||
|
describe('when it has increment', function() |
||||||
|
local result = parse_cmd("'a+++-5,'b+20-t") |
||||||
|
it('should return the start range increment', |
||||||
|
function() expect.equal(result.start_increment, "+++-5") end) |
||||||
|
it('should return the start range increment number', |
||||||
|
function() expect.equal(result.start_increment_number, -2) end) |
||||||
|
it('should return the end range increment', |
||||||
|
function() expect.equal(result.end_increment, "+20-") end) |
||||||
|
it('should return the end range increment number', |
||||||
|
function() expect.equal(result.end_increment_number, 19) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single forward search range', function() |
||||||
|
local result = parse_cmd("/hello/d") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "/hello/") end) |
||||||
|
it('should return the start range type', |
||||||
|
function() expect.equal(result.start_range_type, "forward_search") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "d") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single forward search range', function() |
||||||
|
describe('when it has increments', function() |
||||||
|
local result = parse_cmd("/hello/+10-2d") |
||||||
|
it('should return the start increment', |
||||||
|
function() expect.equal(result.start_increment, "+10-2") end) |
||||||
|
it('should return the start increment number', |
||||||
|
function() expect.equal(result.start_increment_number, 8) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single backward search range', function() |
||||||
|
local result = parse_cmd("?hello?pu") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "?hello?") end) |
||||||
|
it('should return the start range type', |
||||||
|
function() expect.equal(result.start_range_type, "backward_search") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "pu") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single backward search range', function() |
||||||
|
describe('when it has increments', function() |
||||||
|
local result = parse_cmd("?hello?+10-3pu") |
||||||
|
it('should return the start increment', |
||||||
|
function() expect.equal(result.start_increment, "+10-3") end) |
||||||
|
it('should return the start increment number', |
||||||
|
function() expect.equal(result.start_increment_number, 7) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single special range', function() |
||||||
|
local result = parse_cmd("%y") |
||||||
|
it('should return the start range', |
||||||
|
function() expect.equal(result.start_range, "%") end) |
||||||
|
it('should return the start range type', |
||||||
|
function() expect.equal(result.start_range_type, "%") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.command, "y") end) |
||||||
|
end) |
||||||
|
|
||||||
|
describe('when it is called with single special range', function() |
||||||
|
describe('when it has increment', function() |
||||||
|
local result = parse_cmd("%y+20---") |
||||||
|
it('should return the start increment', |
||||||
|
function() expect.equal(result.start_increment, "+20---") end) |
||||||
|
it('should return the command', |
||||||
|
function() expect.equal(result.start_increment_number, 17) end) |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
-- Bug test: mark range '<,'> cannot be detected |
||||||
|
describe('when it is called with complete mark range', function () |
||||||
|
local result = parse_cmd("'<,'>") |
||||||
|
it('should return the start range type', function () |
||||||
|
expect.equal(result.start_range_type, "mark") |
||||||
|
end) |
||||||
|
it('should return the start range', function() |
||||||
|
expect.equal(result.start_range, "'<") |
||||||
|
end) |
||||||
|
it('should return the end range', function() |
||||||
|
expect.equal(result.end_range, "'>") |
||||||
|
end) |
||||||
|
end) |
||||||
|
|
||||||
|
lester.report() -- Print overall statistic of the tests run. |
||||||
|
lester.exit() -- Exit with success if all tests passed. |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2021 Rafael Quintela |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,47 @@ |
|||||||
|
# nvim-goc.lua |
||||||
|
easy go coverage |
||||||
|
|
||||||
|
 |
||||||
|
|
||||||
|
## Setup |
||||||
|
|
||||||
|
```lua |
||||||
|
|
||||||
|
-- if set, when we switch between buffers, it will not split more than once. It will switch to the existing buffer instead |
||||||
|
vim.opt.switchbuf = 'useopen' |
||||||
|
|
||||||
|
local goc = require'nvim-goc' |
||||||
|
goc.setup({ verticalSplit = false }) |
||||||
|
|
||||||
|
|
||||||
|
vim.api.nvim_set_keymap('n', '<Leader>gcr', ':lua require("nvim-goc").Coverage()<CR>', {silent=true}) |
||||||
|
vim.api.nvim_set_keymap('n', '<Leader>gcc', ':lua require("nvim-goc").ClearCoverage()<CR>', {silent=true}) |
||||||
|
vim.api.nvim_set_keymap('n', '<Leader>gct', ':lua require("nvim-goc").CoverageFunc()<CR>', {silent=true}) |
||||||
|
vim.api.nvim_set_keymap('n', '<Leader>gca', ':lua cf(false)<CR><CR>', {silent=true}) |
||||||
|
vim.api.nvim_set_keymap('n', '<Leader>gcb', ':lua cf(true)<CR><CR>', {silent=true}) |
||||||
|
|
||||||
|
_G.cf = function(testCurrentFunction) |
||||||
|
local cb = function(path) |
||||||
|
if path then |
||||||
|
vim.cmd(":silent exec \"!xdg-open " .. path .. "\"") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if testCurrentFunction then |
||||||
|
goc.CoverageFunc(nil, cb, 0) |
||||||
|
else |
||||||
|
goc.Coverage(nil, cb) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- default colors |
||||||
|
-- vim.highlight.link('GocNormal', 'Comment') |
||||||
|
-- vim.highlight.link('GocCovered', 'String') |
||||||
|
-- vim.highlight.link('GocUncovered', 'Error') |
||||||
|
|
||||||
|
-- alternate between test file and normal file |
||||||
|
vim.api.nvim_set_keymap('n', ']a', ':lua require("nvim-goc").Alternate()<CR>', {silent=true}) |
||||||
|
|
||||||
|
-- alternate in a new split |
||||||
|
vim.api.nvim_set_keymap('n', '[a', ':lua require("nvim-goc").Alternate(true)<CR>', {silent=true}) |
||||||
|
``` |
@ -0,0 +1,203 @@ |
|||||||
|
local ts_utils = require "nvim-treesitter.ts_utils" |
||||||
|
|
||||||
|
local M = { |
||||||
|
hi = vim.api.nvim_create_namespace("goc"), |
||||||
|
errBuf = nil, |
||||||
|
splitCmd = 'sp ', |
||||||
|
splitSBCmd = 'to ', |
||||||
|
} |
||||||
|
|
||||||
|
M.Show = function() |
||||||
|
local source = {} |
||||||
|
for i, o in pairs(store) do |
||||||
|
table.insert(source, i .. '\t' .. table.concat(o.regcontents, "\n")) |
||||||
|
end |
||||||
|
|
||||||
|
local w = vim.fn["fzf#wrap"]('Yanks', { |
||||||
|
source = source, |
||||||
|
}) |
||||||
|
w["sink*"] = function(line) |
||||||
|
local o = store[tonumber(string.gmatch(line[2], '%d+')())] |
||||||
|
vim.fn.setreg(vim.v.register, table.concat(o.regcontents, "\n"), o.regtype) |
||||||
|
end |
||||||
|
vim.fn["fzf#run"](w) |
||||||
|
end |
||||||
|
|
||||||
|
M.setup = function(opts) |
||||||
|
vim.highlight.link('GocNormal', 'NONE') |
||||||
|
vim.highlight.link('GocCovered', 'DiffAdd') |
||||||
|
vim.highlight.link('GocUncovered', 'SpellBad') |
||||||
|
|
||||||
|
if opts then |
||||||
|
verticalSplit = opts.verticalSplit or false |
||||||
|
assert(type(verticalSplit) == "boolean", "verticalSplit must be boolean or nil") |
||||||
|
M.splitCmd = verticalSplit and 'vsp ' or 'sp ' |
||||||
|
M.splitSBCmd = verticalSplit and 'vert ' or 'to ' |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
M.Coverage = function(fn, html) |
||||||
|
print('[goc] ...') |
||||||
|
if M.errBuf ~= nil then |
||||||
|
vim.api.nvim_buf_set_lines(M.errBuf, 0, -1, false, {"..."}) |
||||||
|
end |
||||||
|
local fullPathFile = string.gsub(vim.api.nvim_buf_get_name(0), "_test", "") |
||||||
|
local bufnr = vim.uri_to_bufnr("file://" .. fullPathFile) |
||||||
|
|
||||||
|
local relativeFile = string.gsub(vim.fn.expand('%'), "_test", "") |
||||||
|
local package = vim.fn.expand('%:p:h') |
||||||
|
local tmp = vim.api.nvim_eval('tempname()') |
||||||
|
|
||||||
|
local h = nil |
||||||
|
|
||||||
|
-- local args = {'test', '-coverprofile', tmp, package .. '/...'} |
||||||
|
local args = {'test', '-coverprofile', tmp, package} |
||||||
|
if fn then |
||||||
|
args = {'test', '-coverprofile', tmp, "-run", fn, package} |
||||||
|
end |
||||||
|
|
||||||
|
if M.errBuf == nil then |
||||||
|
M.errBuf = vim.api.nvim_create_buf(true, true) |
||||||
|
end |
||||||
|
|
||||||
|
local stdout = vim.loop.new_pipe(false) |
||||||
|
local stderr = vim.loop.new_pipe(false) |
||||||
|
h = vim.loop.spawn('go', {args = args, stdio = {nil, stdout, stderr}}, vim.schedule_wrap(function(code, signal) |
||||||
|
M.ClearCoverage(bufnr) |
||||||
|
|
||||||
|
stdout:read_stop() |
||||||
|
stderr:read_stop() |
||||||
|
h:close() |
||||||
|
|
||||||
|
if code == 0 then |
||||||
|
local percent = string.gmatch(table.concat(vim.api.nvim_buf_get_lines(M.errBuf, 0, -1, true)), 'coverage: (%d+)')() |
||||||
|
if percent ~= nil then |
||||||
|
print('[goc] coverage', percent .. '%') |
||||||
|
if #vim.fn.win_findbuf(M.errBuf) > 0 then |
||||||
|
vim.api.nvim_buf_delete(M.errBuf, {force=true}) |
||||||
|
M.errBuf = nil |
||||||
|
end |
||||||
|
else |
||||||
|
print("[goc] check output!") |
||||||
|
if #vim.fn.win_findbuf(M.errBuf) == 0 then |
||||||
|
vim.cmd("vert sb " .. M.errBuf) |
||||||
|
end |
||||||
|
end |
||||||
|
if html then |
||||||
|
local tmphtml = vim.api.nvim_eval('tempname()') .. '.html' |
||||||
|
vim.cmd(':silent exec "!go tool cover -html='.. tmp ..' -o '.. tmphtml ..'"') |
||||||
|
html(tmphtml) |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
if not vim.api.nvim_buf_is_loaded(bufnr) or #vim.fn.win_findbuf(bufnr) == 0 then |
||||||
|
vim.cmd(M.splitCmd .. string.gsub(fullPathFile, vim.fn.getcwd() .. '/', '')) |
||||||
|
elseif vim.tbl_contains(vim.opt.switchbuf:get(), 'useopen') then |
||||||
|
vim.cmd(":sb " .. string.gsub(fullPathFile, vim.fn.getcwd() .. '/', '')) |
||||||
|
end |
||||||
|
|
||||||
|
for i = 0,vim.fn.line('$') do |
||||||
|
vim.api.nvim_buf_add_highlight(bufnr, M.hi, "GocNormal", i, 0, -1) |
||||||
|
end |
||||||
|
|
||||||
|
local lines = vim.api.nvim_eval('readfile("' .. tmp .. '")') |
||||||
|
for i = 2,#lines do |
||||||
|
local path = string.gmatch(lines[i], '(.+):')() |
||||||
|
if string.find(path, relativeFile) then |
||||||
|
local marks = string.gmatch(string.gsub(lines[i], '[^:]+:', ''), '%d+') |
||||||
|
|
||||||
|
local startline = math.max(tonumber(marks()) - 1, 0) |
||||||
|
local startcol = math.max(tonumber(marks()) - 1, 0) |
||||||
|
local endline = math.max(tonumber(marks()) -1, 0) |
||||||
|
local endcol = tonumber(marks()) |
||||||
|
local numstmt = tonumber(marks()) |
||||||
|
local cnt = tonumber(marks()) |
||||||
|
|
||||||
|
local hig = "GocUncovered" |
||||||
|
if cnt == 1 then |
||||||
|
hig = "GocCovered" |
||||||
|
end |
||||||
|
|
||||||
|
for y = startline,endline do |
||||||
|
local sc = 0 |
||||||
|
local ec = -1 |
||||||
|
if startline == y then |
||||||
|
sc = startcol |
||||||
|
end |
||||||
|
if endline == y then |
||||||
|
ec = endcol |
||||||
|
end |
||||||
|
|
||||||
|
vim.api.nvim_buf_add_highlight(bufnr, M.hi, hig, y, sc, ec) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
else |
||||||
|
print("[goc] fail!") |
||||||
|
if #vim.fn.win_findbuf(M.errBuf) == 0 then |
||||||
|
vim.cmd(M.splitSBCmd .. " sb " .. M.errBuf) |
||||||
|
end |
||||||
|
end |
||||||
|
end)) |
||||||
|
|
||||||
|
local writeToScratch = function(err, data) |
||||||
|
vim.schedule(function() |
||||||
|
if err then |
||||||
|
vim.api.nvim_buf_set_lines(M.errBuf, -1, -1, false, vim.split(err, "\n")) |
||||||
|
return |
||||||
|
end |
||||||
|
if data then |
||||||
|
vim.api.nvim_buf_set_lines(M.errBuf, -1, -1, false, vim.split(data, "\n")) |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
vim.loop.read_start(stdout, writeToScratch) |
||||||
|
vim.loop.read_start(stderr, writeToScratch) |
||||||
|
end |
||||||
|
|
||||||
|
M.CoverageFunc = function(p, html) |
||||||
|
if not p then |
||||||
|
p = ts_utils.get_node_at_cursor() |
||||||
|
if not p then |
||||||
|
print("[goc] no test function found") |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
if p:type() ~= "function_declaration" then |
||||||
|
p = p:parent() |
||||||
|
if not p then |
||||||
|
print("[goc] no test function found") |
||||||
|
return |
||||||
|
end |
||||||
|
return M.CoverageFunc(p, html) |
||||||
|
end |
||||||
|
return M.Coverage(string.gmatch(ts_utils.get_node_text(p)[1], 'Test[^%s%(]+')(), html) |
||||||
|
end |
||||||
|
|
||||||
|
M.ClearCoverage = function(bufnr) |
||||||
|
print("BUF :: ", bufnr) |
||||||
|
-- print("MHI :: ", M.hi) |
||||||
|
vim.api.nvim_buf_clear_namespace(bufnr, M.hi, 0, -1) |
||||||
|
end |
||||||
|
|
||||||
|
M.Alternate = function(split) |
||||||
|
local path, file, ext = string.match(vim.api.nvim_buf_get_name(0), "(.+/)([^.]+)%.(.+)$") |
||||||
|
if ext == "go" then |
||||||
|
local aux = '_test.' |
||||||
|
if string.find(file, '_test') then |
||||||
|
aux = '.' |
||||||
|
path, file, ext = string.match(vim.api.nvim_buf_get_name(0), "(.+/)([^.]+)_test%.(.+)$") |
||||||
|
end |
||||||
|
|
||||||
|
local bufnr = vim.uri_to_bufnr("file://" .. path .. file .. aux .. ext) |
||||||
|
if not vim.api.nvim_buf_is_loaded(bufnr) or #vim.fn.win_findbuf(bufnr) == 0 then |
||||||
|
local cmd = split and M.splitCmd or 'e ' |
||||||
|
vim.cmd(cmd .. path .. file .. aux .. ext) |
||||||
|
elseif vim.tbl_contains(vim.opt.switchbuf:get(), 'useopen') then |
||||||
|
vim.cmd(":" .. M.splitSBCmd .. "sb " .. path .. file .. aux .. ext) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,283 @@ |
|||||||
|
# Contributing to `nvim-treesitter` |
||||||
|
|
||||||
|
First of all, thank you very much for contributing to `nvim-treesitter`. |
||||||
|
|
||||||
|
If you haven't already, you should really come and reach out to us on our [Zulip] |
||||||
|
server, so we can help you with any question you might have! |
||||||
|
There is also a [Matrix channel] for tree-sitter support in Neovim. |
||||||
|
|
||||||
|
As you know, `nvim-treesitter` is roughly split in two parts: |
||||||
|
|
||||||
|
- Parser configurations : for various things like `locals`, `highlights` |
||||||
|
- What we like to call *modules* : tiny lua modules that provide a given feature, based on parser configurations |
||||||
|
|
||||||
|
Depending on which part of the plugin you want to contribute to, please read the appropriate section. |
||||||
|
|
||||||
|
## Style Checks and Tests |
||||||
|
|
||||||
|
We haven't implemented any functional tests yet. Feel free to contribute. |
||||||
|
However, we check code style with `luacheck` and `stylua`! |
||||||
|
Please install luacheck and activate our `pre-push` hook to automatically check style before |
||||||
|
every push: |
||||||
|
|
||||||
|
```bash |
||||||
|
luarocks install luacheck |
||||||
|
cargo install stylua |
||||||
|
ln -s ../../scripts/pre-push .git/hooks/pre-push |
||||||
|
``` |
||||||
|
|
||||||
|
## Adding new modules |
||||||
|
|
||||||
|
If you want to see a new functionality added to `nvim-treesitter` feel free to first open an issue |
||||||
|
to that we can track our solution! |
||||||
|
Thus far, there is basically two types of modules: |
||||||
|
|
||||||
|
- Little modules (like `incremental selection`) that are built in `nvim-treesitter`, we call them |
||||||
|
`builtin modules`. |
||||||
|
- Bigger modules (like `completion-treesitter`, or `nvim-tree-docs`), or modules that integrate |
||||||
|
with other plugins, that we call `remote modules`. |
||||||
|
|
||||||
|
In any case, you can build your own module ! To help you started in the process, we have a template |
||||||
|
repository designed to build new modules [here](https://github.com/nvim-treesitter/module-template). |
||||||
|
Feel free to use it, and contact us over on our |
||||||
|
[Zulip] or on the "Neovim tree-sitter" [Matrix channel]. |
||||||
|
|
||||||
|
## Parser configurations |
||||||
|
|
||||||
|
Contributing to parser configurations is basically modifying one of the `queries/*/*.scm`. |
||||||
|
Each of these `scheme` files contains a *tree-sitter query* for a given purpose. |
||||||
|
Before going any further, we highly suggest that you [read more about tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries). |
||||||
|
|
||||||
|
Each query has an appropriate name, which is then used by modules to extract data from the syntax tree. |
||||||
|
For now these are the types of queries used by `nvim-treesitter`: |
||||||
|
|
||||||
|
- `highlights.scm`: used for syntax highlighting, using the `highlight` module. |
||||||
|
- `locals.scm`: used to extract keyword definitions, scopes, references, etc, using the `locals` module. |
||||||
|
- `textobjects.scm`: used to define text objects. |
||||||
|
- `folds.scm`: used to define folds. |
||||||
|
- `injections.scm`: used to define injections. |
||||||
|
|
||||||
|
For these types there is a *norm* you will have to follow so that features work fine. |
||||||
|
Here are some global advices : |
||||||
|
|
||||||
|
- If your language is listed [here](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages), |
||||||
|
you can install the [playground plugin](https://github.com/nvim-treesitter/playground). |
||||||
|
- If your language is listed [here](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries), |
||||||
|
you can debug and experiment with your queries there. |
||||||
|
- If not, you should consider installing the [tree-sitter cli](https://github.com/tree-sitter/tree-sitter/tree/master/cli), |
||||||
|
you should then be able to open a local playground using `tree-sitter build-wasm && tree-sitter web-ui` within the |
||||||
|
parsers repo. |
||||||
|
- Examples of queries can be found in [queries/](queries/) |
||||||
|
- Matches in the bottom will override queries that are above of them. |
||||||
|
|
||||||
|
If your language is an extension of a language (TypeScript is an extension of JavaScript for |
||||||
|
example), you can include the queries from your base language by adding the following _as the first |
||||||
|
line of your file_. |
||||||
|
|
||||||
|
```query |
||||||
|
; inherits: lang1,(optionallang) |
||||||
|
``` |
||||||
|
|
||||||
|
If you want to inherit a language, but don't want the languages inheriting from yours to inherit it, |
||||||
|
you can mark the language as optional (by putting it between parenthesis). |
||||||
|
|
||||||
|
### Highlights |
||||||
|
|
||||||
|
As languages differ quite a lot, here is a set of captures available to you when building a `highlights.scm` query. |
||||||
|
One important thing to note is that many of these capture groups are not supported by `neovim` for now, and will not have any |
||||||
|
effect on highlighting. We will work on improving highlighting in the near future though. |
||||||
|
|
||||||
|
#### Misc |
||||||
|
|
||||||
|
``` |
||||||
|
@comment |
||||||
|
@error for error `ERROR` nodes. |
||||||
|
@none to disable completely the highlight |
||||||
|
@punctuation.delimiter for `;` `.` `,` |
||||||
|
@punctuation.bracket for `()` or `{}` |
||||||
|
@punctuation.special for symbols with special meaning like `{}` in string interpolation. |
||||||
|
``` |
||||||
|
|
||||||
|
#### Constants |
||||||
|
|
||||||
|
``` |
||||||
|
@constant |
||||||
|
@constant.builtin |
||||||
|
@constant.macro |
||||||
|
@string |
||||||
|
@string.regex |
||||||
|
@string.escape |
||||||
|
@string.special |
||||||
|
@character |
||||||
|
@number |
||||||
|
@boolean |
||||||
|
@float |
||||||
|
``` |
||||||
|
|
||||||
|
#### Functions |
||||||
|
|
||||||
|
``` |
||||||
|
@function |
||||||
|
@function.builtin |
||||||
|
@function.macro |
||||||
|
@parameter |
||||||
|
|
||||||
|
@method |
||||||
|
@field |
||||||
|
@property |
||||||
|
|
||||||
|
@constructor |
||||||
|
``` |
||||||
|
|
||||||
|
#### Keywords |
||||||
|
|
||||||
|
``` |
||||||
|
@conditional (e.g. `if`, `else`) |
||||||
|
@repeat (e.g. `for`, `while`) |
||||||
|
@label for C/Lua-like labels |
||||||
|
@keyword |
||||||
|
@keyword.function (keyword to define a function, e.g. `func` in Go, `def` in Python) |
||||||
|
@keyword.operator (for operators that are English words, e.g. `and`, `or`) |
||||||
|
@keyword.return |
||||||
|
@operator (for symbolic operators, e.g. `+`, `*`) |
||||||
|
@exception (e.g. `throw`, `catch`) |
||||||
|
@include keywords for including modules (e.g. import/from in Python) |
||||||
|
|
||||||
|
@type |
||||||
|
@type.builtin |
||||||
|
@namespace for identifiers referring to namespaces |
||||||
|
@symbol for identifiers referring to symbols |
||||||
|
@attribute for e.g. Python decorators |
||||||
|
``` |
||||||
|
|
||||||
|
#### Variables |
||||||
|
|
||||||
|
``` |
||||||
|
@variable |
||||||
|
@variable.builtin |
||||||
|
``` |
||||||
|
|
||||||
|
#### Text |
||||||
|
|
||||||
|
Mainly for markup languages. |
||||||
|
|
||||||
|
``` |
||||||
|
@text |
||||||
|
@text.strong |
||||||
|
@text.emphasis |
||||||
|
@text.underline |
||||||
|
@text.strike |
||||||
|
@text.title |
||||||
|
@text.literal |
||||||
|
@text.uri |
||||||
|
@text.math (e.g. for LaTeX math environments) |
||||||
|
@text.environment (e.g. for text environments of markup languages) |
||||||
|
@text.environment.name (e.g. for the name/the string indicating the type of text environment) |
||||||
|
@text.reference (for footnotes, text references, citations) |
||||||
|
|
||||||
|
@text.note |
||||||
|
@text.warning |
||||||
|
@text.danger |
||||||
|
``` |
||||||
|
|
||||||
|
#### Tags |
||||||
|
|
||||||
|
Used for xml-like tags |
||||||
|
|
||||||
|
``` |
||||||
|
@tag |
||||||
|
@tag.attribute |
||||||
|
@tag.delimiter |
||||||
|
``` |
||||||
|
|
||||||
|
### Locals |
||||||
|
|
||||||
|
``` |
||||||
|
@definition for various definitions |
||||||
|
@definition.constant |
||||||
|
@definition.function |
||||||
|
@definition.method |
||||||
|
@definition.var |
||||||
|
@definition.parameter |
||||||
|
@definition.macro |
||||||
|
@definition.type |
||||||
|
@definition.field |
||||||
|
@definition.enum |
||||||
|
@definition.namespace for modules or C++ namespaces |
||||||
|
@definition.import for imported names |
||||||
|
|
||||||
|
@definition.associated to determine the type of a variable |
||||||
|
@definition.doc for documentation adjacent to a definition. E.g. |
||||||
|
|
||||||
|
@scope |
||||||
|
@reference |
||||||
|
@constructor |
||||||
|
``` |
||||||
|
|
||||||
|
#### Definition Scope |
||||||
|
|
||||||
|
You can set the scope of a definition by setting the `scope` property on the definition. |
||||||
|
|
||||||
|
For example, a javascript function declaration creates a scope. The function name is captured as the definition. |
||||||
|
This means that the function definition would only be available WITHIN the scope of the function, which is not the case. |
||||||
|
The definition can be used in the scope the function was defined in. |
||||||
|
|
||||||
|
```javascript |
||||||
|
function doSomething() {} |
||||||
|
|
||||||
|
doSomething(); // Should point to the declaration as the definition |
||||||
|
``` |
||||||
|
|
||||||
|
```query |
||||||
|
(function_declaration |
||||||
|
((identifier) @definition.var) |
||||||
|
(#set! "definition.var.scope" "parent")) |
||||||
|
``` |
||||||
|
|
||||||
|
Possible scope values are: |
||||||
|
|
||||||
|
- `parent`: The definition is valid in the containing scope and one more scope above that scope |
||||||
|
- `global`: The definition is valid in the root scope |
||||||
|
- `local`: The definition is valid in the containing scope. This is the default behavior |
||||||
|
|
||||||
|
### Folds |
||||||
|
|
||||||
|
You can define folds for a given language by adding a `folds.scm` query : |
||||||
|
|
||||||
|
``` |
||||||
|
@fold |
||||||
|
``` |
||||||
|
|
||||||
|
If the `fold.scm` query is not present, this will fallback to the `@scope` captures in the `locals` |
||||||
|
query. |
||||||
|
|
||||||
|
### Injections |
||||||
|
|
||||||
|
Some captures are related to language injection (like markdown code blocks). They are used in `injections.scm`. |
||||||
|
You can directly use the name of the language that you want to inject (e.g. `@html` to inject html). |
||||||
|
|
||||||
|
If you want to dynamically detect the language (e.g. for Markdown blocks) use the `@language` to capture |
||||||
|
the node describing the language and `@content` to describe the injection region. |
||||||
|
|
||||||
|
``` |
||||||
|
@{language} ; e.g. @html to describe a html region |
||||||
|
|
||||||
|
@language ; dynamic detection of the injection language (i.e. the text of the captured node describes the language). |
||||||
|
@content ; region for the dynamically detected language. |
||||||
|
@combined ; This will combine all matches of a pattern as one single block of content. |
||||||
|
``` |
||||||
|
|
||||||
|
### Indents |
||||||
|
|
||||||
|
``` |
||||||
|
@indent ; Indent children when matching this node |
||||||
|
@indent_end ; Marks the end of indented block |
||||||
|
@aligned_indent ; Behaves like python aligned/hanging indent |
||||||
|
@dedent ; Dedent children when matching this node |
||||||
|
@branch ; Dedent itself when matching this node |
||||||
|
@ignore ; Do not indent in this node |
||||||
|
@auto ; Behaves like 'autoindent' buffer option |
||||||
|
``` |
||||||
|
|
||||||
|
[Zulip]: nvim-treesitter.zulipchat.com |
||||||
|
[Matrix channel]: https://matrix.to/#/#nvim-treesitter:matrix.org |
@ -0,0 +1,201 @@ |
|||||||
|
Apache License |
||||||
|
Version 2.0, January 2004 |
||||||
|
http://www.apache.org/licenses/ |
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||||
|
|
||||||
|
1. Definitions. |
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||||
|
the copyright owner that is granting the License. |
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||||
|
other entities that control, are controlled by, or are under common |
||||||
|
control with that entity. For the purposes of this definition, |
||||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||||
|
direction or management of such entity, whether by contract or |
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||||
|
exercising permissions granted by this License. |
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, |
||||||
|
including but not limited to software source code, documentation |
||||||
|
source, and configuration files. |
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical |
||||||
|
transformation or translation of a Source form, including but |
||||||
|
not limited to compiled object code, generated documentation, |
||||||
|
and conversions to other media types. |
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||||
|
Object form, made available under the License, as indicated by a |
||||||
|
copyright notice that is included in or attached to the work |
||||||
|
(an example is provided in the Appendix below). |
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||||
|
form, that is based on (or derived from) the Work and for which the |
||||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||||
|
of this License, Derivative Works shall not include works that remain |
||||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||||
|
the Work and Derivative Works thereof. |
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including |
||||||
|
the original version of the Work and any modifications or additions |
||||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||||
|
means any form of electronic, verbal, or written communication sent |
||||||
|
to the Licensor or its representatives, including but not limited to |
||||||
|
communication on electronic mailing lists, source code control systems, |
||||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||||
|
excluding communication that is conspicuously marked or otherwise |
||||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||||
|
subsequently incorporated within the Work. |
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||||
|
Work and such Derivative Works in Source or Object form. |
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
(except as stated in this section) patent license to make, have made, |
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||||
|
where such license applies only to those patent claims licensable |
||||||
|
by such Contributor that are necessarily infringed by their |
||||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||||
|
institute patent litigation against any entity (including a |
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||||
|
or a Contribution incorporated within the Work constitutes direct |
||||||
|
or contributory patent infringement, then any patent licenses |
||||||
|
granted to You under this License for that Work shall terminate |
||||||
|
as of the date such litigation is filed. |
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||||
|
Work or Derivative Works thereof in any medium, with or without |
||||||
|
modifications, and in Source or Object form, provided that You |
||||||
|
meet the following conditions: |
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or |
||||||
|
Derivative Works a copy of this License; and |
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices |
||||||
|
stating that You changed the files; and |
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||||
|
that You distribute, all copyright, patent, trademark, and |
||||||
|
attribution notices from the Source form of the Work, |
||||||
|
excluding those notices that do not pertain to any part of |
||||||
|
the Derivative Works; and |
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||||
|
distribution, then any Derivative Works that You distribute must |
||||||
|
include a readable copy of the attribution notices contained |
||||||
|
within such NOTICE file, excluding those notices that do not |
||||||
|
pertain to any part of the Derivative Works, in at least one |
||||||
|
of the following places: within a NOTICE text file distributed |
||||||
|
as part of the Derivative Works; within the Source form or |
||||||
|
documentation, if provided along with the Derivative Works; or, |
||||||
|
within a display generated by the Derivative Works, if and |
||||||
|
wherever such third-party notices normally appear. The contents |
||||||
|
of the NOTICE file are for informational purposes only and |
||||||
|
do not modify the License. You may add Your own attribution |
||||||
|
notices within Derivative Works that You distribute, alongside |
||||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||||
|
that such additional attribution notices cannot be construed |
||||||
|
as modifying the License. |
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and |
||||||
|
may provide additional or different license terms and conditions |
||||||
|
for use, reproduction, or distribution of Your modifications, or |
||||||
|
for any such Derivative Works as a whole, provided Your use, |
||||||
|
reproduction, and distribution of the Work otherwise complies with |
||||||
|
the conditions stated in this License. |
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||||
|
by You to the Licensor shall be under the terms and conditions of |
||||||
|
this License, without any additional terms or conditions. |
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||||
|
the terms of any separate license agreement you may have executed |
||||||
|
with Licensor regarding such Contributions. |
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||||
|
except as required for reasonable and customary use in describing the |
||||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||||
|
agreed to in writing, Licensor provides the Work (and each |
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||||
|
implied, including, without limitation, any warranties or conditions |
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||||
|
appropriateness of using or redistributing the Work and assume any |
||||||
|
risks associated with Your exercise of permissions under this License. |
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||||
|
whether in tort (including negligence), contract, or otherwise, |
||||||
|
unless required by applicable law (such as deliberate and grossly |
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||||
|
liable to You for damages, including any direct, indirect, special, |
||||||
|
incidental, or consequential damages of any character arising as a |
||||||
|
result of this License or out of the use or inability to use the |
||||||
|
Work (including but not limited to damages for loss of goodwill, |
||||||
|
work stoppage, computer failure or malfunction, or any and all |
||||||
|
other commercial damages or losses), even if such Contributor |
||||||
|
has been advised of the possibility of such damages. |
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||||
|
or other liability obligations and/or rights consistent with this |
||||||
|
License. However, in accepting such obligations, You may act only |
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||||
|
of any other Contributor, and only if You agree to indemnify, |
||||||
|
defend, and hold each Contributor harmless for any liability |
||||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||||
|
of your accepting any such warranty or additional liability. |
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS |
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work. |
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following |
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||||
|
replaced with your own identifying information. (Don't include |
||||||
|
the brackets!) The text should be enclosed in the appropriate |
||||||
|
comment syntax for the file format. We also recommend that a |
||||||
|
file or class name and description of purpose be included on the |
||||||
|
same "printed page" as the copyright notice for easier |
||||||
|
identification within third-party archives. |
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner] |
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
you may not use this file except in compliance with the License. |
||||||
|
You may obtain a copy of the License at |
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software |
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
See the License for the specific language governing permissions and |
||||||
|
limitations under the License. |
@ -0,0 +1,553 @@ |
|||||||
|
<div align="center"> |
||||||
|
<h1>nvim-treesitter</h1> |
||||||
|
<p> |
||||||
|
<a href="https://nvim-treesitter.zulipchat.com/"> |
||||||
|
<img alt="Zulip Chat" src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" /> |
||||||
|
</a> |
||||||
|
<a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Linting+and+style+checking%22+branch%3Amaster"> |
||||||
|
<img alt="Linting and Style" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Linting%20and%20style%20checking/badge.svg" /> |
||||||
|
</a> |
||||||
|
<a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Check+loading+of+syntax+files%22+branch%3Amaster"> |
||||||
|
<img alt="Syntax files" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Check%20loading%20of%20syntax%20files/badge.svg" /> |
||||||
|
</a> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div align="center"> |
||||||
|
<p> |
||||||
|
<img src="assets/logo.png" align="center" alt="Logo" /> |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
<a href="https://github.com/tree-sitter/tree-sitter">Treesitter</a> |
||||||
|
configurations and abstraction layer for |
||||||
|
<a href="https://github.com/neovim/neovim/">Neovim</a>. |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
<i> |
||||||
|
Logo by <a href="https://github.com/steelsojka">@steelsojka</a> |
||||||
|
</i> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for [tree-sitter](https://github.com/tree-sitter/tree-sitter) in Neovim and to provide some basic functionality such as highlighting based on it: |
||||||
|
|
||||||
|
 |
||||||
|
|
||||||
|
Traditional highlighting (left) vs Treesitter-based highlighting (right). |
||||||
|
More examples can be found in [our gallery](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery). |
||||||
|
|
||||||
|
**Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim. |
||||||
|
Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable! |
||||||
|
We recommend using the nightly builds of Neovim if possible. |
||||||
|
You can find the current roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/projects/1). |
||||||
|
The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!** |
||||||
|
|
||||||
|
Nvim-treesitter is based on three interlocking features: [**language parsers**](#language-parsers), [**queries**](#adding-queries), and [**modules**](#available-modules), where *modules* provide features – e.g., highlighting – based on *queries* for syntax objects extracted from a given buffer by *language parsers*. |
||||||
|
Users will generally only need to interact with parsers and modules as explained in the next section. |
||||||
|
For more detailed information on setting these up, see ["Advanced setup"](#advanced-setup). |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### Table of contents |
||||||
|
|
||||||
|
* [Quickstart](#quickstart) |
||||||
|
* [Supported languages](#supported-languages) |
||||||
|
* [Available modules](#available-modules) |
||||||
|
* [Advanced setup](#advanced-setup) |
||||||
|
* [Extra features](#extra-features) |
||||||
|
* [Troubleshooting](#troubleshooting) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# Quickstart |
||||||
|
|
||||||
|
## Requirements |
||||||
|
|
||||||
|
- Neovim latest stable version or [nightly](https://github.com/neovim/neovim#install-from-source) |
||||||
|
- `tar` and `curl` in your path (or alternatively `git`) |
||||||
|
- A C compiler in your path and libstdc++ installed ([Windows users please read this!](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Windows-support)). |
||||||
|
|
||||||
|
## Installation |
||||||
|
|
||||||
|
You can install `nvim-treesitter` with your favorite package manager (or using the native `package` feature of vim, see `:h packages`). |
||||||
|
|
||||||
|
**NOTE: This plugin is only guaranteed to work with specific versions of language parsers** (as specified in the `lockfile.json`). **When upgrading the plugin, you must make sure that all installed parsers are updated to the latest version** via `:TSUpdate`. |
||||||
|
It is strongly recommended to automate this; e.g., if you are using [vim-plug](https://github.com/junegunn/vim-plug), put this in your `init.vim` file: |
||||||
|
|
||||||
|
```vim |
||||||
|
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} |
||||||
|
``` |
||||||
|
|
||||||
|
For other plugin managers such as `packer.nvim`, see this [Installation page from the wiki](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Installation) (Note that this page is community maintained). |
||||||
|
|
||||||
|
## Language parsers |
||||||
|
|
||||||
|
Treesitter uses a different _parser_ for every language, which needs to be generated via `tree-sitter-cli` from a `grammar.js` file, then compiled to a `.so` library that needs to be placed in neovim's `runtimepath` (typically under `parser/{language}.so`). |
||||||
|
To simplify this, `nvim-treesitter` provides commands to automate this process. |
||||||
|
If the language is already [supported by `nvim-treesitter`](#supported-languages), you can install it with |
||||||
|
```vim |
||||||
|
:TSInstall <language_to_install> |
||||||
|
``` |
||||||
|
This command supports tab expansion. |
||||||
|
You can also get a list of all available languages and their installation status with `:TSInstallInfo`. |
||||||
|
Parsers not on this list can be added manually by following the steps described under ["Adding parsers"](#adding-parsers) below. |
||||||
|
|
||||||
|
To make sure a parser is at the latest compatible version (as specified in `nvim-treesitter`'s `lockfile.json`), use `:TSUpdate {language}`. To update all parsers unconditionally, use `:TSUpdate all` or just `:TSUpdate`. |
||||||
|
|
||||||
|
## Modules |
||||||
|
|
||||||
|
Each module provides a distinct tree-sitter-based feature such as [highlighting](#highlight), [indentation](#indentation), or [folding](#folding); see [`:h nvim-treesitter-modules`](doc/nvim-treesitter.txt) or ["Available modules"](#available-modules) below for a list of modules and their options. |
||||||
|
|
||||||
|
All modules are disabled by default and need to be activated explicitly in your `init.vim`, e.g., via |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
-- One of "all", "maintained" (parsers with maintainers), or a list of languages |
||||||
|
ensure_installed = "maintained", |
||||||
|
|
||||||
|
-- Install languages synchronously (only applied to `ensure_installed`) |
||||||
|
sync_install = false, |
||||||
|
|
||||||
|
-- List of parsers to ignore installing |
||||||
|
ignore_install = { "javascript" }, |
||||||
|
|
||||||
|
highlight = { |
||||||
|
-- `false` will disable the whole extension |
||||||
|
enable = true, |
||||||
|
|
||||||
|
-- list of language that will be disabled |
||||||
|
disable = { "c", "rust" }, |
||||||
|
|
||||||
|
-- Setting this to true will run `:h syntax` and tree-sitter at the same time. |
||||||
|
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). |
||||||
|
-- Using this option may slow down your editor, and you may see some duplicate highlights. |
||||||
|
-- Instead of true it can also be a list of languages |
||||||
|
additional_vim_regex_highlighting = false, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
Each module can also be enabled or disabled interactively through the following commands: |
||||||
|
|
||||||
|
```vim |
||||||
|
:TSBufEnable {module} " enable module on current buffer |
||||||
|
:TSBufDisable {module} " disable module on current buffer |
||||||
|
:TSEnableAll {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype. |
||||||
|
:TSDisableAll {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype. |
||||||
|
:TSModuleInfo [{module}] " list information about modules state for each filetype |
||||||
|
``` |
||||||
|
|
||||||
|
Check [`:h nvim-treesitter-commands`](doc/nvim-treesitter.txt) for a list of all available commands. |
||||||
|
It may be necessary to reload the buffer (e.g., via `:e`) after enabling a module interactively. |
||||||
|
|
||||||
|
# Supported languages |
||||||
|
|
||||||
|
For `nvim-treesitter` to support a specific feature for a specific language requires both a parser for that language and an appropriate language-specific query file for that feature. |
||||||
|
|
||||||
|
The following is a list of languages for which a parser can be installed through `:TSInstall`; a checked box means that `nvim-treesitter` also contains queries at least for the `highlight` module. |
||||||
|
|
||||||
|
Experimental parsers are parsers that are maintained, but not stable enough for |
||||||
|
daily use yet. They are excluded from automatic installation when |
||||||
|
`ensure_installed` is set to `"maintained"`. |
||||||
|
|
||||||
|
We are looking for maintainers to add more parsers and to write query files for their languages. Check our [tracking issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/2282) for open language requests. |
||||||
|
|
||||||
|
<!--This section of the README is automatically updated by a CI job--> |
||||||
|
<!--parserinfo--> |
||||||
|
- [x] [bash](https://github.com/tree-sitter/tree-sitter-bash) (maintained by @TravonteD) |
||||||
|
- [x] [beancount](https://github.com/polarmutex/tree-sitter-beancount) (maintained by @polarmutex) |
||||||
|
- [x] [bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) (maintained by @theHamsta by asking @clason) |
||||||
|
- [x] [c](https://github.com/tree-sitter/tree-sitter-c) (maintained by @vigoux) |
||||||
|
- [x] [c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) (maintained by @Luxed) |
||||||
|
- [x] [clojure](https://github.com/sogaiu/tree-sitter-clojure) (maintained by @sogaiu) |
||||||
|
- [x] [cmake](https://github.com/uyha/tree-sitter-cmake) (maintained by @uyha) |
||||||
|
- [x] [comment](https://github.com/stsewd/tree-sitter-comment) (maintained by @stsewd) |
||||||
|
- [x] [commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) (maintained by @theHamsta) |
||||||
|
- [x] [cpp](https://github.com/tree-sitter/tree-sitter-cpp) (maintained by @theHamsta) |
||||||
|
- [x] [css](https://github.com/tree-sitter/tree-sitter-css) (maintained by @TravonteD) |
||||||
|
- [x] [cuda](https://github.com/theHamsta/tree-sitter-cuda) (maintained by @theHamsta) |
||||||
|
- [x] [d](https://github.com/CyberShadow/tree-sitter-d) (experimental, maintained by @nawordar) |
||||||
|
- [x] [dart](https://github.com/UserNobody14/tree-sitter-dart) (maintained by @Akin909) |
||||||
|
- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar) |
||||||
|
- [x] [dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) (maintained by @camdencheek) |
||||||
|
- [x] [dot](https://github.com/rydesun/tree-sitter-dot) (maintained by @rydesun) |
||||||
|
- [x] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay) |
||||||
|
- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @jonatanklosko) |
||||||
|
- [ ] [elm](https://github.com/elm-tooling/tree-sitter-elm) |
||||||
|
- [x] [erlang](https://github.com/AbstractMachinesLab/tree-sitter-erlang) (maintained by @ostera) |
||||||
|
- [x] [fennel](https://github.com/travonted/tree-sitter-fennel) (maintained by @TravonteD) |
||||||
|
- [x] [fish](https://github.com/ram02z/tree-sitter-fish) (maintained by @ram02z) |
||||||
|
- [x] [foam](https://github.com/FoamScience/tree-sitter-foam) (experimental, maintained by @FoamScience) |
||||||
|
- [ ] [fortran](https://github.com/stadelmanma/tree-sitter-fortran) |
||||||
|
- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn) |
||||||
|
- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @Shatur95) |
||||||
|
- [x] [Glimmer and Ember](https://github.com/alexlafroscia/tree-sitter-glimmer) (maintained by @alexlafroscia) |
||||||
|
- [x] [glsl](https://github.com/theHamsta/tree-sitter-glsl) (maintained by @theHamsta) |
||||||
|
- [x] [go](https://github.com/tree-sitter/tree-sitter-go) (maintained by @theHamsta, @WinWisely268) |
||||||
|
- [x] [Godot Resources (gdresource)](https://github.com/PrestonKnopp/tree-sitter-godot-resource) (maintained by @pierpo) |
||||||
|
- [x] [gomod](https://github.com/camdencheek/tree-sitter-go-mod) (maintained by @camdencheek) |
||||||
|
- [x] [gowork](https://github.com/omertuc/tree-sitter-go-work) (maintained by @omertuc) |
||||||
|
- [x] [graphql](https://github.com/bkegley/tree-sitter-graphql) (maintained by @bkegley) |
||||||
|
- [ ] [hack](https://github.com/slackhq/tree-sitter-hack) |
||||||
|
- [ ] [haskell](https://github.com/tree-sitter/tree-sitter-haskell) |
||||||
|
- [x] [hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann) |
||||||
|
- [x] [heex](https://github.com/connorlay/tree-sitter-heex) (maintained by @connorlay) |
||||||
|
- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410) |
||||||
|
- [x] [hocon](https://github.com/antosha417/tree-sitter-hocon) (maintained by @antosha417) |
||||||
|
- [x] [html](https://github.com/tree-sitter/tree-sitter-html) (maintained by @TravonteD) |
||||||
|
- [x] [http](https://github.com/NTBBloodbath/tree-sitter-http) (maintained by @NTBBloodbath) |
||||||
|
- [x] [java](https://github.com/tree-sitter/tree-sitter-java) (maintained by @p00f) |
||||||
|
- [x] [javascript](https://github.com/tree-sitter/tree-sitter-javascript) (maintained by @steelsojka) |
||||||
|
- [x] [jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) (maintained by @steelsojka) |
||||||
|
- [x] [json](https://github.com/tree-sitter/tree-sitter-json) (maintained by @steelsojka) |
||||||
|
- [x] [json5](https://github.com/Joakker/tree-sitter-json5) (maintained by @Joakker) |
||||||
|
- [x] [JSON with comments](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git) (maintained by @WhyNotHugo) |
||||||
|
- [x] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @mroavi, @theHamsta) |
||||||
|
- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa) |
||||||
|
- [x] [latex](https://github.com/latex-lsp/tree-sitter-latex) (maintained by @theHamsta by asking @clason) |
||||||
|
- [x] [ledger](https://github.com/cbarrete/tree-sitter-ledger) (maintained by @cbarrete) |
||||||
|
- [x] [llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) (maintained by @benwilliamgraham) |
||||||
|
- [x] [lua](https://github.com/MunifTanjim/tree-sitter-lua) (maintained by @muniftanjim) |
||||||
|
- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991) |
||||||
|
- [ ] [markdown](https://github.com/MDeiml/tree-sitter-markdown) |
||||||
|
- [x] [ninja](https://github.com/alemuller/tree-sitter-ninja) (maintained by @alemuller) |
||||||
|
- [x] [nix](https://github.com/cstrahan/tree-sitter-nix) (maintained by @leo60228) |
||||||
|
- [x] [norg](https://github.com/nvim-neorg/tree-sitter-norg) (maintained by @JoeyGrajciar, @vhyrro, @mrossinek) |
||||||
|
- [x] [ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) |
||||||
|
- [x] [ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) |
||||||
|
- [x] [ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) (maintained by @undu) |
||||||
|
- [x] [pascal](https://github.com/Isopod/tree-sitter-pascal.git) (maintained by @isopod) |
||||||
|
- [x] [perl](https://github.com/ganezdragon/tree-sitter-perl) (maintained by @ganezdragon) |
||||||
|
- [x] [php](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka) |
||||||
|
- [x] [phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) (experimental, maintained by @mikehaertl) |
||||||
|
- [x] [pioasm](https://github.com/leo60228/tree-sitter-pioasm) (maintained by @leo60228) |
||||||
|
- [x] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva) |
||||||
|
- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (maintained by @zealot128) |
||||||
|
- [x] [python](https://github.com/tree-sitter/tree-sitter-python) (maintained by @stsewd, @theHamsta) |
||||||
|
- [x] [ql](https://github.com/tree-sitter/tree-sitter-ql) (maintained by @pwntester) |
||||||
|
- [x] [Tree-sitter query language](https://github.com/nvim-treesitter/tree-sitter-query) (maintained by @steelsojka) |
||||||
|
- [x] [r](https://github.com/r-lib/tree-sitter-r) (maintained by @jimhester) |
||||||
|
- [x] [rasi](https://github.com/Fymyte/tree-sitter-rasi) (maintained by @Fymyte) |
||||||
|
- [x] [regex](https://github.com/tree-sitter/tree-sitter-regex) (maintained by @theHamsta) |
||||||
|
- [x] [rst](https://github.com/stsewd/tree-sitter-rst) (maintained by @stsewd) |
||||||
|
- [x] [ruby](https://github.com/tree-sitter/tree-sitter-ruby) (maintained by @TravonteD) |
||||||
|
- [x] [rust](https://github.com/tree-sitter/tree-sitter-rust) (maintained by @vigoux) |
||||||
|
- [x] [scala](https://github.com/tree-sitter/tree-sitter-scala) (maintained by @stevanmilic) |
||||||
|
- [x] [scss](https://github.com/serenadeai/tree-sitter-scss) (maintained by @elianiva) |
||||||
|
- [x] [sparql](https://github.com/BonaBeavis/tree-sitter-sparql) (maintained by @bonabeavis) |
||||||
|
- [x] [supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) (maintained by @madskjeldgaard) |
||||||
|
- [x] [surface](https://github.com/connorlay/tree-sitter-surface) (maintained by @connorlay) |
||||||
|
- [x] [svelte](https://github.com/Himujjal/tree-sitter-svelte) (maintained by @elianiva) |
||||||
|
- [ ] [swift](https://github.com/alex-pinkus/tree-sitter-swift) |
||||||
|
- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce) |
||||||
|
- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer) |
||||||
|
- [x] [toml](https://github.com/ikatyang/tree-sitter-toml) (maintained by @tk-shirasaka) |
||||||
|
- [x] [tsx](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) |
||||||
|
- [x] [turtle](https://github.com/BonaBeavis/tree-sitter-turtle) (maintained by @bonabeavis) |
||||||
|
- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) |
||||||
|
- [x] [verilog](https://github.com/tree-sitter/tree-sitter-verilog) (experimental, maintained by @zegervdv) |
||||||
|
- [x] [vim](https://github.com/vigoux/tree-sitter-viml) (maintained by @vigoux) |
||||||
|
- [x] [vue](https://github.com/ikatyang/tree-sitter-vue) (maintained by @WhyNotHugo) |
||||||
|
- [x] [yaml](https://github.com/ikatyang/tree-sitter-yaml) (maintained by @stsewd) |
||||||
|
- [x] [yang](https://github.com/Hubro/tree-sitter-yang) (maintained by @Hubro) |
||||||
|
- [x] [zig](https://github.com/maxxnino/tree-sitter-zig) (maintained by @maxxnino) |
||||||
|
<!--parserinfo--> |
||||||
|
|
||||||
|
|
||||||
|
# Available modules |
||||||
|
|
||||||
|
Modules provide the top-level features of `nvim-treesitter`. |
||||||
|
The following is a list of modules included in `nvim-treesitter` and their configuration via `init.vim` (where multiple modules can be combined in a single call to `setup`). |
||||||
|
Note that not all modules work for all languages (depending on the queries available for them). |
||||||
|
Additional modules can be provided as [external plugins](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Extra-modules-and-plugins). |
||||||
|
|
||||||
|
#### Highlight |
||||||
|
|
||||||
|
Consistent syntax highlighting. |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
highlight = { |
||||||
|
enable = true, |
||||||
|
custom_captures = { |
||||||
|
-- Highlight the @foo.bar capture group with the "Identifier" highlight group. |
||||||
|
["foo.bar"] = "Identifier", |
||||||
|
}, |
||||||
|
-- Setting this to true will run `:h syntax` and tree-sitter at the same time. |
||||||
|
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). |
||||||
|
-- Using this option may slow down your editor, and you may see some duplicate highlights. |
||||||
|
-- Instead of true it can also be a list of languages |
||||||
|
additional_vim_regex_highlighting = false, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
#### Incremental selection |
||||||
|
|
||||||
|
Incremental selection based on the named nodes from the grammar. |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
incremental_selection = { |
||||||
|
enable = true, |
||||||
|
keymaps = { |
||||||
|
init_selection = "gnn", |
||||||
|
node_incremental = "grn", |
||||||
|
scope_incremental = "grc", |
||||||
|
node_decremental = "grm", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
#### Indentation |
||||||
|
|
||||||
|
Indentation based on treesitter for the `=` operator. |
||||||
|
**NOTE: This is an experimental feature**. |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
indent = { |
||||||
|
enable = true |
||||||
|
} |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
#### Folding |
||||||
|
|
||||||
|
Tree-sitter based folding. *(Technically not a module because it's per windows and not per buffer.)* |
||||||
|
|
||||||
|
```vim |
||||||
|
set foldmethod=expr |
||||||
|
set foldexpr=nvim_treesitter#foldexpr() |
||||||
|
``` |
||||||
|
|
||||||
|
This will respect your `foldminlines` and `foldnestmax` settings. |
||||||
|
|
||||||
|
# Advanced setup |
||||||
|
|
||||||
|
## Adding parsers |
||||||
|
|
||||||
|
If you have a parser that is not on the list of supported languages (either as a repository on Github or in a local directory), you can add it manually for use by `nvim-treesitter` as follows: |
||||||
|
|
||||||
|
1. Clone the repository or [create a new project](https://tree-sitter.github.io/tree-sitter/creating-parsers#project-setup) in, say, `~/projects/tree-sitter-zimbu`. Make sure that the `tree-sitter-cli` executable is installed and in your path; see <https://tree-sitter.github.io/tree-sitter/creating-parsers#installation> for installation instructions. |
||||||
|
2. Run `tree-sitter generate` in this directory (followed by `tree-sitter test` for good measure). |
||||||
|
3. Add the following snippet to your `init.vim`: |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
local parser_config = require "nvim-treesitter.parsers".get_parser_configs() |
||||||
|
parser_config.zimbu = { |
||||||
|
install_info = { |
||||||
|
url = "~/projects/tree-sitter-zimbu", -- local path or git repo |
||||||
|
files = {"src/parser.c"}, |
||||||
|
-- optional entries: |
||||||
|
branch = "main", -- default branch in case of git repo if different from master |
||||||
|
generate_requires_npm = false, -- if stand-alone parser without npm dependencies |
||||||
|
requires_generate_from_grammar = false, -- if folder contains pre-generated src/parser.c |
||||||
|
}, |
||||||
|
filetype = "zu", -- if filetype does not agrees with parser name |
||||||
|
used_by = {"bar", "baz"} -- additional filetypes that use this parser |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
4. Start `nvim` and `:TSInstall zimbu`. |
||||||
|
|
||||||
|
You can also skip step 2 and use `:TSInstallFromGrammar zimbu` to install directly from a `grammar.js` in the top-level directory specified by `url`. |
||||||
|
Once the parser is installed, you can update it (from the latest revision of the `main` branch if `url` is a Github repository) with `:TSUpdate zimbu`. |
||||||
|
|
||||||
|
Note that neither `:TSInstall` nor `:TSInstallFromGrammar` copy query files from the grammar repository. |
||||||
|
If you want your installed grammar to be useful, you must manually [add query files](#adding-queries) to your local nvim-treesitter installation. |
||||||
|
Note also that module functionality is only triggered if your language's filetype is correctly identified. |
||||||
|
If Neovim does not detect your language's filetype by default, you can add a short Vimscript file to nvim-treesitter's `ftdetect` runtime directory. |
||||||
|
See [Neovim's documentation](https://neovim.io/doc/user/filetype.html#new-filetype) on how to use Vimscript to detect a filetype. |
||||||
|
|
||||||
|
If you use a git repository for your parser and want to use a specific version, you can set the `revision` key |
||||||
|
in the `install_info` table for you parser config. |
||||||
|
|
||||||
|
## Update parsers used_by |
||||||
|
|
||||||
|
Sometimes needs to use some parser for different filetype. |
||||||
|
|
||||||
|
Add the following snippet to your `init.vim`: |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
local parser_config = require "nvim-treesitter.parsers".get_parser_configs() |
||||||
|
parser_config.typescript.used_by = "javascriptflow" |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
## Adding queries |
||||||
|
|
||||||
|
Queries are what `nvim-treesitter` uses to extract information from the syntax tree; |
||||||
|
they are located in the `queries/{language}/*` runtime directories (see `:h rtp`), |
||||||
|
like the `queries` folder of this plugin, e.g. `queries/{language}/{locals,highlights,textobjects}.scm`. |
||||||
|
Other modules may require additional queries such as `folding.scm`. You can find a |
||||||
|
list of all supported capture names in [CONTRIBUTING.md](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations). |
||||||
|
|
||||||
|
All queries found in the runtime directories will be combined. |
||||||
|
By convention, if you want to write a query, use the `queries/` directory, |
||||||
|
but if you want to extend a query use the `after/queries/` directory. |
||||||
|
|
||||||
|
If you want to completely override a query, you can use `:h set_query()`. |
||||||
|
For example, to override the `injections` queries from `c` with your own: |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require("vim.treesitter.query").set_query("c", "injections", "(comment) @comment") |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
Note: when using `set_query`, all queries in the runtime directories will be ignored. |
||||||
|
|
||||||
|
## Adding modules |
||||||
|
|
||||||
|
If you wish you write your own module, you need to support |
||||||
|
|
||||||
|
- tree-sitter language detection support; |
||||||
|
- attaching and detaching to buffers; |
||||||
|
- all nvim-treesitter commands. |
||||||
|
|
||||||
|
At the top level, you can use the `define_modules` function to define one or more modules or module groups: |
||||||
|
|
||||||
|
```vim |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter'.define_modules { |
||||||
|
my_cool_plugin = { |
||||||
|
attach = function(bufnr, lang) |
||||||
|
-- Do cool stuff here |
||||||
|
end, |
||||||
|
detach = function(bufnr) |
||||||
|
-- Undo cool stuff here |
||||||
|
end, |
||||||
|
is_supported = function(lang) |
||||||
|
-- Check if the language is supported |
||||||
|
end |
||||||
|
} |
||||||
|
} |
||||||
|
EOF |
||||||
|
``` |
||||||
|
|
||||||
|
with the following properties: |
||||||
|
|
||||||
|
- `module_path` specifies a require path (string) that exports a module with an `attach` and `detach` function. This is not required if the functions are on this definition. |
||||||
|
- `enable` determines if the module is enabled by default. This is usually overridden by the user. |
||||||
|
- `disable` takes a list of languages that this module is disabled for. This is usually overridden by the user. |
||||||
|
- `is_supported` takes a function that takes a language and determines if this module supports that language. |
||||||
|
- `attach` takes a function that attaches to a buffer. This is required if `module_path` is not provided. |
||||||
|
- `detach` takes a function that detaches from a buffer. This is required if `module_path` is not provided. |
||||||
|
|
||||||
|
# Extra features |
||||||
|
|
||||||
|
### Statusline indicator |
||||||
|
|
||||||
|
```vim |
||||||
|
echo nvim_treesitter#statusline(90) " 90 can be any length |
||||||
|
module->expression_statement->call->identifier |
||||||
|
``` |
||||||
|
|
||||||
|
### Utilities |
||||||
|
|
||||||
|
You can get some utility functions with |
||||||
|
|
||||||
|
```lua |
||||||
|
local ts_utils = require 'nvim-treesitter.ts_utils' |
||||||
|
``` |
||||||
|
|
||||||
|
Check [`:h nvim-treesitter-utils`](doc/nvim-treesitter.txt) for more information. |
||||||
|
|
||||||
|
# Troubleshooting |
||||||
|
|
||||||
|
Before doing anything, make sure you have the latest version of this plugin and run `:checkhealth nvim_treesitter`. |
||||||
|
It can also help to update the parsers via `:TSUpdate`. |
||||||
|
|
||||||
|
#### Feature `X` does not work for `{language}`... |
||||||
|
|
||||||
|
First, check the `health#nvim_treesitter#check` and the `health#treesitter#check` sections of `:checkhealth` for any warning. |
||||||
|
If there is one, it's highly likely that this is the cause of the problem. |
||||||
|
|
||||||
|
Next check the `## Parser/Features` subsection of the `health#nvim_treesitter#check` section of `:checkhealth` to ensure the desired module is enabled for your language. |
||||||
|
If not, you might be missing query files; see [Adding queries](#adding-queries). |
||||||
|
|
||||||
|
Finally, ensure Neovim is correctly identifying your language's filetype using the `:echo &filetype` command while one of your language's files is open in Neovim. |
||||||
|
If not, add a short Vimscript file to nvim-treesitter's `ftdetect` runtime directory following [Neovim's documentation](https://neovim.io/doc/user/filetype.html#new-filetype) on filetype detection. |
||||||
|
You can also quickly & temporarily set the filetype for a single buffer with the `:set filetype=langname` command to test whether it fixes the problem. |
||||||
|
|
||||||
|
If everything is okay, then it might be an actual error. |
||||||
|
In that case, feel free to [open an issue here](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). |
||||||
|
|
||||||
|
#### I get `module 'vim.treesitter.query' not found` |
||||||
|
|
||||||
|
Make sure you have the latest version of Neovim. |
||||||
|
|
||||||
|
#### I get `Error detected while processing .../plugin/nvim-treesitter.vim` every time I open Neovim |
||||||
|
|
||||||
|
This is probably due to a change in a parser's grammar or its queries. |
||||||
|
Try updating the parser that you suspect has changed (`:TSUpdate {language}`) or all of them (`:TSUpdate`). |
||||||
|
If the error persists after updating all parsers, |
||||||
|
please [open an issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). |
||||||
|
|
||||||
|
#### I get `query error: invalid node type at position` |
||||||
|
|
||||||
|
This could be due a query file outside this plugin using outdated nodes, |
||||||
|
or due to an outdated parser. |
||||||
|
|
||||||
|
- Make sure you have the parsers up to date with `:TSUpdate` |
||||||
|
- Make sure you don't have more than one `parser` runtime directory. |
||||||
|
You can execute this command `:echo nvim_get_runtime_file('parser', v:true)` to find all runtime directories. |
||||||
|
If you get more than one path, remove the ones that are outside this plugin (`nvim-treesitter` directory), |
||||||
|
so the correct version of the parser is used. |
||||||
|
|
||||||
|
#### I experience weird highlighting issues similar to [#78](https://github.com/nvim-treesitter/nvim-treesitter/issues/78) |
||||||
|
|
||||||
|
This is a well known issue, which arises when the tree and the buffer have gotten out of sync. |
||||||
|
As this is an upstream issue, we don't have any definite fix. |
||||||
|
To get around this, you can force reparsing the buffer with |
||||||
|
|
||||||
|
```vim |
||||||
|
:write | edit | TSBufEnable highlight |
||||||
|
``` |
||||||
|
|
||||||
|
This will save, restore and enable highlighting for the current buffer. |
||||||
|
|
||||||
|
#### I experience bugs when using `nvim-treesitter`'s `foldexpr` similar to [#194](https://github.com/nvim-treesitter/nvim-treesitter/issues/194) |
||||||
|
|
||||||
|
This might happen, and is known to happen, with `vim-clap`. |
||||||
|
To avoid these kind of errors, please use `setlocal` instead of `set` for the respective filetypes. |
||||||
|
|
||||||
|
#### I run into errors like `module 'nvim-treesitter.configs' not found` at startup |
||||||
|
|
||||||
|
This is because of `rtp` management in `nvim`, adding `packadd |
||||||
|
nvim-treesitter` should fix the issue. |
||||||
|
|
||||||
|
#### I want to use Git instead of curl for downloading the parsers |
||||||
|
|
||||||
|
In your Lua config: |
||||||
|
|
||||||
|
```lua |
||||||
|
require("nvim-treesitter.install").prefer_git = true |
||||||
|
``` |
||||||
|
|
||||||
|
#### I want to use a HTTP proxy for downloading the parsers |
||||||
|
|
||||||
|
You can either configure curl to use additional CLI arguments in your Lua config: |
||||||
|
```lua |
||||||
|
require("nvim-treesitter.install").command_extra_args = { |
||||||
|
curl = { "--proxy", "<proxy url>" }, |
||||||
|
} |
||||||
|
``` |
||||||
|
or you can configure git via `.gitconfig` and use git instead of curl |
||||||
|
|
||||||
|
```lua |
||||||
|
require("nvim-treesitter.install").prefer_git = true |
||||||
|
``` |
@ -0,0 +1 @@ |
|||||||
|
setlocal commentstring=#\ %s |
@ -0,0 +1 @@ |
|||||||
|
setlocal commentstring=#\ %s |
@ -0,0 +1 @@ |
|||||||
|
setlocal commentstring=<%#\ %s\ %> |
@ -0,0 +1 @@ |
|||||||
|
setlocal commentstring=;\ %s |
@ -0,0 +1 @@ |
|||||||
|
setlocal commentstring={!--\ %s\ --} |
After Width: | Height: | Size: 854 KiB |
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,3 @@ |
|||||||
|
function! health#nvim_treesitter#check() |
||||||
|
lua require 'nvim-treesitter.health'.check() |
||||||
|
endfunction |
@ -0,0 +1,27 @@ |
|||||||
|
function! nvim_treesitter#statusline(...) abort |
||||||
|
return luaeval("require'nvim-treesitter'.statusline(_A)", get(a:, 1, {})) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#foldexpr() abort |
||||||
|
return luaeval(printf('require"nvim-treesitter.fold".get_fold_indic(%d)', v:lnum)) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#installable_parsers(arglead, cmdline, cursorpos) abort |
||||||
|
return join(luaeval("require'nvim-treesitter.parsers'.available_parsers()") + ['all', 'maintained'], "\n") |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort |
||||||
|
return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all', 'maintained'], "\n") |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort |
||||||
|
return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n") |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort |
||||||
|
return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n") |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! nvim_treesitter#indent() abort |
||||||
|
return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum)) |
||||||
|
endfunction |
@ -0,0 +1,749 @@ |
|||||||
|
*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim. |
||||||
|
|
||||||
|
Minimum version of neovim: nightly |
||||||
|
|
||||||
|
Authors: |
||||||
|
Kiyan Yazdani <yazdani.kiyan@protonmail.com> |
||||||
|
Thomas Vigouroux <tomvig38@gmail.com> |
||||||
|
Stephan Seitz <stephan.seitz@fau.de> |
||||||
|
Steven Sojka <Steven.Sojka@tdameritrade.com> |
||||||
|
Santos Gallegos <stsewd@protonmail.com> |
||||||
|
https://github.com/nvim-treesitter/nvim-treesitter/graphs/contributors |
||||||
|
|
||||||
|
Type |gO| to see the table of contents. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
INTRODUCTION *nvim-treesitter-intro* |
||||||
|
|
||||||
|
nvim-treesitter wraps the Neovim treesitter API to provide functionalities |
||||||
|
such as highlighting and incremental selection, and a command to easily |
||||||
|
install parsers. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
QUICK START *nvim-treesitter-quickstart* |
||||||
|
|
||||||
|
Install the parser for your language |
||||||
|
|
||||||
|
> |
||||||
|
:TSInstall {language} |
||||||
|
< |
||||||
|
|
||||||
|
To get a list of supported languages |
||||||
|
|
||||||
|
> |
||||||
|
:TSInstallInfo |
||||||
|
< |
||||||
|
|
||||||
|
By default, everything is disabled. |
||||||
|
To enable supported features, put this in your `init.vim` file: |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
-- One of "all", "maintained" (parsers with maintainers), or a list of languages |
||||||
|
ensure_installed = "maintained", |
||||||
|
|
||||||
|
-- Install languages synchronously (only applied to `ensure_installed`) |
||||||
|
sync_install = false, |
||||||
|
|
||||||
|
-- List of parsers to ignore installing |
||||||
|
ignore_install = { "javascript" }, |
||||||
|
|
||||||
|
highlight = { |
||||||
|
-- `false` will disable the whole extension |
||||||
|
enable = true, |
||||||
|
|
||||||
|
-- list of language that will be disabled |
||||||
|
disable = { "c", "rust" }, |
||||||
|
|
||||||
|
-- Setting this to true will run `:h syntax` and tree-sitter at the same time. |
||||||
|
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). |
||||||
|
-- Using this option may slow down your editor, and you may see some duplicate highlights. |
||||||
|
-- Instead of true it can also be a list of languages |
||||||
|
additional_vim_regex_highlighting = false, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
|
||||||
|
See |nvim-treesitter-modules| for a list of all available modules and its options. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
MODULES *nvim-treesitter-modules* |
||||||
|
|
||||||
|
|nvim-treesitter| provides several functionalities via modules (and submodules), |
||||||
|
each module makes use of the query files defined for each language, |
||||||
|
you can add your own queries too, see |nvim-treesitter-query-extensions|. |
||||||
|
|
||||||
|
All modules are disabled by default, and some provide default keymaps. |
||||||
|
Each module corresponds to an entry in the dictionary passed to the |
||||||
|
`nvim-treesitter.configs.setup` function, this should be in your `init.vim` file. |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
-- Modules and its options go here |
||||||
|
highlight = { enable = true }, |
||||||
|
incremental_selection = { enable = true }, |
||||||
|
textobjects = { enable = true }, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
|
||||||
|
All modules share some common options, like `enable` and `disable`. |
||||||
|
When `enable` is `true` this will enable the module for all supported languages, |
||||||
|
if you want to disable the module for some languages you can pass a list to the `disable` option. |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
highlight = { |
||||||
|
enable = true, |
||||||
|
disable = { "cpp", "lua" }, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
|
||||||
|
For more fine-grained control, `disable` can also take a function and |
||||||
|
whenever it returns `true`, the module is disabled for that buffer. |
||||||
|
The function is called once when a module starts in a buffer and receives the |
||||||
|
language and buffer number as arguments: |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
highlight = { |
||||||
|
enable = true, |
||||||
|
disable = function(lang, bufnr) -- Disable in large C++ buffers |
||||||
|
return lang == "cpp" and vim.api.nvim_buf_line_count(bufnr) > 50000 |
||||||
|
end, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
|
||||||
|
Options that define or accept a keymap use the same format you use to define |
||||||
|
keymaps in Neovim, so you can write keymaps as `gd`, `<space>a`, `<leader>a` |
||||||
|
`<C-a>` (control + a), `<A-n>` (alt + n), `<CR>` (enter), etc. |
||||||
|
|
||||||
|
External plugins can provide their own modules with their own options, |
||||||
|
those can also be configured using the `nvim-treesitter.configs.setup` |
||||||
|
function. |
||||||
|
|
||||||
|
------------------------------------------------------------------------------ |
||||||
|
HIGHLIGHT *nvim-treesitter-highlight-mod* |
||||||
|
|
||||||
|
Consistent syntax highlighting. |
||||||
|
|
||||||
|
Query files: `highlights.scm`. |
||||||
|
Supported options: |
||||||
|
|
||||||
|
- enable: `true` or `false`. |
||||||
|
- disable: list of languages. |
||||||
|
- custom_captures: A map of user defined capture groups to highlight groups. |
||||||
|
See |nvim-treesitter-query-extensions|. |
||||||
|
- additional_vim_regex_highlighting: `true` or `false`, or a list of languages. |
||||||
|
Set this to `true` if you depend on 'syntax' being enabled |
||||||
|
(like for indentation). Using this option may slow down your editor, |
||||||
|
and you may see some duplicate highlights. |
||||||
|
Defaults to `false`. |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
highlight = { |
||||||
|
enable = true, |
||||||
|
custom_captures = { |
||||||
|
-- Highlight the @foo.bar capture group with the "Identifier" highlight group. |
||||||
|
["foo.bar"] = "Identifier", |
||||||
|
}, |
||||||
|
-- Setting this to true or a list of languages will run `:h syntax` and tree-sitter at the same time. |
||||||
|
additional_vim_regex_highlighting = false, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
Note: The api is not stable yet. |
||||||
|
|
||||||
|
------------------------------------------------------------------------------ |
||||||
|
INCREMENTAL SELECTION *nvim-treesitter-incremental-selection-mod* |
||||||
|
|
||||||
|
Incremental selection based on the named nodes from the grammar. |
||||||
|
|
||||||
|
Query files: `locals.scm`. |
||||||
|
Supported options: |
||||||
|
- enable: `true` or `false`. |
||||||
|
- disable: list of languages. |
||||||
|
- keymaps: |
||||||
|
- init_selection: in normal mode, start incremental selection. |
||||||
|
Defaults to `gnn`. |
||||||
|
- node_incremental: in visual mode, increment to the upper named parent. |
||||||
|
Defaults to `grn`. |
||||||
|
- scope_incremental: in visual mode, increment to the upper scope |
||||||
|
(as defined in `locals.scm`). Defaults to `grc`. |
||||||
|
- node_decremental: in visual mode, decrement to the previous named node. |
||||||
|
Defaults to `grm`. |
||||||
|
|
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
incremental_selection = { |
||||||
|
enable = true, |
||||||
|
keymaps = { |
||||||
|
init_selection = "gnn", |
||||||
|
node_incremental = "grn", |
||||||
|
scope_incremental = "grc", |
||||||
|
node_decremental = "grm", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
|
||||||
|
------------------------------------------------------------------------------ |
||||||
|
INDENTATION *nvim-treesitter-indentation-mod* |
||||||
|
|
||||||
|
Indentation based on treesitter for the |=| operator. |
||||||
|
NOTE: this is an experimental feature. |
||||||
|
|
||||||
|
Query files: `indents.scm`. |
||||||
|
Supported options: |
||||||
|
- enable: `true` or `false`. |
||||||
|
- disable: list of languages. |
||||||
|
> |
||||||
|
lua <<EOF |
||||||
|
require'nvim-treesitter.configs'.setup { |
||||||
|
indent = { |
||||||
|
enable = true |
||||||
|
}, |
||||||
|
} |
||||||
|
EOF |
||||||
|
< |
||||||
|
============================================================================== |
||||||
|
COMMANDS *nvim-treesitter-commands* |
||||||
|
|
||||||
|
*:TSInstall* |
||||||
|
:TSInstall {language} ...~ |
||||||
|
|
||||||
|
Install one or more treesitter parsers. |
||||||
|
You can use |:TSInstall| `all` to install all parsers. Use |:TSInstall!| to |
||||||
|
force the reinstallation of already installed parsers. |
||||||
|
*:TSInstallSync* |
||||||
|
:TSInstallSync {language} ...~ |
||||||
|
|
||||||
|
Perform the |:TSInstall| operation synchronously. |
||||||
|
|
||||||
|
*:TSInstallInfo* |
||||||
|
:TSInstallInfo~ |
||||||
|
|
||||||
|
List information about currently installed parsers |
||||||
|
|
||||||
|
*:TSUpdate* |
||||||
|
:TSUpdate {language} ...~ |
||||||
|
|
||||||
|
Update the installed parser for one more {language} or all installed parsers |
||||||
|
if {language} is omitted. The specified parser is installed if it is not already |
||||||
|
installed. |
||||||
|
|
||||||
|
*:TSUpdateSync* |
||||||
|
:TSUpdateSync {language} ...~ |
||||||
|
|
||||||
|
Perform the |:TSUpdate| operation synchronously. |
||||||
|
|
||||||
|
*:TSUninstall* |
||||||
|
:TSUninstall {language} ...~ |
||||||
|
|
||||||
|
Deletes the parser for one or more {language}. You can use 'all' for language |
||||||
|
to uninstall all parsers. |
||||||
|
|
||||||
|
*:TSBufEnable* |
||||||
|
:TSBufEnable {module}~ |
||||||
|
|
||||||
|
Enable {module} on the current buffer. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
|
||||||
|
*:TSBufDisable* |
||||||
|
:TSBufDisable {module}~ |
||||||
|
|
||||||
|
Disable {module} on the current buffer. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
|
||||||
|
*:TSBufToggle* |
||||||
|
:TSBufToggle {module}~ |
||||||
|
|
||||||
|
Toggle (enable if disabled, disable if enabled) {module} on the current |
||||||
|
buffer. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
|
||||||
|
*:TSEnableAll* |
||||||
|
:TSEnableAll {module} [{language}]~ |
||||||
|
|
||||||
|
Enable {module} for the session. |
||||||
|
If {language} is specified, enable module for the session only for this |
||||||
|
particular language. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
A list of languages can be found at |:TSInstallInfo| |
||||||
|
|
||||||
|
*:TSDisableAll* |
||||||
|
:TSDisableAll {module} [{language}]~ |
||||||
|
|
||||||
|
Disable {module} for the session. |
||||||
|
If {language} is specified, disable module for the session only for this |
||||||
|
particular language. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
A list of languages can be found at |:TSInstallInfo| |
||||||
|
|
||||||
|
*:TSToggleAll* |
||||||
|
:TSToggleAll {module} [{language}]~ |
||||||
|
|
||||||
|
Toggle (enable if disabled, disable if enabled) {module} for the session. |
||||||
|
If {language} is specified, toggle module for the session only for this |
||||||
|
particular language. |
||||||
|
A list of modules can be found at |:TSModuleInfo| |
||||||
|
A list of languages can be found at |:TSInstallInfo| |
||||||
|
|
||||||
|
*:TSModuleInfo* |
||||||
|
:TSModuleInfo [{module}]~ |
||||||
|
|
||||||
|
List the state for the given module or all modules for the current session in |
||||||
|
a new buffer. |
||||||
|
|
||||||
|
These highlight groups are used by default: |
||||||
|
> |
||||||
|
highlight default TSModuleInfoGood guifg=LightGreen gui=bold |
||||||
|
highlight default TSModuleInfoBad guifg=Crimson |
||||||
|
highlight default link TSModuleInfoHeader Type |
||||||
|
highlight default link TSModuleInfoNamespace Statement |
||||||
|
highlight default link TSModuleInfoParser Identifier |
||||||
|
< |
||||||
|
|
||||||
|
*:TSEditQuery* |
||||||
|
:TSEditQuery {query-group} [{lang}]~ |
||||||
|
|
||||||
|
Edit the query file for a {query-group} (e.g. highlights, locals) for given |
||||||
|
{lang}. If there are multiple the user is prompted to select one of them. |
||||||
|
If no such file exists, a buffer for a new file in the user's config directory |
||||||
|
is created. If {lang} is not specified, the language of the current buffer |
||||||
|
is used. |
||||||
|
|
||||||
|
*:TSEditQueryUserAfter* |
||||||
|
:TSEditQueryUserAfter {query-group} [{lang}]~ |
||||||
|
|
||||||
|
Same as |:TSEditQuery| but edits a file in the `after` directory of the |
||||||
|
user's config directory. Useful to add custom extensions for the queries |
||||||
|
provided by a plugin. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
UTILS *nvim-treesitter-utils* |
||||||
|
|
||||||
|
Nvim treesitter has some wrapper functions that you can retrieve with: |
||||||
|
> |
||||||
|
local ts_utils = require 'nvim-treesitter.ts_utils' |
||||||
|
< |
||||||
|
Methods |
||||||
|
*ts_utils.get_node_at_cursor* |
||||||
|
get_node_at_cursor(winnr)~ |
||||||
|
|
||||||
|
`winnr` will be 0 if nil. |
||||||
|
Returns the node under the cursor. |
||||||
|
|
||||||
|
*ts_utils.get_node_text* |
||||||
|
get_node_text(node, bufnr)~ |
||||||
|
|
||||||
|
Returns the text content of a `node`. |
||||||
|
|
||||||
|
*ts_utils.is_parent* |
||||||
|
is_parent(dest, source)~ |
||||||
|
|
||||||
|
Determines whether `dest` is a parent of `source`. |
||||||
|
Returns a boolean. |
||||||
|
|
||||||
|
*ts_utils.get_named_children* |
||||||
|
get_named_children(node)~ |
||||||
|
|
||||||
|
Returns a table of named children of `node`. |
||||||
|
|
||||||
|
*ts_utils.get_next_node* |
||||||
|
get_next_node(node, allow_switch_parent, allow_next_parent)~ |
||||||
|
|
||||||
|
Returns the next node within the same parent. |
||||||
|
If no node is found, returns `nil`. |
||||||
|
If `allow_switch_parent` is true, it will allow switching parent |
||||||
|
when the node is the last node. |
||||||
|
If `allow_next_parent` is true, it will allow next parent if |
||||||
|
the node is the last node and the next parent doesn't have children. |
||||||
|
|
||||||
|
*ts_utils.get_previous_node* |
||||||
|
get_previous_node(node, allow_switch_parents, allow_prev_parent)~ |
||||||
|
|
||||||
|
Returns the previous node within the same parent. |
||||||
|
`allow_switch_parent` and `allow_prev_parent` follow the same rule |
||||||
|
as |ts_utils.get_next_node| but if the node is the first node. |
||||||
|
|
||||||
|
*ts_utils.goto_node* |
||||||
|
goto_node(node, goto_end, avoid_set_jump)~ |
||||||
|
|
||||||
|
Sets cursor to the position of `node` in the current windows. |
||||||
|
If `goto_end` is truthy, the cursor is set to the end the node range. |
||||||
|
Setting `avoid_set_jump` to `true`, avoids setting the current cursor position |
||||||
|
to the jump list. |
||||||
|
|
||||||
|
*ts_utils.swap_nodes* |
||||||
|
swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)~ |
||||||
|
|
||||||
|
Swaps the nodes or ranges. |
||||||
|
set `cursor_to_second` to true to move the cursor to the second node |
||||||
|
|
||||||
|
*ts_utils.memoize_by_buf_tick* |
||||||
|
memoize_by_buf_tick(fn, options)~ |
||||||
|
|
||||||
|
Caches the return value for a function and returns the cache value if the tick |
||||||
|
of the buffer has not changed from the previous. |
||||||
|
|
||||||
|
`fn`: a function that takes any arguments |
||||||
|
and returns a value to store. |
||||||
|
`options?`: <table> |
||||||
|
- `bufnr`: a function/value that extracts the bufnr from the given arguments. |
||||||
|
- `key`: a function/value that extracts the cache key from the given arguments. |
||||||
|
`returns`: a function to call with bufnr as argument to |
||||||
|
retrieve the value from the cache |
||||||
|
|
||||||
|
*ts_utils.node_to_lsp_range* |
||||||
|
node_to_lsp_range(node)~ |
||||||
|
|
||||||
|
Get an lsp formatted range from a node range |
||||||
|
|
||||||
|
*ts_utils.get_node_range* |
||||||
|
get_node_range(node_or_range)~ |
||||||
|
|
||||||
|
Get the range from either a node or a range |
||||||
|
|
||||||
|
*ts_utils.node_length* |
||||||
|
node_length(node)~ |
||||||
|
|
||||||
|
Get the byte length of node range |
||||||
|
|
||||||
|
*ts_utils.update_selection* |
||||||
|
update_selection(buf, node)~ |
||||||
|
|
||||||
|
Set the selection to the node range |
||||||
|
|
||||||
|
*ts_utils.highlight_range* |
||||||
|
highlight_range(range, buf, hl_namespace, hl_group)~ |
||||||
|
|
||||||
|
Set a highlight that spans the given range |
||||||
|
|
||||||
|
*ts_utils.highlight_node* |
||||||
|
highlight_node(node, buf, hl_namespace, hl_group)~ |
||||||
|
|
||||||
|
Set a highlight that spans the given node's range |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
FUNCTIONS *nvim-treesitter-functions* |
||||||
|
|
||||||
|
*nvim_treesitter#statusline()* |
||||||
|
nvim_treesitter#statusline(opts)~ |
||||||
|
|
||||||
|
Returns a string describing the current position in the file. This |
||||||
|
could be used as a statusline indicator. |
||||||
|
Default options (lua syntax): |
||||||
|
> |
||||||
|
{ |
||||||
|
indicator_size = 100, |
||||||
|
type_patterns = {'class', 'function', 'method'}, |
||||||
|
transform_fn = function(line) return line:gsub('%s*[%[%(%{]*%s*$', '') end, |
||||||
|
separator = ' -> ' |
||||||
|
} |
||||||
|
< |
||||||
|
- `indicator_size` - How long should the string be. If longer, it is cut from |
||||||
|
the beginning. |
||||||
|
- `type_patterns` - Which node type patterns to match. |
||||||
|
- `transform_fn` - Function used to transform the single item in line. By |
||||||
|
default removes opening brackets and spaces from end. |
||||||
|
- `separator` - Separator between nodes. |
||||||
|
|
||||||
|
*nvim_treesitter#foldexpr()* |
||||||
|
nvim_treesitter#foldexpr()~ |
||||||
|
|
||||||
|
Functions to be used to determine the fold level at a given line number. |
||||||
|
To use it: > |
||||||
|
set foldmethod=expr |
||||||
|
set foldexpr=nvim_treesitter#foldexpr() |
||||||
|
< |
||||||
|
|
||||||
|
This will respect your 'foldminlines' and 'foldnestmax' settings. |
||||||
|
|
||||||
|
Note: This is highly experimental, and folding can break on some types of |
||||||
|
edits. If you encounter such breakage, hiting `zx` should fix folding. |
||||||
|
In any case, feel free to open an issue with the reproducing steps. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
HIGHLIGHTS *nvim-treesitter-highlights* |
||||||
|
|
||||||
|
The following is a list of highlights groups, the syntactic elements they |
||||||
|
apply to, and some examples. |
||||||
|
|
||||||
|
*hl-TSAttribute* |
||||||
|
`TSAttribute` |
||||||
|
Annotations that can be attached to the code to denote some kind of meta |
||||||
|
information. e.g. C++/Dart attributes. |
||||||
|
|
||||||
|
*hl-TSBoolean* |
||||||
|
`TSBoolean` |
||||||
|
Boolean literals: `True` and `False` in Python. |
||||||
|
|
||||||
|
*hl-TSCharacter* |
||||||
|
`TSCharacter` |
||||||
|
Character literals: `'a'` in C. |
||||||
|
|
||||||
|
*hl-TSComment* |
||||||
|
`TSComment` |
||||||
|
Line comments and block comments. |
||||||
|
|
||||||
|
*hl-TSConditional* |
||||||
|
`TSConditional` |
||||||
|
Keywords related to conditionals: `if`, `when`, `cond`, etc. |
||||||
|
|
||||||
|
*hl-TSConstant* |
||||||
|
`TSConstant` |
||||||
|
Constants identifiers. These might not be semantically constant. |
||||||
|
E.g. uppercase variables in Python. |
||||||
|
|
||||||
|
*hl-TSConstBuiltin* |
||||||
|
`TSConstBuiltin` |
||||||
|
Built-in constant values: `nil` in Lua. |
||||||
|
|
||||||
|
*hl-TSConstMacro* |
||||||
|
`TSConstMacro` |
||||||
|
Constants defined by macros: `NULL` in C. |
||||||
|
|
||||||
|
*hl-TSConstructor* |
||||||
|
`TSConstructor` |
||||||
|
Constructor calls and definitions: `{}` in Lua, and Java constructors. |
||||||
|
|
||||||
|
*hl-TSError* |
||||||
|
`TSError` |
||||||
|
Syntax/parser errors. This might highlight large sections of code while the |
||||||
|
user is typing still incomplete code, use a sensible highlight. |
||||||
|
|
||||||
|
*hl-TSException* |
||||||
|
`TSException` |
||||||
|
Exception related keywords: `try`, `except`, `finally` in Python. |
||||||
|
|
||||||
|
*hl-TSField* |
||||||
|
`TSField` |
||||||
|
Object and struct fields. |
||||||
|
|
||||||
|
*hl-TSFloat* |
||||||
|
`TSFloat` |
||||||
|
Floating-point number literals. |
||||||
|
|
||||||
|
*hl-TSFunction* |
||||||
|
`TSFunction` |
||||||
|
Function calls and definitions. |
||||||
|
|
||||||
|
*hl-TSFuncBuiltin* |
||||||
|
`TSFuncBuiltin` |
||||||
|
Built-in functions: `print` in Lua. |
||||||
|
|
||||||
|
*hl-TSFuncMacro* |
||||||
|
`TSFuncMacro` |
||||||
|
Macro defined functions (calls and definitions): each `macro_rules` in |
||||||
|
Rust. |
||||||
|
|
||||||
|
*hl-TSInclude* |
||||||
|
`TSInclude` |
||||||
|
File or module inclusion keywords: `#include` in C, `use` or `extern crate` in |
||||||
|
Rust. |
||||||
|
|
||||||
|
*hl-TSKeyword* |
||||||
|
`TSKeyword` |
||||||
|
Keywords that don't fit into other categories. |
||||||
|
|
||||||
|
*hl-TSKeywordFunction* |
||||||
|
`TSKeywordFunction` |
||||||
|
Keywords used to define a function: `function` in Lua, `def` and `lambda` in |
||||||
|
Python. |
||||||
|
|
||||||
|
*hl-TSKeywordOperator* |
||||||
|
`TSKeywordOperator` |
||||||
|
Unary and binary operators that are English words: `and`, `or` in Python; |
||||||
|
`sizeof` in C. |
||||||
|
|
||||||
|
*hl-TSKeywordReturn* |
||||||
|
`TSKeywordReturn` |
||||||
|
Keywords like `return` and `yield`. |
||||||
|
|
||||||
|
*hl-TSLabel* |
||||||
|
`TSLabel` |
||||||
|
GOTO labels: `label:` in C, and `::label::` in Lua. |
||||||
|
|
||||||
|
*hl-TSMethod* |
||||||
|
`TSMethod` |
||||||
|
Method calls and definitions. |
||||||
|
|
||||||
|
*hl-TSNamespace* |
||||||
|
`TSNamespace` |
||||||
|
Identifiers referring to modules and namespaces. |
||||||
|
|
||||||
|
*hl-None* |
||||||
|
`TSNone` |
||||||
|
No highlighting (sets all highlight arguments to `NONE`). this group is used |
||||||
|
to clear certain ranges, for example, string interpolations. Don't change the |
||||||
|
values of this highlight group. |
||||||
|
|
||||||
|
*hl-TSNumber* |
||||||
|
`TSNumber` |
||||||
|
Numeric literals that don't fit into other categories. |
||||||
|
|
||||||
|
*hl-TSOperator* |
||||||
|
`TSOperator` |
||||||
|
Binary or unary operators: `+`, and also `->` and `*` in C. |
||||||
|
|
||||||
|
*hl-TSParameter* |
||||||
|
`TSParameter` |
||||||
|
Parameters of a function. |
||||||
|
|
||||||
|
*hl-TSParameterReference* |
||||||
|
`TSParameterReference` |
||||||
|
References to parameters of a function. |
||||||
|
|
||||||
|
*hl-TSProperty* |
||||||
|
`TSProperty` |
||||||
|
Same as `TSField`. |
||||||
|
|
||||||
|
*hl-TSPunctDelimiter* |
||||||
|
`TSPunctDelimiter` |
||||||
|
Punctuation delimiters: Periods, commas, semicolons, etc. |
||||||
|
|
||||||
|
*hl-TSPunctBracket* |
||||||
|
`TSPunctBracket` |
||||||
|
Brackets, braces, parentheses, etc. |
||||||
|
|
||||||
|
*hl-TSPunctSpecial* |
||||||
|
`TSPunctSpecial` |
||||||
|
Special punctuation that doesn't fit into the previous categories. |
||||||
|
|
||||||
|
*hl-TSRepeat* |
||||||
|
`TSRepeat` |
||||||
|
Keywords related to loops: `for`, `while`, etc. |
||||||
|
|
||||||
|
*hl-TSString* |
||||||
|
`TSString` |
||||||
|
String literals. |
||||||
|
|
||||||
|
*hl-TSStringRegex* |
||||||
|
`TSStringRegex` |
||||||
|
Regular expression literals. |
||||||
|
|
||||||
|
*hl-TSStringEscape* |
||||||
|
`TSStringEscape` |
||||||
|
Escape characters within a string: `\n`, `\t`, etc. |
||||||
|
|
||||||
|
*hl-TSStringSpecial* |
||||||
|
`TSStringSpecial` |
||||||
|
Strings with special meaning that don't fit into the previous categories. |
||||||
|
|
||||||
|
*hl-TSSymbol* |
||||||
|
`TSSymbol` |
||||||
|
Identifiers referring to symbols or atoms. |
||||||
|
|
||||||
|
*hl-TSTag* |
||||||
|
`TSTag` |
||||||
|
Tags like HTML tag names. |
||||||
|
|
||||||
|
*hl-TSTagAttribute* |
||||||
|
`TSTagAttribute` |
||||||
|
HTML tag attributes. |
||||||
|
|
||||||
|
*hl-TSTagDelimiter* |
||||||
|
`TSTagDelimiter` |
||||||
|
Tag delimiters like `<` `>` `/`. |
||||||
|
|
||||||
|
*hl-TSText* |
||||||
|
`TSText` |
||||||
|
Non-structured text. Like text in a markup language. |
||||||
|
|
||||||
|
*hl-TSSTrong* |
||||||
|
`TSStrong` |
||||||
|
Text to be represented in bold. |
||||||
|
|
||||||
|
*hl-TSEmphasis* |
||||||
|
`TSEmphasis` |
||||||
|
Text to be represented with emphasis. |
||||||
|
|
||||||
|
*hl-TSUnderline* |
||||||
|
`TSUnderline` |
||||||
|
Text to be represented with an underline. |
||||||
|
|
||||||
|
*hl-TSStrike* |
||||||
|
`TSStrike` |
||||||
|
Strikethrough text. |
||||||
|
|
||||||
|
*hl-TSTitle* |
||||||
|
`TSTitle` |
||||||
|
Text that is part of a title. |
||||||
|
|
||||||
|
*hl-TSLiteral* |
||||||
|
`TSLiteral` |
||||||
|
Literal or verbatim text. |
||||||
|
|
||||||
|
*hl-TSURI* |
||||||
|
`TSURI` |
||||||
|
URIs like hyperlinks or email addresses. |
||||||
|
|
||||||
|
*hl-TSMath* |
||||||
|
`TSMath` |
||||||
|
Math environments like LaTeX's `$ ... $` |
||||||
|
|
||||||
|
*hl-TSTextReference* |
||||||
|
`TSTextReference` |
||||||
|
Footnotes, text references, citations, etc. |
||||||
|
|
||||||
|
*hl-TSEnvironment* |
||||||
|
`TSEnvironment` |
||||||
|
Text environments of markup languages. |
||||||
|
|
||||||
|
*hl-TSEnvironmentName* |
||||||
|
`TSEnvironmentName` |
||||||
|
Text/string indicating the type of text environment. Like the name of a |
||||||
|
`\begin` block in LaTeX. |
||||||
|
|
||||||
|
*hl-TSNote* |
||||||
|
`TSNote` |
||||||
|
Text representation of an informational note. |
||||||
|
|
||||||
|
*TSWarning* |
||||||
|
`TSWarning` |
||||||
|
Text representation of a warning note. |
||||||
|
|
||||||
|
*TSDanger* |
||||||
|
`TSDanger` |
||||||
|
Text representation of a danger note. |
||||||
|
|
||||||
|
*hl-TSType* |
||||||
|
`TSType` |
||||||
|
Type (and class) definitions and annotations. |
||||||
|
|
||||||
|
*hl-TSTypeBuiltin* |
||||||
|
`TSTypeBuiltin` |
||||||
|
Built-in types: `i32` in Rust. |
||||||
|
|
||||||
|
*hl-TSVariable* |
||||||
|
`TSVariable` |
||||||
|
Variable names that don't fit into other categories. |
||||||
|
|
||||||
|
*hl-TSVariableBuiltin* |
||||||
|
`TSVariableBuiltin` |
||||||
|
Variable names defined by the language: `this` or `self` in Javascript. |
||||||
|
|
||||||
|
============================================================================== |
||||||
|
PERFORMANCE *nvim-treesitter-performance* |
||||||
|
|
||||||
|
`nvim-treesitter` checks the 'runtimepath' on startup in order to discover |
||||||
|
available parsers and queries and index them. As a consequence, a very long |
||||||
|
'runtimepath' might result in delayed startup times. |
||||||
|
|
||||||
|
|
||||||
|
vim:tw=78:ts=8:expandtab:noet:ft=help:norl: |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.fusion setfiletype fusion |
@ -0,0 +1,2 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.tscn setlocal ft=gdresource |
||||||
|
autocmd BufRead,BufNewFile *.tres setlocal ft=gdresource |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.gd set ft=gdscript |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.hbs set ft=handlebars |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.glsl set filetype=glsl |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile go.work set filetype=gowork |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.graphql,*.graphqls,*.gql setfiletype graphql |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.hack,*.hackpartial setfiletype hack |
@ -0,0 +1,2 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.hcl set filetype=hcl |
||||||
|
autocmd BufRead,BufNewFile *.tf,*.tfvars set filetype=terraform |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile *.heex set filetype=heex |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.hjson set filetype=hjson |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufNewFile,BufRead *.json5 set ft=json5 |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.ldg,*.ledger,*.journal setfiletype ledger |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.nix setfiletype nix |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.prisma set filetype=prisma |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile *.pug setlocal filetype=pug |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.ql,*.qll setfiletype ql |
@ -0,0 +1,11 @@ |
|||||||
|
" Last Change: 2020 Sep 01 |
||||||
|
|
||||||
|
function! s:shouldFt(path) |
||||||
|
let l:q_dir = fnamemodify(a:path, ":p:h:h:t") |
||||||
|
|
||||||
|
if l:q_dir =~? "queries" |
||||||
|
setlocal ft=query |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
autocmd BufNewFile,BufRead *.scm call s:shouldFt(expand("%")) |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile *.sface set filetype=surface |
@ -0,0 +1 @@ |
|||||||
|
autocmd BufRead,BufNewFile *.tl setfiletype teal |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile *.tla set filetype=tla |
@ -0,0 +1 @@ |
|||||||
|
au BufRead,BufNewFile *.yang set filetype=yang |
@ -0,0 +1,293 @@ |
|||||||
|
{ |
||||||
|
"bash": { |
||||||
|
"revision": "275effdfc0edce774acf7d481f9ea195c6c403cd" |
||||||
|
}, |
||||||
|
"beancount": { |
||||||
|
"revision": "78b8ddca3ab774573a4e3bf64eabd79e9452cea9" |
||||||
|
}, |
||||||
|
"bibtex": { |
||||||
|
"revision": "ccfd77db0ed799b6c22c214fe9d2937f47bc8b34" |
||||||
|
}, |
||||||
|
"c": { |
||||||
|
"revision": "e348e8ec5efd3aac020020e4af53d2ff18f393a9" |
||||||
|
}, |
||||||
|
"c_sharp": { |
||||||
|
"revision": "352a4630c81a7a5cbd3bc67327743bd8d38f2dd2" |
||||||
|
}, |
||||||
|
"clojure": { |
||||||
|
"revision": "39bf0977d223879436c1425fe6bfeb3bcfd86f92" |
||||||
|
}, |
||||||
|
"cmake": { |
||||||
|
"revision": "f6616f1e417ee8b62daf251aa1daa5d73781c596" |
||||||
|
}, |
||||||
|
"comment": { |
||||||
|
"revision": "6975eb268f42df2afc313f96c0693e284685dba7" |
||||||
|
}, |
||||||
|
"commonlisp": { |
||||||
|
"revision": "4fd115d3bb7046cd094f21bfe5766c302dbf64cd" |
||||||
|
}, |
||||||
|
"cpp": { |
||||||
|
"revision": "656d7ea44b2b0daece78791e30281e283f30001e" |
||||||
|
}, |
||||||
|
"css": { |
||||||
|
"revision": "a03f1d2d1dfbf6f8e0fdca5f9ff030228241eb57" |
||||||
|
}, |
||||||
|
"cuda": { |
||||||
|
"revision": "14cd86e18ba45e327017de5b3e0f8d8f7f8e98ec" |
||||||
|
}, |
||||||
|
"d": { |
||||||
|
"revision": "c2fbf21bd3aa45495fe13247e040ad5815250032" |
||||||
|
}, |
||||||
|
"dart": { |
||||||
|
"revision": "6a25376685d1d47968c2cef06d4db8d84a70025e" |
||||||
|
}, |
||||||
|
"devicetree": { |
||||||
|
"revision": "fa70098cd70393f84785f85cdc6a45299b59cd5b" |
||||||
|
}, |
||||||
|
"dockerfile": { |
||||||
|
"revision": "28ac8596bab00b2dac9a76deaa9c4cb2b22642fd" |
||||||
|
}, |
||||||
|
"dot": { |
||||||
|
"revision": "92877bac7033e409ccfb3f07fe28ef1dfd359457" |
||||||
|
}, |
||||||
|
"eex": { |
||||||
|
"revision": "f742f2fe327463335e8671a87c0b9b396905d1d1" |
||||||
|
}, |
||||||
|
"elixir": { |
||||||
|
"revision": "de20391afe5cb03ef1e8a8e43167e7b58cc52869" |
||||||
|
}, |
||||||
|
"elm": { |
||||||
|
"revision": "bd50ccf66b42c55252ac8efc1086af4ac6bab8cd" |
||||||
|
}, |
||||||
|
"erlang": { |
||||||
|
"revision": "9d5fd0c329280a156bf7614a49dc5e8c58cc037c" |
||||||
|
}, |
||||||
|
"fennel": { |
||||||
|
"revision": "fce4331731a960077ff5f98939bc675179f1908a" |
||||||
|
}, |
||||||
|
"fish": { |
||||||
|
"revision": "04e54ab6585dfd4fee6ddfe5849af56f101b6d4f" |
||||||
|
}, |
||||||
|
"foam": { |
||||||
|
"revision": "fdb7f14b885abfc4df57728c9b2a2f2ad24d3cb7" |
||||||
|
}, |
||||||
|
"fortran": { |
||||||
|
"revision": "f0f2f100952a353e64e26b0fa710b4c296d7af13" |
||||||
|
}, |
||||||
|
"fusion": { |
||||||
|
"revision": "19db2f47ba4c3a0f6238d4ae0e2abfca16e61dd6" |
||||||
|
}, |
||||||
|
"gdscript": { |
||||||
|
"revision": "2a6abdaa47fcb91397e09a97c7433fd995ea46c6" |
||||||
|
}, |
||||||
|
"glimmer": { |
||||||
|
"revision": "2644d7db571fe36204fdfcf8eed7bfa97f32c25a" |
||||||
|
}, |
||||||
|
"glsl": { |
||||||
|
"revision": "ffb93961426926554a0ba4a389ea6e9d6fafdea9" |
||||||
|
}, |
||||||
|
"go": { |
||||||
|
"revision": "0fa917a7022d1cd2e9b779a6a8fc5dc7fad69c75" |
||||||
|
}, |
||||||
|
"godot_resource": { |
||||||
|
"revision": "b6ef0768711086a86b3297056f9ffb5cc1d77b4a" |
||||||
|
}, |
||||||
|
"gomod": { |
||||||
|
"revision": "3cbcb572109ea0bc476a292208722c326c9e6c3a" |
||||||
|
}, |
||||||
|
"gowork": { |
||||||
|
"revision": "6dd9dd79fb51e9f2abc829d5e97b15015b6a8ae2" |
||||||
|
}, |
||||||
|
"graphql": { |
||||||
|
"revision": "5e66e961eee421786bdda8495ed1db045e06b5fe" |
||||||
|
}, |
||||||
|
"hack": { |
||||||
|
"revision": "4770eb21a36307c156cfd2555ddd8e10c304fdc3" |
||||||
|
}, |
||||||
|
"haskell": { |
||||||
|
"revision": "d6ccd2d9c40bdec29fee0027ef04fe5ff1ae4ceb" |
||||||
|
}, |
||||||
|
"hcl": { |
||||||
|
"revision": "3cb7fc28247efbcb2973b97e71c78838ad98a583" |
||||||
|
}, |
||||||
|
"heex": { |
||||||
|
"revision": "d8b5b9f016cd3c7b0ee916cf031d9a2188c0fc44" |
||||||
|
}, |
||||||
|
"hjson": { |
||||||
|
"revision": "02fa3b79b3ff9a296066da6277adfc3f26cbc9e0" |
||||||
|
}, |
||||||
|
"hocon": { |
||||||
|
"revision": "5b4688cc57c773e69fe7dfc3f6b83c054557f4f1" |
||||||
|
}, |
||||||
|
"html": { |
||||||
|
"revision": "161a92474a7bb2e9e830e48e76426f38299d99d1" |
||||||
|
}, |
||||||
|
"http": { |
||||||
|
"revision": "bfddd16b1cf78e0042fd1f6846a179f76a254e20" |
||||||
|
}, |
||||||
|
"java": { |
||||||
|
"revision": "a24ae7d16de3517bff243a87d087d0b4877a65c5" |
||||||
|
}, |
||||||
|
"javascript": { |
||||||
|
"revision": "fdeb68ac8d2bd5a78b943528bb68ceda3aade2eb" |
||||||
|
}, |
||||||
|
"jsdoc": { |
||||||
|
"revision": "189a6a4829beb9cdbe837260653b4a3dfb0cc3db" |
||||||
|
}, |
||||||
|
"json": { |
||||||
|
"revision": "203e239408d642be83edde8988d6e7b20a19f0e8" |
||||||
|
}, |
||||||
|
"json5": { |
||||||
|
"revision": "5dd5cdc418d9659682556b6adca2dd9ace0ac6d2" |
||||||
|
}, |
||||||
|
"jsonc": { |
||||||
|
"revision": "02b01653c8a1c198ae7287d566efa86a135b30d5" |
||||||
|
}, |
||||||
|
"julia": { |
||||||
|
"revision": "12ea597262125fc22fd2e91aa953ac69b19c26ca" |
||||||
|
}, |
||||||
|
"kotlin": { |
||||||
|
"revision": "a4f71eb9b8c9b19ded3e0e9470be4b1b77c2b569" |
||||||
|
}, |
||||||
|
"latex": { |
||||||
|
"revision": "6f796b700c69a8af28132e84ed6d0c8f0c17a5e2" |
||||||
|
}, |
||||||
|
"ledger": { |
||||||
|
"revision": "0cdeb0e51411a3ba5493662952c3039de08939ca" |
||||||
|
}, |
||||||
|
"llvm": { |
||||||
|
"revision": "3b213925b9c4f42c1acfe2e10bfbb438d9c6834d" |
||||||
|
}, |
||||||
|
"lua": { |
||||||
|
"revision": "2e372ad0af8c6a68ba39f107a2edc9e3fc19ecf1" |
||||||
|
}, |
||||||
|
"make": { |
||||||
|
"revision": "a4b9187417d6be349ee5fd4b6e77b4172c6827dd" |
||||||
|
}, |
||||||
|
"markdown": { |
||||||
|
"revision": "8bee14c30ecadd55c2d65633973b4e81f93525e0" |
||||||
|
}, |
||||||
|
"ninja": { |
||||||
|
"revision": "0a95cfdc0745b6ae82f60d3a339b37f19b7b9267" |
||||||
|
}, |
||||||
|
"nix": { |
||||||
|
"revision": "6d6aaa50793b8265b6a8b6628577a0083d3b923d" |
||||||
|
}, |
||||||
|
"norg": { |
||||||
|
"revision": "c4be6addec0a8ada234684ced6c928189fd399af" |
||||||
|
}, |
||||||
|
"ocaml": { |
||||||
|
"revision": "23d419ba45789c5a47d31448061557716b02750a" |
||||||
|
}, |
||||||
|
"ocaml_interface": { |
||||||
|
"revision": "23d419ba45789c5a47d31448061557716b02750a" |
||||||
|
}, |
||||||
|
"ocamllex": { |
||||||
|
"revision": "ac1d5957e719d49bd6acd27439b79843e4daf8ed" |
||||||
|
}, |
||||||
|
"pascal": { |
||||||
|
"revision": "2fd40f477d3e2794af152618ccfac8d92eb72a66" |
||||||
|
}, |
||||||
|
"perl": { |
||||||
|
"revision": "ab2b39439f2fc82fd5ea0b7e08509760d4cbacd5" |
||||||
|
}, |
||||||
|
"php": { |
||||||
|
"revision": "57f855461aeeca73bd4218754fb26b5ac143f98f" |
||||||
|
}, |
||||||
|
"phpdoc": { |
||||||
|
"revision": "52c0fbf581d0fc2d29696dbbd4fca99a73082210" |
||||||
|
}, |
||||||
|
"pioasm": { |
||||||
|
"revision": "924aadaf5dea2a6074d72027b064f939acf32e20" |
||||||
|
}, |
||||||
|
"prisma": { |
||||||
|
"revision": "74a721e8eed1a4a25cf495d45974ba24f315f81a" |
||||||
|
}, |
||||||
|
"pug": { |
||||||
|
"revision": "5875f9a7d94836708119b0a1102bb5792e8bf673" |
||||||
|
}, |
||||||
|
"python": { |
||||||
|
"revision": "24b530ca158d2782ea9046e756057a412e16b52f" |
||||||
|
}, |
||||||
|
"ql": { |
||||||
|
"revision": "8e7fd7e638d4a0ec7a792ee16b19dbc6407aa810" |
||||||
|
}, |
||||||
|
"query": { |
||||||
|
"revision": "5217c6805c09f8fc00ed13d17d5fcb791437aee6" |
||||||
|
}, |
||||||
|
"r": { |
||||||
|
"revision": "d9868735e401e4870a3d4422790b585fea3faec8" |
||||||
|
}, |
||||||
|
"rasi": { |
||||||
|
"revision": "e2961f02244c068a67549adf896b0779e4a29516" |
||||||
|
}, |
||||||
|
"regex": { |
||||||
|
"revision": "e1cfca3c79896ff79842f057ea13e529b66af636" |
||||||
|
}, |
||||||
|
"rst": { |
||||||
|
"revision": "b74770c0166f28c1a0ab293513a78712ca1c338b" |
||||||
|
}, |
||||||
|
"ruby": { |
||||||
|
"revision": "888e2e563ed3b43c417f17e57f7e29c39ce9aeea" |
||||||
|
}, |
||||||
|
"rust": { |
||||||
|
"revision": "eeb0702ebdac504b97196577b1dac43c80913d7b" |
||||||
|
}, |
||||||
|
"scala": { |
||||||
|
"revision": "0a3dd53a7fc4b352a538397d054380aaa28be54c" |
||||||
|
}, |
||||||
|
"scss": { |
||||||
|
"revision": "f3174d3d131eb776f86dfa3d90fe6f7325c0ad9a" |
||||||
|
}, |
||||||
|
"sparql": { |
||||||
|
"revision": "05f949d3c1c15e3261473a244d3ce87777374dec" |
||||||
|
}, |
||||||
|
"supercollider": { |
||||||
|
"revision": "a7201b61779be59ac0fc0d118746c886dbc3edbd" |
||||||
|
}, |
||||||
|
"surface": { |
||||||
|
"revision": "f4586b35ac8548667a9aaa4eae44456c1f43d032" |
||||||
|
}, |
||||||
|
"svelte": { |
||||||
|
"revision": "98274d94ec33e994e8354d9ddfdef58cca471294" |
||||||
|
}, |
||||||
|
"swift": { |
||||||
|
"revision": "9ee2d8c29600425b64e7b173b2224033a0f99d0c" |
||||||
|
}, |
||||||
|
"teal": { |
||||||
|
"revision": "fcc5f6f4d194dede4e676834ff28a506e39e17b4" |
||||||
|
}, |
||||||
|
"tlaplus": { |
||||||
|
"revision": "f99f369f4b907108ac35cc49a56b7c8602f8332d" |
||||||
|
}, |
||||||
|
"toml": { |
||||||
|
"revision": "8bd2056818b21860e3d756b5a58c4f6e05fb744e" |
||||||
|
}, |
||||||
|
"tsx": { |
||||||
|
"revision": "e8e8e8dc2745840b036421b4e43286750443cb13" |
||||||
|
}, |
||||||
|
"turtle": { |
||||||
|
"revision": "085437f5cb117703b7f520dd92161140a684f092" |
||||||
|
}, |
||||||
|
"typescript": { |
||||||
|
"revision": "e8e8e8dc2745840b036421b4e43286750443cb13" |
||||||
|
}, |
||||||
|
"verilog": { |
||||||
|
"revision": "8f6b1f357d1231c420404b5f7a368a73c25adfa2" |
||||||
|
}, |
||||||
|
"vim": { |
||||||
|
"revision": "bc573ef552adf8bed9e36eb687a0cccf43158634" |
||||||
|
}, |
||||||
|
"vue": { |
||||||
|
"revision": "91fe2754796cd8fba5f229505a23fa08f3546c06" |
||||||
|
}, |
||||||
|
"yaml": { |
||||||
|
"revision": "0e36bed171768908f331ff7dff9d956bae016efb" |
||||||
|
}, |
||||||
|
"yang": { |
||||||
|
"revision": "8e9d175982afcefa3dac8ca20d40d1643accd2bd" |
||||||
|
}, |
||||||
|
"zig": { |
||||||
|
"revision": "93331b8bd8b4ebee2b575490b2758f16ad4e9f30" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
if not pcall(require, "vim.treesitter.languagetree") then |
||||||
|
error "nvim-treesitter requires a more recent Neovim nightly version!" |
||||||
|
end |
||||||
|
|
||||||
|
local install = require "nvim-treesitter.install" |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
local ts_utils = require "nvim-treesitter.ts_utils" |
||||||
|
local info = require "nvim-treesitter.info" |
||||||
|
local configs = require "nvim-treesitter.configs" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
|
||||||
|
-- Registers all query predicates |
||||||
|
require "nvim-treesitter.query_predicates" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
function M.setup() |
||||||
|
utils.setup_commands("install", install.commands) |
||||||
|
utils.setup_commands("info", info.commands) |
||||||
|
utils.setup_commands("configs", configs.commands) |
||||||
|
configs.init() |
||||||
|
end |
||||||
|
|
||||||
|
function M.define_modules(...) |
||||||
|
configs.define_modules(...) |
||||||
|
end |
||||||
|
|
||||||
|
local get_line_for_node = function(node, type_patterns, transform_fn) |
||||||
|
local node_type = node:type() |
||||||
|
local is_valid = false |
||||||
|
for _, rgx in ipairs(type_patterns) do |
||||||
|
if node_type:find(rgx) then |
||||||
|
is_valid = true |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
if not is_valid then |
||||||
|
return "" |
||||||
|
end |
||||||
|
local line = transform_fn(vim.trim(ts_utils.get_node_text(node)[1] or "")) |
||||||
|
-- Escape % to avoid statusline to evaluate content as expression |
||||||
|
return line:gsub("%%", "%%%%") |
||||||
|
end |
||||||
|
|
||||||
|
-- Trim spaces and opening brackets from end |
||||||
|
local transform_line = function(line) |
||||||
|
return line:gsub("%s*[%[%(%{]*%s*$", "") |
||||||
|
end |
||||||
|
|
||||||
|
function M.statusline(opts) |
||||||
|
if not parsers.has_parser() then |
||||||
|
return |
||||||
|
end |
||||||
|
local options = opts or {} |
||||||
|
if type(opts) == "number" then |
||||||
|
options = { indicator_size = opts } |
||||||
|
end |
||||||
|
local indicator_size = options.indicator_size or 100 |
||||||
|
local type_patterns = options.type_patterns or { "class", "function", "method" } |
||||||
|
local transform_fn = options.transform_fn or transform_line |
||||||
|
local separator = options.separator or " -> " |
||||||
|
|
||||||
|
local current_node = ts_utils.get_node_at_cursor() |
||||||
|
if not current_node then |
||||||
|
return "" |
||||||
|
end |
||||||
|
|
||||||
|
local lines = {} |
||||||
|
local expr = current_node |
||||||
|
|
||||||
|
while expr do |
||||||
|
local line = get_line_for_node(expr, type_patterns, transform_fn) |
||||||
|
if line ~= "" and not vim.tbl_contains(lines, line) then |
||||||
|
table.insert(lines, 1, line) |
||||||
|
end |
||||||
|
expr = expr:parent() |
||||||
|
end |
||||||
|
|
||||||
|
local text = table.concat(lines, separator) |
||||||
|
local text_len = #text |
||||||
|
if text_len > indicator_size then |
||||||
|
return "..." .. text:sub(text_len - indicator_size, text_len) |
||||||
|
end |
||||||
|
|
||||||
|
return text |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,48 @@ |
|||||||
|
local api = vim.api |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
--- Creates a cache table for buffers keyed by a type name. |
||||||
|
--- Cache entries attach to the buffer and cleanup entries |
||||||
|
--- as buffers are detached. |
||||||
|
function M.create_buffer_cache() |
||||||
|
local cache = {} |
||||||
|
|
||||||
|
local items = setmetatable({}, { |
||||||
|
__index = function(tbl, key) |
||||||
|
rawset(tbl, key, {}) |
||||||
|
return rawget(tbl, key) |
||||||
|
end, |
||||||
|
}) |
||||||
|
|
||||||
|
function cache.set(type_name, bufnr, value) |
||||||
|
if not cache.has(type_name, bufnr) then |
||||||
|
-- Clean up the cache if the buffer is detached |
||||||
|
-- to avoid memory leaks |
||||||
|
api.nvim_buf_attach(bufnr, false, { |
||||||
|
on_detach = function() |
||||||
|
cache.remove(type_name, bufnr) |
||||||
|
return true |
||||||
|
end, |
||||||
|
}) |
||||||
|
end |
||||||
|
|
||||||
|
items[type_name][bufnr] = value |
||||||
|
end |
||||||
|
|
||||||
|
function cache.get(type_name, bufnr) |
||||||
|
return items[type_name][bufnr] |
||||||
|
end |
||||||
|
|
||||||
|
function cache.has(type_name, bufnr) |
||||||
|
return cache.get(type_name, bufnr) ~= nil |
||||||
|
end |
||||||
|
|
||||||
|
function cache.remove(type_name, bufnr) |
||||||
|
items[type_name][bufnr] = nil |
||||||
|
end |
||||||
|
|
||||||
|
return cache |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,532 @@ |
|||||||
|
local api = vim.api |
||||||
|
|
||||||
|
local queries = require "nvim-treesitter.query" |
||||||
|
local ts_query = require "vim.treesitter.query" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
local caching = require "nvim-treesitter.caching" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local config = { |
||||||
|
modules = {}, |
||||||
|
sync_install = false, |
||||||
|
ensure_installed = {}, |
||||||
|
ignore_install = {}, |
||||||
|
update_strategy = "lockfile", |
||||||
|
} |
||||||
|
-- List of modules that need to be setup on initialization. |
||||||
|
local queued_modules_defs = {} |
||||||
|
-- Whether we've initialized the plugin yet. |
||||||
|
local is_initialized = false |
||||||
|
local builtin_modules = { |
||||||
|
highlight = { |
||||||
|
module_path = "nvim-treesitter.highlight", |
||||||
|
enable = false, |
||||||
|
custom_captures = {}, |
||||||
|
is_supported = function(lang) |
||||||
|
return queries.has_highlights(lang) |
||||||
|
end, |
||||||
|
additional_vim_regex_highlighting = false, |
||||||
|
}, |
||||||
|
incremental_selection = { |
||||||
|
module_path = "nvim-treesitter.incremental_selection", |
||||||
|
enable = false, |
||||||
|
keymaps = { |
||||||
|
init_selection = "gnn", |
||||||
|
node_incremental = "grn", |
||||||
|
scope_incremental = "grc", |
||||||
|
node_decremental = "grm", |
||||||
|
}, |
||||||
|
is_supported = function() |
||||||
|
return true |
||||||
|
end, |
||||||
|
}, |
||||||
|
indent = { |
||||||
|
module_path = "nvim-treesitter.indent", |
||||||
|
enable = false, |
||||||
|
is_supported = queries.has_indents, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
local attached_buffers_by_module = caching.create_buffer_cache() |
||||||
|
|
||||||
|
-- Resolves a module by requiring the `module_path` or using the module definition. |
||||||
|
local function resolve_module(mod_name) |
||||||
|
local config_mod = M.get_module(mod_name) |
||||||
|
|
||||||
|
if not config_mod then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
if type(config_mod.attach) == "function" and type(config_mod.detach) == "function" then |
||||||
|
return config_mod |
||||||
|
elseif type(config_mod.module_path) == "string" then |
||||||
|
return require(config_mod.module_path) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Enables and attaches the module to a buffer for lang. |
||||||
|
-- @param mod path to module |
||||||
|
-- @param bufnr buffer number, defaults to current buffer |
||||||
|
-- @param lang language, defaults to current language |
||||||
|
local function enable_module(mod, bufnr, lang) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local lang = lang or parsers.get_buf_lang(bufnr) |
||||||
|
M.attach_module(mod, bufnr, lang) |
||||||
|
end |
||||||
|
|
||||||
|
-- Enables autocomands for the module. |
||||||
|
-- After the module is loaded `loaded` will be set to true for the module. |
||||||
|
-- @param mod path to module |
||||||
|
local function enable_mod_conf_autocmd(mod) |
||||||
|
local config_mod = M.get_module(mod) |
||||||
|
if not config_mod or config_mod.loaded then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local cmd = string.format("lua require'nvim-treesitter.configs'.reattach_module('%s')", mod) |
||||||
|
api.nvim_command(string.format("autocmd NvimTreesitter FileType * %s", cmd)) |
||||||
|
|
||||||
|
config_mod.loaded = true |
||||||
|
end |
||||||
|
|
||||||
|
-- Enables the module globally and for all current buffers. |
||||||
|
-- After enabled, `enable` will be set to true for the module. |
||||||
|
-- @param mod path to module |
||||||
|
local function enable_all(mod) |
||||||
|
local config_mod = M.get_module(mod) |
||||||
|
if not config_mod then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
for _, bufnr in pairs(api.nvim_list_bufs()) do |
||||||
|
enable_module(mod, bufnr) |
||||||
|
end |
||||||
|
|
||||||
|
enable_mod_conf_autocmd(mod) |
||||||
|
config_mod.enable = true |
||||||
|
end |
||||||
|
|
||||||
|
-- Disables and detaches the module for a buffer. |
||||||
|
-- @param mod path to module |
||||||
|
-- @param bufnr buffer number, defaults to current buffer |
||||||
|
local function disable_module(mod, bufnr) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
M.detach_module(mod, bufnr) |
||||||
|
end |
||||||
|
|
||||||
|
-- Disables autocomands for the module. |
||||||
|
-- After the module is unloaded `loaded` will be set to false for the module. |
||||||
|
-- @param mod path to module |
||||||
|
local function disable_mod_conf_autocmd(mod) |
||||||
|
local config_mod = M.get_module(mod) |
||||||
|
if not config_mod or not config_mod.loaded then |
||||||
|
return |
||||||
|
end |
||||||
|
-- TODO(kyazdani): detach the correct autocmd... doesn't work when using %s, cmd. |
||||||
|
-- This will remove all autocomands! |
||||||
|
api.nvim_command "autocmd! NvimTreesitter FileType *" |
||||||
|
config_mod.loaded = false |
||||||
|
end |
||||||
|
|
||||||
|
-- Disables the module globally and for all current buffers. |
||||||
|
-- After disabled, `enable` will be set to false for the module. |
||||||
|
-- @param mod path to module |
||||||
|
local function disable_all(mod) |
||||||
|
local config_mod = M.get_module(mod) |
||||||
|
if not config_mod or not config_mod.enable then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
for _, bufnr in pairs(api.nvim_list_bufs()) do |
||||||
|
disable_module(mod, bufnr) |
||||||
|
end |
||||||
|
|
||||||
|
disable_mod_conf_autocmd(mod) |
||||||
|
config_mod.enable = false |
||||||
|
end |
||||||
|
|
||||||
|
-- Toggles a module for a buffer |
||||||
|
-- @param mod path to module |
||||||
|
-- @param bufnr buffer number, defaults to current buffer |
||||||
|
-- @param lang language, defaults to current language |
||||||
|
local function toggle_module(mod, bufnr, lang) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local lang = lang or parsers.get_buf_lang(bufnr) |
||||||
|
|
||||||
|
if attached_buffers_by_module.has(mod, bufnr) then |
||||||
|
disable_module(mod, bufnr) |
||||||
|
else |
||||||
|
enable_module(mod, bufnr, lang) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Toggles the module globally and for all current buffers. |
||||||
|
-- @param mod path to module |
||||||
|
local function toggle_all(mod) |
||||||
|
local config_mod = M.get_module(mod) |
||||||
|
if not config_mod then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
if config_mod.enable then |
||||||
|
disable_all(mod) |
||||||
|
else |
||||||
|
enable_all(mod) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Recurses through all modules including submodules |
||||||
|
-- @param accumulator function called for each module |
||||||
|
-- @param root root configuration table to start at |
||||||
|
-- @param path prefix path |
||||||
|
local function recurse_modules(accumulator, root, path) |
||||||
|
local root = root or config.modules |
||||||
|
|
||||||
|
for name, module in pairs(root) do |
||||||
|
local new_path = path and (path .. "." .. name) or name |
||||||
|
|
||||||
|
if M.is_module(module) then |
||||||
|
accumulator(name, module, new_path, root) |
||||||
|
elseif type(module) == "table" then |
||||||
|
recurse_modules(accumulator, module, new_path) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Shows current configuration of all nvim-treesitter modules |
||||||
|
-- @param process_function function used as the `process` parameter |
||||||
|
-- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess) |
||||||
|
local function config_info(process_function) |
||||||
|
process_function = process_function |
||||||
|
or function(item, path) |
||||||
|
if path[#path] == vim.inspect.METATABLE then |
||||||
|
return |
||||||
|
end |
||||||
|
if path[#path] == "is_supported" then |
||||||
|
return |
||||||
|
end |
||||||
|
return item |
||||||
|
end |
||||||
|
print(vim.inspect(config, { process = process_function })) |
||||||
|
end |
||||||
|
|
||||||
|
if not vim.ui then |
||||||
|
vim.ui = { |
||||||
|
select = function(items, opts, on_choice) |
||||||
|
vim.validate { |
||||||
|
items = { items, "table", false }, |
||||||
|
on_choice = { on_choice, "function", false }, |
||||||
|
} |
||||||
|
opts = opts or {} |
||||||
|
local choices = { opts.prompt or "Select one of:" } |
||||||
|
local format_item = opts.format_item or tostring |
||||||
|
for i, item in pairs(items) do |
||||||
|
table.insert(choices, string.format("%d: %s", i, format_item(item))) |
||||||
|
end |
||||||
|
local choice = vim.fn.inputlist(choices) |
||||||
|
if choice < 1 or choice > #items then |
||||||
|
on_choice(nil, nil) |
||||||
|
else |
||||||
|
on_choice(items[choice], choice) |
||||||
|
end |
||||||
|
end, |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
function M.edit_query_file(query_group, lang) |
||||||
|
lang = lang or parsers.get_buf_lang() |
||||||
|
local files = ts_query.get_query_files(lang, query_group, true) |
||||||
|
if #files == 0 then |
||||||
|
utils.notify "No query file found! Creating a new one!" |
||||||
|
M.edit_query_file_user_after(query_group, lang) |
||||||
|
elseif #files == 1 then |
||||||
|
vim.cmd(":edit " .. files[1]) |
||||||
|
else |
||||||
|
vim.ui.select(files, { prompt = "Select a file:" }, function(file) |
||||||
|
if file then |
||||||
|
vim.cmd(":edit " .. file) |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.edit_query_file_user_after(query_group, lang) |
||||||
|
lang = lang or parsers.get_buf_lang() |
||||||
|
local folder = utils.join_path(vim.fn.stdpath "config", "after", "queries", lang) |
||||||
|
local file = utils.join_path(folder, query_group .. ".scm") |
||||||
|
if vim.fn.isdirectory(folder) ~= 1 then |
||||||
|
vim.ui.select({ "Yes", "No" }, { prompt = '"' .. folder .. '" does not exist. Create it?' }, function(choice) |
||||||
|
if choice == "Yes" then |
||||||
|
vim.fn.mkdir(folder, "p", "0755") |
||||||
|
vim.cmd(":edit " .. file) |
||||||
|
end |
||||||
|
end) |
||||||
|
else |
||||||
|
vim.cmd(":edit " .. file) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
M.commands = { |
||||||
|
TSBufEnable = { |
||||||
|
run = enable_module, |
||||||
|
args = { |
||||||
|
"-nargs=1", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSBufDisable = { |
||||||
|
run = disable_module, |
||||||
|
args = { |
||||||
|
"-nargs=1", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSBufToggle = { |
||||||
|
run = toggle_module, |
||||||
|
args = { |
||||||
|
"-nargs=1", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSEnableAll = { |
||||||
|
run = enable_all, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSDisableAll = { |
||||||
|
run = disable_all, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSToggleAll = { |
||||||
|
run = toggle_all, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSConfigInfo = { |
||||||
|
run = config_info, |
||||||
|
args = { |
||||||
|
"-nargs=0", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSEditQuery = { |
||||||
|
run = M.edit_query_file, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#available_query_groups", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSEditQueryUserAfter = { |
||||||
|
run = M.edit_query_file_user_after, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#available_query_groups", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
-- @param mod: module (string) |
||||||
|
-- @param lang: the language of the buffer (string) |
||||||
|
-- @param bufnr: the bufnr (number) |
||||||
|
function M.is_enabled(mod, lang, bufnr) |
||||||
|
if not parsers.list[lang] or not parsers.has_parser(lang) then |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local module_config = M.get_module(mod) |
||||||
|
if not module_config then |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
if not module_config.enable or not module_config.is_supported(lang) then |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local disable = module_config.disable |
||||||
|
if type(disable) == "function" then |
||||||
|
if disable(lang, bufnr) then |
||||||
|
return false |
||||||
|
end |
||||||
|
elseif type(disable) == "table" then |
||||||
|
-- Otherwise it's a list of languages |
||||||
|
for _, parser in pairs(disable) do |
||||||
|
if lang == parser then |
||||||
|
return false |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
-- Setup call for users to override module configurations. |
||||||
|
-- @param user_data module overrides |
||||||
|
function M.setup(user_data) |
||||||
|
config.modules = vim.tbl_deep_extend("force", config.modules, user_data) |
||||||
|
config.ignore_install = user_data.ignore_install or {} |
||||||
|
|
||||||
|
local ensure_installed = user_data.ensure_installed or {} |
||||||
|
if #ensure_installed > 0 then |
||||||
|
if user_data.sync_install then |
||||||
|
require("nvim-treesitter.install").ensure_installed_sync(ensure_installed) |
||||||
|
else |
||||||
|
require("nvim-treesitter.install").ensure_installed(ensure_installed) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
config.modules.ensure_installed = nil |
||||||
|
|
||||||
|
recurse_modules(function(_, _, new_path) |
||||||
|
local data = utils.get_at_path(config.modules, new_path) |
||||||
|
if data.enable then |
||||||
|
enable_all(new_path) |
||||||
|
end |
||||||
|
end, config.modules) |
||||||
|
end |
||||||
|
|
||||||
|
-- Defines a table of modules that can be attached/detached to buffers |
||||||
|
-- based on language support. A module consist of the following properties: |
||||||
|
-- * @enable Whether the modules is enabled. Can be true or false. |
||||||
|
-- * @disable A list of languages to disable the module for. Only relevant if enable is true. |
||||||
|
-- * @keymaps A list of user mappings for a given module if relevant. |
||||||
|
-- * @is_supported A function which, given a ft, will return true if the ft works on the module. |
||||||
|
-- * @module_path A string path to a module file using `require`. The exported module must contain |
||||||
|
-- an `attach` and `detach` function. This path is not required if `attach` and `detach` |
||||||
|
-- functions are provided directly on the module definition. |
||||||
|
-- * @attach An attach function that is called for each buffer that the module is enabled for. This is required |
||||||
|
-- if a `module_path` is not specified. |
||||||
|
-- * @detach A detach function that is called for each buffer that the module is enabled for. This is required |
||||||
|
-- if a `module_path` is not specified. |
||||||
|
-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order |
||||||
|
-- and can be loaded lazily. |
||||||
|
-- @example |
||||||
|
-- require"nvim-treesitter".define_modules { |
||||||
|
-- my_cool_module = { |
||||||
|
-- attach = function() |
||||||
|
-- do_some_cool_setup() |
||||||
|
-- end, |
||||||
|
-- detach = function() |
||||||
|
-- do_some_cool_teardown() |
||||||
|
-- end |
||||||
|
-- } |
||||||
|
-- } |
||||||
|
function M.define_modules(mod_defs) |
||||||
|
if not is_initialized then |
||||||
|
table.insert(queued_modules_defs, mod_defs) |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
recurse_modules(function(key, mod, _, group) |
||||||
|
group[key] = vim.tbl_extend("keep", mod, { |
||||||
|
enable = false, |
||||||
|
disable = {}, |
||||||
|
is_supported = function() |
||||||
|
return true |
||||||
|
end, |
||||||
|
}) |
||||||
|
end, mod_defs) |
||||||
|
|
||||||
|
config.modules = vim.tbl_deep_extend("keep", config.modules, mod_defs) |
||||||
|
|
||||||
|
for _, mod in ipairs(M.available_modules(mod_defs)) do |
||||||
|
local module_config = M.get_module(mod) |
||||||
|
if module_config and module_config.enable then |
||||||
|
enable_mod_conf_autocmd(mod) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Attaches a module to a buffer |
||||||
|
-- @param mod_name the module name |
||||||
|
-- @param bufnr the bufnr |
||||||
|
-- @param lang the language of the buffer |
||||||
|
function M.attach_module(mod_name, bufnr, lang) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local lang = lang or parsers.get_buf_lang(bufnr) |
||||||
|
local resolved_mod = resolve_module(mod_name) |
||||||
|
|
||||||
|
if resolved_mod and not attached_buffers_by_module.has(mod_name, bufnr) and M.is_enabled(mod_name, lang, bufnr) then |
||||||
|
attached_buffers_by_module.set(mod_name, bufnr, true) |
||||||
|
resolved_mod.attach(bufnr, lang) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Detaches a module to a buffer |
||||||
|
-- @param mod_name the module name |
||||||
|
-- @param bufnr the bufnr |
||||||
|
function M.detach_module(mod_name, bufnr) |
||||||
|
local resolved_mod = resolve_module(mod_name) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
|
||||||
|
if resolved_mod and attached_buffers_by_module.has(mod_name, bufnr) then |
||||||
|
attached_buffers_by_module.remove(mod_name, bufnr) |
||||||
|
resolved_mod.detach(bufnr) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Same as attach_module, but if the module is already attached, detach it first. |
||||||
|
-- @param mod_name the module name |
||||||
|
-- @param bufnr the bufnr |
||||||
|
-- @param lang the language of the buffer |
||||||
|
function M.reattach_module(mod_name, bufnr, lang) |
||||||
|
M.detach_module(mod_name, bufnr) |
||||||
|
M.attach_module(mod_name, bufnr, lang) |
||||||
|
end |
||||||
|
|
||||||
|
-- Gets available modules |
||||||
|
-- @param root root table to find modules |
||||||
|
function M.available_modules(root) |
||||||
|
local modules = {} |
||||||
|
|
||||||
|
recurse_modules(function(_, _, path) |
||||||
|
table.insert(modules, path) |
||||||
|
end, root) |
||||||
|
|
||||||
|
return modules |
||||||
|
end |
||||||
|
|
||||||
|
-- Gets a module config by path |
||||||
|
-- @param mod_path path to the module |
||||||
|
-- @returns the module or nil |
||||||
|
function M.get_module(mod_path) |
||||||
|
local mod = utils.get_at_path(config.modules, mod_path) |
||||||
|
|
||||||
|
return M.is_module(mod) and mod or nil |
||||||
|
end |
||||||
|
|
||||||
|
-- Determines whether the provided table is a module. |
||||||
|
-- A module should contain an attach and detach function. |
||||||
|
-- @param mod the module table |
||||||
|
function M.is_module(mod) |
||||||
|
return type(mod) == "table" |
||||||
|
and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string") |
||||||
|
end |
||||||
|
|
||||||
|
-- Initializes built-in modules and any queued modules |
||||||
|
-- registered by plugins or the user. |
||||||
|
function M.init() |
||||||
|
is_initialized = true |
||||||
|
M.define_modules(builtin_modules) |
||||||
|
|
||||||
|
for _, mod_def in ipairs(queued_modules_defs) do |
||||||
|
M.define_modules(mod_def) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_update_strategy() |
||||||
|
return config.update_strategy |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_ignored_parser_installs() |
||||||
|
return config.ignore_install or {} |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,112 @@ |
|||||||
|
local api = vim.api |
||||||
|
local tsutils = require "nvim-treesitter.ts_utils" |
||||||
|
local query = require "nvim-treesitter.query" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
-- This is cached on buf tick to avoid computing that multiple times |
||||||
|
-- Especially not for every line in the file when `zx` is hit |
||||||
|
local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) |
||||||
|
local max_fold_level = api.nvim_win_get_option(0, "foldnestmax") |
||||||
|
local trim_level = function(level) |
||||||
|
if level > max_fold_level then |
||||||
|
return max_fold_level |
||||||
|
end |
||||||
|
return level |
||||||
|
end |
||||||
|
|
||||||
|
local parser = parsers.get_parser(bufnr) |
||||||
|
|
||||||
|
if not parser then |
||||||
|
return {} |
||||||
|
end |
||||||
|
|
||||||
|
local matches = query.get_capture_matches_recursively(bufnr, function(lang) |
||||||
|
if query.has_folds(lang) then |
||||||
|
return "@fold", "folds" |
||||||
|
elseif query.has_locals(lang) then |
||||||
|
return "@scope", "locals" |
||||||
|
end |
||||||
|
end) |
||||||
|
|
||||||
|
-- start..stop is an inclusive range |
||||||
|
local start_counts = {} |
||||||
|
local stop_counts = {} |
||||||
|
|
||||||
|
local prev_start = -1 |
||||||
|
local prev_stop = -1 |
||||||
|
|
||||||
|
local min_fold_lines = api.nvim_win_get_option(0, "foldminlines") |
||||||
|
|
||||||
|
for _, node in ipairs(matches) do |
||||||
|
local start, _, stop, stop_col = node.node:range() |
||||||
|
|
||||||
|
if stop_col == 0 then |
||||||
|
stop = stop - 1 |
||||||
|
end |
||||||
|
|
||||||
|
local fold_length = stop - start + 1 |
||||||
|
local should_fold = fold_length > min_fold_lines |
||||||
|
|
||||||
|
-- Fold only multiline nodes that are not exactly the same as previously met folds |
||||||
|
-- Checking against just the previously found fold is sufficient if nodes |
||||||
|
-- are returned in preorder or postorder when traversing tree |
||||||
|
if should_fold and not (start == prev_start and stop == prev_stop) then |
||||||
|
start_counts[start] = (start_counts[start] or 0) + 1 |
||||||
|
stop_counts[stop] = (stop_counts[stop] or 0) + 1 |
||||||
|
prev_start = start |
||||||
|
prev_stop = stop |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local levels = {} |
||||||
|
local current_level = 0 |
||||||
|
|
||||||
|
-- We now have the list of fold opening and closing, fill the gaps and mark where fold start |
||||||
|
for lnum = 0, api.nvim_buf_line_count(bufnr) do |
||||||
|
local prefix = "" |
||||||
|
|
||||||
|
local last_trimmed_level = trim_level(current_level) |
||||||
|
current_level = current_level + (start_counts[lnum] or 0) |
||||||
|
local trimmed_level = trim_level(current_level) |
||||||
|
current_level = current_level - (stop_counts[lnum] or 0) |
||||||
|
local next_trimmed_level = trim_level(current_level) |
||||||
|
|
||||||
|
-- Determine if it's the start/end of a fold |
||||||
|
-- NB: vim's fold-expr interface does not have a mechanism to indicate that |
||||||
|
-- two (or more) folds start at this line, so it cannot distinguish between |
||||||
|
-- ( \n ( \n )) \n (( \n ) \n ) |
||||||
|
-- versus |
||||||
|
-- ( \n ( \n ) \n ( \n ) \n ) |
||||||
|
-- If it did have such a mechanism, (trimmed_level - last_trimmed_level) |
||||||
|
-- would be the correct number of starts to pass on. |
||||||
|
if trimmed_level - last_trimmed_level > 0 then |
||||||
|
prefix = ">" |
||||||
|
elseif trimmed_level - next_trimmed_level > 0 then |
||||||
|
-- Ending marks tend to confuse vim more than it helps, particularly when |
||||||
|
-- the fold level changes by at least 2; we can uncomment this if |
||||||
|
-- vim's behavior gets fixed. |
||||||
|
-- prefix = "<" |
||||||
|
prefix = "" |
||||||
|
end |
||||||
|
|
||||||
|
levels[lnum + 1] = prefix .. tostring(trimmed_level) |
||||||
|
end |
||||||
|
|
||||||
|
return levels |
||||||
|
end) |
||||||
|
|
||||||
|
function M.get_fold_indic(lnum) |
||||||
|
if not parsers.has_parser() or not lnum then |
||||||
|
return "0" |
||||||
|
end |
||||||
|
|
||||||
|
local buf = api.nvim_get_current_buf() |
||||||
|
|
||||||
|
local levels = folds_levels(buf) or {} |
||||||
|
|
||||||
|
return levels[lnum] or "0" |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,150 @@ |
|||||||
|
local api = vim.api |
||||||
|
local fn = vim.fn |
||||||
|
|
||||||
|
local queries = require "nvim-treesitter.query" |
||||||
|
local info = require "nvim-treesitter.info" |
||||||
|
local shell = require "nvim-treesitter.shell_command_selectors" |
||||||
|
local install = require "nvim-treesitter.install" |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
|
||||||
|
local health_start = vim.fn["health#report_start"] |
||||||
|
local health_ok = vim.fn["health#report_ok"] |
||||||
|
local health_error = vim.fn["health#report_error"] |
||||||
|
local health_warn = vim.fn["health#report_warn"] |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local NVIM_TREESITTER_MINIMUM_ABI = 13 |
||||||
|
|
||||||
|
local function install_health() |
||||||
|
health_start "Installation" |
||||||
|
|
||||||
|
if fn.executable "tree-sitter" == 0 then |
||||||
|
health_warn( |
||||||
|
"`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar," |
||||||
|
.. " not required for :TSInstall)" |
||||||
|
) |
||||||
|
else |
||||||
|
health_ok( |
||||||
|
"`tree-sitter` found " |
||||||
|
.. (utils.ts_cli_version() or "(unknown version)") |
||||||
|
.. " (parser generator, only needed for :TSInstallFromGrammar)" |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
if fn.executable "node" == 0 then |
||||||
|
health_warn( |
||||||
|
"`node` executable not found (only needed for :TSInstallFromGrammar," .. " not required for :TSInstall)" |
||||||
|
) |
||||||
|
else |
||||||
|
local handle = io.popen "node --version" |
||||||
|
local result = handle:read "*a" |
||||||
|
handle:close() |
||||||
|
local version = vim.split(result, "\n")[1] |
||||||
|
health_ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)") |
||||||
|
end |
||||||
|
|
||||||
|
if fn.executable "git" == 0 then |
||||||
|
health_error("`git` executable not found.", { |
||||||
|
"Install it with your package manager.", |
||||||
|
"Check that your `$PATH` is set correctly.", |
||||||
|
}) |
||||||
|
else |
||||||
|
health_ok "`git` executable found." |
||||||
|
end |
||||||
|
|
||||||
|
local cc = shell.select_executable(install.compilers) |
||||||
|
if not cc then |
||||||
|
health_error("`cc` executable not found.", { |
||||||
|
"Check that any of " |
||||||
|
.. vim.inspect(install.compilers) |
||||||
|
.. " is in your $PATH" |
||||||
|
.. ' or set the environment variable CC or `require"nvim-treesitter.install".compilers` explicitly!', |
||||||
|
}) |
||||||
|
else |
||||||
|
local version = vim.fn.systemlist(cc .. (cc == "cl" and "" or " --version"))[1] |
||||||
|
health_ok( |
||||||
|
"`" |
||||||
|
.. cc |
||||||
|
.. "` executable found. Selected from " |
||||||
|
.. vim.inspect(install.compilers) |
||||||
|
.. (version and ("\nVersion: " .. version) or "") |
||||||
|
) |
||||||
|
end |
||||||
|
if vim.treesitter.language_version then |
||||||
|
if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then |
||||||
|
health_ok( |
||||||
|
"Neovim was compiled with tree-sitter runtime ABI version " |
||||||
|
.. vim.treesitter.language_version |
||||||
|
.. " (required >=" |
||||||
|
.. NVIM_TREESITTER_MINIMUM_ABI |
||||||
|
.. "). Parsers must be compatible with runtime ABI." |
||||||
|
) |
||||||
|
else |
||||||
|
health_error( |
||||||
|
"Neovim was compiled with tree-sitter runtime ABI version " |
||||||
|
.. vim.treesitter.language_version |
||||||
|
.. ".\n" |
||||||
|
.. "nvim-treesitter expects at least ABI version " |
||||||
|
.. NVIM_TREESITTER_MINIMUM_ABI |
||||||
|
.. "\n" |
||||||
|
.. "Please make sure that Neovim is linked against are recent tree-sitter runtime when building" |
||||||
|
.. " or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI." |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function query_status(lang, query_group) |
||||||
|
local ok, err = pcall(queries.get_query, lang, query_group) |
||||||
|
if not ok then |
||||||
|
return "x", err |
||||||
|
elseif not err then |
||||||
|
return "." |
||||||
|
else |
||||||
|
return "✓" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.check() |
||||||
|
local error_collection = {} |
||||||
|
-- Installation dependency checks |
||||||
|
install_health() |
||||||
|
queries.invalidate_query_cache() |
||||||
|
-- Parser installation checks |
||||||
|
local parser_installation = { "Parser/Features H L F I J" } |
||||||
|
for _, parser_name in pairs(info.installed_parsers()) do |
||||||
|
local installed = #api.nvim_get_runtime_file("parser/" .. parser_name .. ".so", false) |
||||||
|
|
||||||
|
-- Only append information about installed parsers |
||||||
|
if installed >= 1 then |
||||||
|
local multiple_parsers = installed > 1 and "+" or "" |
||||||
|
local out = " - " .. parser_name .. multiple_parsers .. string.rep(" ", 15 - (#parser_name + #multiple_parsers)) |
||||||
|
for _, query_group in pairs(queries.built_in_query_groups) do |
||||||
|
local status, err = query_status(parser_name, query_group) |
||||||
|
out = out .. status .. " " |
||||||
|
if err then |
||||||
|
table.insert(error_collection, { parser_name, query_group, err }) |
||||||
|
end |
||||||
|
end |
||||||
|
table.insert(parser_installation, out) |
||||||
|
end |
||||||
|
end |
||||||
|
local legend = [[ |
||||||
|
|
||||||
|
Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections |
||||||
|
+) multiple parsers found, only one will be used |
||||||
|
x) errors found in the query, try to run :TSUpdate {lang}]] |
||||||
|
table.insert(parser_installation, legend) |
||||||
|
-- Finally call the report function |
||||||
|
health_start(table.concat(parser_installation, "\n")) |
||||||
|
if #error_collection > 0 then |
||||||
|
health_start "The following errors have been detected:" |
||||||
|
for _, p in ipairs(error_collection) do |
||||||
|
local lang, type, err = unpack(p) |
||||||
|
health_error(lang .. "(" .. type .. "): " .. err) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,132 @@ |
|||||||
|
local api = vim.api |
||||||
|
local ts = vim.treesitter |
||||||
|
|
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local configs = require "nvim-treesitter.configs" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local hlmap = vim.treesitter.highlighter.hl_map |
||||||
|
|
||||||
|
-- nvim-treesitter Highlight Group Mappings |
||||||
|
-- Note: Some highlight groups may not be applied upstream, some may be experimental |
||||||
|
|
||||||
|
hlmap["annotation"] = "TSAnnotation" |
||||||
|
|
||||||
|
hlmap["attribute"] = "TSAttribute" |
||||||
|
|
||||||
|
hlmap["boolean"] = "TSBoolean" |
||||||
|
|
||||||
|
hlmap["character"] = "TSCharacter" |
||||||
|
|
||||||
|
hlmap["comment"] = "TSComment" |
||||||
|
|
||||||
|
hlmap["conditional"] = "TSConditional" |
||||||
|
|
||||||
|
hlmap["constant"] = "TSConstant" |
||||||
|
hlmap["constant.builtin"] = "TSConstBuiltin" |
||||||
|
hlmap["constant.macro"] = "TSConstMacro" |
||||||
|
|
||||||
|
hlmap["constructor"] = "TSConstructor" |
||||||
|
|
||||||
|
hlmap["error"] = "TSError" |
||||||
|
hlmap["exception"] = "TSException" |
||||||
|
|
||||||
|
hlmap["field"] = "TSField" |
||||||
|
|
||||||
|
hlmap["float"] = "TSFloat" |
||||||
|
|
||||||
|
hlmap["function"] = "TSFunction" |
||||||
|
hlmap["function.builtin"] = "TSFuncBuiltin" |
||||||
|
hlmap["function.macro"] = "TSFuncMacro" |
||||||
|
|
||||||
|
hlmap["include"] = "TSInclude" |
||||||
|
|
||||||
|
hlmap["keyword"] = "TSKeyword" |
||||||
|
hlmap["keyword.function"] = "TSKeywordFunction" |
||||||
|
hlmap["keyword.operator"] = "TSKeywordOperator" |
||||||
|
hlmap["keyword.return"] = "TSKeywordReturn" |
||||||
|
|
||||||
|
hlmap["label"] = "TSLabel" |
||||||
|
|
||||||
|
hlmap["method"] = "TSMethod" |
||||||
|
|
||||||
|
hlmap["namespace"] = "TSNamespace" |
||||||
|
|
||||||
|
hlmap["none"] = "TSNone" |
||||||
|
hlmap["number"] = "TSNumber" |
||||||
|
|
||||||
|
hlmap["operator"] = "TSOperator" |
||||||
|
|
||||||
|
hlmap["parameter"] = "TSParameter" |
||||||
|
hlmap["parameter.reference"] = "TSParameterReference" |
||||||
|
|
||||||
|
hlmap["property"] = "TSProperty" |
||||||
|
|
||||||
|
hlmap["punctuation.delimiter"] = "TSPunctDelimiter" |
||||||
|
hlmap["punctuation.bracket"] = "TSPunctBracket" |
||||||
|
hlmap["punctuation.special"] = "TSPunctSpecial" |
||||||
|
|
||||||
|
hlmap["repeat"] = "TSRepeat" |
||||||
|
|
||||||
|
hlmap["string"] = "TSString" |
||||||
|
hlmap["string.regex"] = "TSStringRegex" |
||||||
|
hlmap["string.escape"] = "TSStringEscape" |
||||||
|
hlmap["string.special"] = "TSStringSpecial" |
||||||
|
|
||||||
|
hlmap["symbol"] = "TSSymbol" |
||||||
|
|
||||||
|
hlmap["tag"] = "TSTag" |
||||||
|
hlmap["tag.attribute"] = "TSTagAttribute" |
||||||
|
hlmap["tag.delimiter"] = "TSTagDelimiter" |
||||||
|
|
||||||
|
hlmap["text"] = "TSText" |
||||||
|
hlmap["text.strong"] = "TSStrong" |
||||||
|
hlmap["text.emphasis"] = "TSEmphasis" |
||||||
|
hlmap["text.underline"] = "TSUnderline" |
||||||
|
hlmap["text.strike"] = "TSStrike" |
||||||
|
hlmap["text.title"] = "TSTitle" |
||||||
|
hlmap["text.literal"] = "TSLiteral" |
||||||
|
hlmap["text.uri"] = "TSURI" |
||||||
|
hlmap["text.math"] = "TSMath" |
||||||
|
hlmap["text.reference"] = "TSTextReference" |
||||||
|
hlmap["text.environment"] = "TSEnvironment" |
||||||
|
hlmap["text.environment.name"] = "TSEnvironmentName" |
||||||
|
|
||||||
|
hlmap["text.note"] = "TSNote" |
||||||
|
hlmap["text.warning"] = "TSWarning" |
||||||
|
hlmap["text.danger"] = "TSDanger" |
||||||
|
|
||||||
|
hlmap["type"] = "TSType" |
||||||
|
hlmap["type.builtin"] = "TSTypeBuiltin" |
||||||
|
|
||||||
|
hlmap["variable"] = "TSVariable" |
||||||
|
hlmap["variable.builtin"] = "TSVariableBuiltin" |
||||||
|
|
||||||
|
function M.attach(bufnr, lang) |
||||||
|
local parser = parsers.get_parser(bufnr, lang) |
||||||
|
local config = configs.get_module "highlight" |
||||||
|
|
||||||
|
for k, v in pairs(config.custom_captures) do |
||||||
|
hlmap[k] = v |
||||||
|
end |
||||||
|
|
||||||
|
ts.highlighter.new(parser, {}) |
||||||
|
|
||||||
|
local is_table = type(config.additional_vim_regex_highlighting) == "table" |
||||||
|
if |
||||||
|
config.additional_vim_regex_highlighting |
||||||
|
and (not is_table or vim.tbl_contains(config.additional_vim_regex_highlighting, lang)) |
||||||
|
then |
||||||
|
api.nvim_buf_set_option(bufnr, "syntax", "ON") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.detach(bufnr) |
||||||
|
if ts.highlighter.active[bufnr] then |
||||||
|
ts.highlighter.active[bufnr]:destroy() |
||||||
|
end |
||||||
|
api.nvim_buf_set_option(bufnr, "syntax", "ON") |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,148 @@ |
|||||||
|
local api = vim.api |
||||||
|
|
||||||
|
local configs = require "nvim-treesitter.configs" |
||||||
|
local ts_utils = require "nvim-treesitter.ts_utils" |
||||||
|
local locals = require "nvim-treesitter.locals" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local queries = require "nvim-treesitter.query" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local selections = {} |
||||||
|
|
||||||
|
function M.init_selection() |
||||||
|
local buf = api.nvim_get_current_buf() |
||||||
|
local node = ts_utils.get_node_at_cursor() |
||||||
|
selections[buf] = { [1] = node } |
||||||
|
ts_utils.update_selection(buf, node) |
||||||
|
end |
||||||
|
|
||||||
|
--- Get the range of the current visual selection. |
||||||
|
-- |
||||||
|
-- The range start with 1 and the ending is inclusive. |
||||||
|
local function visual_selection_range() |
||||||
|
local _, csrow, cscol, _ = unpack(vim.fn.getpos "'<") |
||||||
|
local _, cerow, cecol, _ = unpack(vim.fn.getpos "'>") |
||||||
|
|
||||||
|
local start_row, start_col, end_row, end_col |
||||||
|
|
||||||
|
if csrow < cerow or (csrow == cerow and cscol <= cecol) then |
||||||
|
start_row = csrow |
||||||
|
start_col = cscol |
||||||
|
end_row = cerow |
||||||
|
end_col = cecol |
||||||
|
else |
||||||
|
start_row = cerow |
||||||
|
start_col = cecol |
||||||
|
end_row = csrow |
||||||
|
end_col = cscol |
||||||
|
end |
||||||
|
|
||||||
|
return start_row, start_col, end_row, end_col |
||||||
|
end |
||||||
|
|
||||||
|
local function range_matches(node) |
||||||
|
local csrow, cscol, cerow, cecol = visual_selection_range() |
||||||
|
local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } |
||||||
|
return srow == csrow and scol == cscol and erow == cerow and ecol == cecol |
||||||
|
end |
||||||
|
|
||||||
|
local function select_incremental(get_parent) |
||||||
|
return function() |
||||||
|
local buf = api.nvim_get_current_buf() |
||||||
|
local nodes = selections[buf] |
||||||
|
|
||||||
|
local csrow, cscol, cerow, cecol = visual_selection_range() |
||||||
|
-- Initialize incremental selection with current selection |
||||||
|
if not nodes or #nodes == 0 or not range_matches(nodes[#nodes]) then |
||||||
|
local root = parsers.get_parser():parse()[1]:root() |
||||||
|
local node = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) |
||||||
|
ts_utils.update_selection(buf, node) |
||||||
|
if nodes and #nodes > 0 then |
||||||
|
table.insert(selections[buf], node) |
||||||
|
else |
||||||
|
selections[buf] = { [1] = node } |
||||||
|
end |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
-- Find a node that changes the current selection. |
||||||
|
local node = nodes[#nodes] |
||||||
|
while true do |
||||||
|
local parent = get_parent(node) |
||||||
|
if not parent or parent == node then |
||||||
|
-- Keep searching in the main tree |
||||||
|
-- TODO: we should search on the parent tree of the current node. |
||||||
|
local root = parsers.get_parser():parse()[1]:root() |
||||||
|
parent = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) |
||||||
|
if not parent or root == node or parent == node then |
||||||
|
ts_utils.update_selection(buf, node) |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
node = parent |
||||||
|
local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } |
||||||
|
local same_range = (srow == csrow and scol == cscol and erow == cerow and ecol == cecol) |
||||||
|
if not same_range then |
||||||
|
table.insert(selections[buf], node) |
||||||
|
if node ~= nodes[#nodes] then |
||||||
|
table.insert(nodes, node) |
||||||
|
end |
||||||
|
ts_utils.update_selection(buf, node) |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
M.node_incremental = select_incremental(function(node) |
||||||
|
return node:parent() or node |
||||||
|
end) |
||||||
|
|
||||||
|
M.scope_incremental = select_incremental(function(node) |
||||||
|
local lang = parsers.get_buf_lang() |
||||||
|
if queries.has_locals(lang) then |
||||||
|
return locals.containing_scope(node:parent() or node) |
||||||
|
else |
||||||
|
return node |
||||||
|
end |
||||||
|
end) |
||||||
|
|
||||||
|
function M.node_decremental() |
||||||
|
local buf = api.nvim_get_current_buf() |
||||||
|
local nodes = selections[buf] |
||||||
|
if not nodes or #nodes < 2 then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
table.remove(selections[buf]) |
||||||
|
local node = nodes[#nodes] |
||||||
|
ts_utils.update_selection(buf, node) |
||||||
|
end |
||||||
|
|
||||||
|
function M.attach(bufnr) |
||||||
|
local config = configs.get_module "incremental_selection" |
||||||
|
for funcname, mapping in pairs(config.keymaps) do |
||||||
|
local mode |
||||||
|
if funcname == "init_selection" then |
||||||
|
mode = "n" |
||||||
|
else |
||||||
|
mode = "x" |
||||||
|
end |
||||||
|
local cmd = string.format(":lua require'nvim-treesitter.incremental_selection'.%s()<CR>", funcname) |
||||||
|
api.nvim_buf_set_keymap(bufnr, mode, mapping, cmd, { silent = true, noremap = true }) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.detach(bufnr) |
||||||
|
local config = configs.get_module "incremental_selection" |
||||||
|
for f, mapping in pairs(config.keymaps) do |
||||||
|
if f == "init_selection" then |
||||||
|
api.nvim_buf_del_keymap(bufnr, "n", mapping) |
||||||
|
else |
||||||
|
api.nvim_buf_del_keymap(bufnr, "x", mapping) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,166 @@ |
|||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local queries = require "nvim-treesitter.query" |
||||||
|
local tsutils = require "nvim-treesitter.ts_utils" |
||||||
|
|
||||||
|
local function get_first_node_at_line(root, lnum) |
||||||
|
local col = vim.fn.indent(lnum) |
||||||
|
return root:descendant_for_range(lnum - 1, col, lnum - 1, col) |
||||||
|
end |
||||||
|
|
||||||
|
local function get_last_node_at_line(root, lnum) |
||||||
|
local col = #vim.fn.getline(lnum) - 1 |
||||||
|
return root:descendant_for_range(lnum - 1, col, lnum - 1, col) |
||||||
|
end |
||||||
|
|
||||||
|
local function get_matching_prev_sibling(anchor, start, matcher) |
||||||
|
local start_row, start_col = start[1], start[2] |
||||||
|
local node = anchor:descendant_for_range(start_row, start_col, start_row, start_col) |
||||||
|
local pos = 1 |
||||||
|
-- TODO: reconsider this 999 limit or do something differently in future. |
||||||
|
-- if anchor has more than 999 children, this would not work. |
||||||
|
while pos < 999 and node and not matcher(node) do |
||||||
|
node = node:prev_sibling() |
||||||
|
pos = pos + 1 |
||||||
|
end |
||||||
|
return node, pos |
||||||
|
end |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local get_indents = tsutils.memoize_by_buf_tick(function(bufnr, root, lang) |
||||||
|
local map = { |
||||||
|
auto = {}, |
||||||
|
indent = {}, |
||||||
|
indent_end = {}, |
||||||
|
dedent = {}, |
||||||
|
branch = {}, |
||||||
|
ignore = {}, |
||||||
|
aligned_indent = {}, |
||||||
|
} |
||||||
|
|
||||||
|
for name, node, metadata in queries.iter_captures(bufnr, "indents", root, lang) do |
||||||
|
map[name][node:id()] = metadata or {} |
||||||
|
end |
||||||
|
|
||||||
|
return map |
||||||
|
end, { |
||||||
|
-- Memoize by bufnr and lang together. |
||||||
|
key = function(bufnr, root, lang) |
||||||
|
return tostring(bufnr) .. root:id() .. "_" .. lang |
||||||
|
end, |
||||||
|
}) |
||||||
|
|
||||||
|
---@param lnum number (1-indexed) |
||||||
|
function M.get_indent(lnum) |
||||||
|
local parser = parsers.get_parser() |
||||||
|
if not parser or not lnum then |
||||||
|
return -1 |
||||||
|
end |
||||||
|
|
||||||
|
-- get_root_for_position is 0-based. |
||||||
|
local root, _, lang_tree = tsutils.get_root_for_position(lnum - 1, 0, parser) |
||||||
|
|
||||||
|
-- Not likely, but just in case... |
||||||
|
if not root then |
||||||
|
return 0 |
||||||
|
end |
||||||
|
|
||||||
|
local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang()) |
||||||
|
local is_empty_line = string.match(vim.fn.getline(lnum), "^%s*$") ~= nil |
||||||
|
local node |
||||||
|
if is_empty_line then |
||||||
|
local prevlnum = vim.fn.prevnonblank(lnum) |
||||||
|
node = get_last_node_at_line(root, prevlnum) |
||||||
|
if q.indent_end[node:id()] then |
||||||
|
node = get_first_node_at_line(root, lnum) |
||||||
|
end |
||||||
|
else |
||||||
|
node = get_first_node_at_line(root, lnum) |
||||||
|
end |
||||||
|
|
||||||
|
local indent_size = vim.fn.shiftwidth() |
||||||
|
local indent = 0 |
||||||
|
if root:start() ~= 0 then |
||||||
|
-- injected tree |
||||||
|
indent = vim.fn.indent(root:start() + 1) |
||||||
|
end |
||||||
|
|
||||||
|
-- tracks to ensure multiple indent levels are not applied for same line |
||||||
|
local is_processed_by_row = {} |
||||||
|
|
||||||
|
while node do |
||||||
|
-- do 'autoindent' if not marked as @indent |
||||||
|
if not q.indent[node:id()] and q.auto[node:id()] and node:start() < lnum - 1 and lnum - 1 <= node:end_() then |
||||||
|
return -1 |
||||||
|
end |
||||||
|
|
||||||
|
-- Do not indent if we are inside an @ignore block. |
||||||
|
-- If a node spans from L1,C1 to L2,C2, we know that lines where L1 < line <= L2 would |
||||||
|
-- have their indentations contained by the node. |
||||||
|
if not q.indent[node:id()] and q.ignore[node:id()] and node:start() < lnum - 1 and lnum - 1 <= node:end_() then |
||||||
|
return 0 |
||||||
|
end |
||||||
|
|
||||||
|
local srow, _, erow = node:range() |
||||||
|
|
||||||
|
local is_processed = false |
||||||
|
|
||||||
|
if |
||||||
|
not is_processed_by_row[srow] |
||||||
|
and ((q.branch[node:id()] and srow == lnum - 1) or (q.dedent[node:id()] and srow ~= lnum - 1)) |
||||||
|
then |
||||||
|
indent = indent - indent_size |
||||||
|
is_processed = true |
||||||
|
end |
||||||
|
|
||||||
|
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum) |
||||||
|
if not is_processed_by_row[srow] and (q.indent[node:id()] and srow ~= erow and srow ~= lnum - 1) then |
||||||
|
indent = indent + indent_size |
||||||
|
is_processed = true |
||||||
|
end |
||||||
|
|
||||||
|
if q.aligned_indent[node:id()] and srow ~= erow then |
||||||
|
local metadata = q.aligned_indent[node:id()] |
||||||
|
local opening_delimiter = metadata.delimiter:sub(1, 1) |
||||||
|
local o_delim_node, pos = get_matching_prev_sibling(node, { srow, #vim.fn.getline(srow + 1) - 1 }, function(n) |
||||||
|
return n:type() == opening_delimiter |
||||||
|
end) |
||||||
|
|
||||||
|
if o_delim_node then |
||||||
|
if pos == 1 then |
||||||
|
-- hanging indent (previous line ended with starting delimiter) |
||||||
|
indent = indent + indent_size * 1 |
||||||
|
else |
||||||
|
local _, o_scol = o_delim_node:start() |
||||||
|
local aligned_indent = math.max(indent, 0) + o_scol |
||||||
|
if indent > 0 then |
||||||
|
indent = aligned_indent |
||||||
|
else |
||||||
|
indent = aligned_indent + 1 -- extra space for starting delimiter |
||||||
|
end |
||||||
|
is_processed = true |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
is_processed_by_row[srow] = is_processed_by_row[srow] or is_processed |
||||||
|
|
||||||
|
node = node:parent() |
||||||
|
end |
||||||
|
|
||||||
|
return indent |
||||||
|
end |
||||||
|
|
||||||
|
local indent_funcs = {} |
||||||
|
|
||||||
|
function M.attach(bufnr) |
||||||
|
indent_funcs[bufnr] = vim.bo.indentexpr |
||||||
|
vim.bo.indentexpr = "nvim_treesitter#indent()" |
||||||
|
vim.api.nvim_command("au Filetype " .. vim.bo.filetype .. " setlocal indentexpr=nvim_treesitter#indent()") |
||||||
|
end |
||||||
|
|
||||||
|
function M.detach(bufnr) |
||||||
|
vim.bo.indentexpr = indent_funcs[bufnr] |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,174 @@ |
|||||||
|
local api = vim.api |
||||||
|
local configs = require "nvim-treesitter.configs" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local function install_info() |
||||||
|
local max_len = 0 |
||||||
|
for _, ft in pairs(parsers.available_parsers()) do |
||||||
|
if #ft > max_len then |
||||||
|
max_len = #ft |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local parser_list = parsers.available_parsers() |
||||||
|
table.sort(parser_list) |
||||||
|
for _, ft in pairs(parser_list) do |
||||||
|
local is_installed = #api.nvim_get_runtime_file("parser/" .. ft .. ".so", false) > 0 |
||||||
|
api.nvim_out_write(ft .. string.rep(" ", max_len - #ft + 1)) |
||||||
|
if is_installed then |
||||||
|
api.nvim_out_write "[✓] installed\n" |
||||||
|
else |
||||||
|
api.nvim_out_write "[✗] not installed\n" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Sort a list of modules into namespaces. |
||||||
|
-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'} |
||||||
|
-- -> |
||||||
|
-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}} |
||||||
|
local function namespace_modules(modulelist) |
||||||
|
local modules = {} |
||||||
|
for _, module in ipairs(modulelist) do |
||||||
|
if module:find "%." then |
||||||
|
local namespace, submodule = module:match "^(.*)%.(.*)$" |
||||||
|
if not modules[namespace] then |
||||||
|
modules[namespace] = {} |
||||||
|
end |
||||||
|
table.insert(modules[namespace], submodule) |
||||||
|
else |
||||||
|
if not modules.default then |
||||||
|
modules.default = {} |
||||||
|
end |
||||||
|
table.insert(modules.default, module) |
||||||
|
end |
||||||
|
end |
||||||
|
return modules |
||||||
|
end |
||||||
|
|
||||||
|
local function longest_string_length(list) |
||||||
|
local length = 0 |
||||||
|
for _, value in ipairs(list) do |
||||||
|
if #value > length then |
||||||
|
length = #value |
||||||
|
end |
||||||
|
end |
||||||
|
return length |
||||||
|
end |
||||||
|
|
||||||
|
local function append_module_table(curbuf, origbuf, parserlist, namespace, modulelist) |
||||||
|
local maxlen_parser = longest_string_length(parserlist) |
||||||
|
table.sort(modulelist) |
||||||
|
|
||||||
|
-- header |
||||||
|
local header = ">> " .. namespace .. string.rep(" ", maxlen_parser - #namespace - 1) |
||||||
|
for _, module in pairs(modulelist) do |
||||||
|
header = header .. module .. " " |
||||||
|
end |
||||||
|
api.nvim_buf_set_lines(curbuf, -1, -1, true, { header }) |
||||||
|
|
||||||
|
-- actual table |
||||||
|
for _, parser in ipairs(parserlist) do |
||||||
|
local padding = string.rep(" ", maxlen_parser - #parser + 2) |
||||||
|
local line = parser .. padding |
||||||
|
local namespace_prefix = (namespace == "default") and "" or namespace .. "." |
||||||
|
for _, module in pairs(modulelist) do |
||||||
|
local modlen = #module |
||||||
|
module = namespace_prefix .. module |
||||||
|
if configs.is_enabled(module, parser, origbuf) then |
||||||
|
line = line .. "✓" |
||||||
|
else |
||||||
|
line = line .. "✗" |
||||||
|
end |
||||||
|
line = line .. string.rep(" ", modlen + 1) |
||||||
|
end |
||||||
|
api.nvim_buf_set_lines(curbuf, -1, -1, true, { line }) |
||||||
|
end |
||||||
|
|
||||||
|
api.nvim_buf_set_lines(curbuf, -1, -1, true, { "" }) |
||||||
|
end |
||||||
|
|
||||||
|
local function print_info_modules(parserlist, module) |
||||||
|
local origbuf = api.nvim_get_current_buf() |
||||||
|
api.nvim_command "enew" |
||||||
|
local curbuf = api.nvim_get_current_buf() |
||||||
|
|
||||||
|
local modules |
||||||
|
if module then |
||||||
|
modules = namespace_modules { module } |
||||||
|
else |
||||||
|
modules = namespace_modules(configs.available_modules()) |
||||||
|
end |
||||||
|
|
||||||
|
local namespaces = {} |
||||||
|
for k, _ in pairs(modules) do |
||||||
|
table.insert(namespaces, k) |
||||||
|
end |
||||||
|
table.sort(namespaces) |
||||||
|
|
||||||
|
table.sort(parserlist) |
||||||
|
for _, namespace in ipairs(namespaces) do |
||||||
|
append_module_table(curbuf, origbuf, parserlist, namespace, modules[namespace]) |
||||||
|
end |
||||||
|
|
||||||
|
api.nvim_buf_set_option(curbuf, "modified", false) |
||||||
|
api.nvim_buf_set_option(curbuf, "buftype", "nofile") |
||||||
|
api.nvim_exec( |
||||||
|
[[ |
||||||
|
syntax match TSModuleInfoGood /✓/ |
||||||
|
syntax match TSModuleInfoBad /✗/ |
||||||
|
syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace |
||||||
|
syntax match TSModuleInfoNamespace /^>> \w*/ contained |
||||||
|
syntax match TSModuleInfoParser /^[^> ]*\ze / |
||||||
|
highlight default TSModuleInfoGood guifg=LightGreen gui=bold |
||||||
|
highlight default TSModuleInfoBad guifg=Crimson |
||||||
|
highlight default link TSModuleInfoHeader Type |
||||||
|
highlight default link TSModuleInfoNamespace Statement |
||||||
|
highlight default link TSModuleInfoParser Identifier |
||||||
|
]], |
||||||
|
false |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
local function module_info(module) |
||||||
|
if module and not configs.get_module(module) then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local parserlist = parsers.available_parsers() |
||||||
|
if module then |
||||||
|
print_info_modules(parserlist, module) |
||||||
|
else |
||||||
|
print_info_modules(parserlist) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.installed_parsers() |
||||||
|
local installed = {} |
||||||
|
for _, p in pairs(parsers.available_parsers()) do |
||||||
|
if parsers.has_parser(p) then |
||||||
|
table.insert(installed, p) |
||||||
|
end |
||||||
|
end |
||||||
|
return installed |
||||||
|
end |
||||||
|
|
||||||
|
M.commands = { |
||||||
|
TSInstallInfo = { |
||||||
|
run = install_info, |
||||||
|
args = { |
||||||
|
"-nargs=0", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSModuleInfo = { |
||||||
|
run = module_info, |
||||||
|
args = { |
||||||
|
"-nargs=?", |
||||||
|
"-complete=custom,nvim_treesitter#available_modules", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,590 @@ |
|||||||
|
local api = vim.api |
||||||
|
local fn = vim.fn |
||||||
|
local luv = vim.loop |
||||||
|
|
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local info = require "nvim-treesitter.info" |
||||||
|
local configs = require "nvim-treesitter.configs" |
||||||
|
local shell = require "nvim-treesitter.shell_command_selectors" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
local lockfile = {} |
||||||
|
|
||||||
|
M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" } |
||||||
|
M.prefer_git = fn.has "win32" == 1 |
||||||
|
M.command_extra_args = {} |
||||||
|
M.ts_generate_args = nil |
||||||
|
|
||||||
|
local started_commands = 0 |
||||||
|
local finished_commands = 0 |
||||||
|
local failed_commands = 0 |
||||||
|
local complete_std_output = {} |
||||||
|
local complete_error_output = {} |
||||||
|
|
||||||
|
local function reset_progress_counter() |
||||||
|
if started_commands ~= finished_commands then |
||||||
|
return |
||||||
|
end |
||||||
|
started_commands = 0 |
||||||
|
finished_commands = 0 |
||||||
|
failed_commands = 0 |
||||||
|
complete_std_output = {} |
||||||
|
complete_error_output = {} |
||||||
|
end |
||||||
|
|
||||||
|
local function get_job_status() |
||||||
|
return "[nvim-treesitter] [" |
||||||
|
.. finished_commands |
||||||
|
.. "/" |
||||||
|
.. started_commands |
||||||
|
.. (failed_commands > 0 and ", failed: " .. failed_commands or "") |
||||||
|
.. "]" |
||||||
|
end |
||||||
|
|
||||||
|
local function get_parser_install_info(lang, validate) |
||||||
|
local parser_config = parsers.get_parser_configs()[lang] |
||||||
|
|
||||||
|
if not parser_config then |
||||||
|
return error("Parser not available for language " .. lang) |
||||||
|
end |
||||||
|
|
||||||
|
local install_info = parser_config.install_info |
||||||
|
|
||||||
|
if validate then |
||||||
|
vim.validate { |
||||||
|
url = { install_info.url, "string" }, |
||||||
|
files = { install_info.files, "table" }, |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
return install_info |
||||||
|
end |
||||||
|
|
||||||
|
local function load_lockfile() |
||||||
|
local filename = utils.join_path(utils.get_package_path(), "lockfile.json") |
||||||
|
lockfile = vim.fn.filereadable(filename) == 1 and vim.fn.json_decode(vim.fn.readfile(filename)) or {} |
||||||
|
end |
||||||
|
|
||||||
|
local function get_revision(lang) |
||||||
|
if #lockfile == 0 then |
||||||
|
load_lockfile() |
||||||
|
end |
||||||
|
|
||||||
|
local install_info = get_parser_install_info(lang) |
||||||
|
if install_info.revision then |
||||||
|
return install_info.revision |
||||||
|
end |
||||||
|
|
||||||
|
return (lockfile[lang] and lockfile[lang].revision) |
||||||
|
end |
||||||
|
|
||||||
|
local function get_installed_revision(lang) |
||||||
|
local lang_file = utils.join_path(utils.get_parser_info_dir(), lang .. ".revision") |
||||||
|
if vim.fn.filereadable(lang_file) == 1 then |
||||||
|
return vim.fn.readfile(lang_file)[1] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function is_installed(lang) |
||||||
|
return #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0 |
||||||
|
end |
||||||
|
|
||||||
|
local function needs_update(lang) |
||||||
|
local revision = get_revision(lang) |
||||||
|
return not revision or revision ~= get_installed_revision(lang) |
||||||
|
end |
||||||
|
|
||||||
|
local function outdated_parsers() |
||||||
|
return vim.tbl_filter(function(lang) |
||||||
|
return needs_update(lang) |
||||||
|
end, info.installed_parsers()) |
||||||
|
end |
||||||
|
|
||||||
|
local function onread(handle, is_stderr) |
||||||
|
return function(err, data) |
||||||
|
if data then |
||||||
|
if is_stderr then |
||||||
|
complete_error_output[handle] = (complete_error_output[handle] or "") .. data |
||||||
|
else |
||||||
|
complete_std_output[handle] = (complete_std_output[handle] or "") .. data |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.iter_cmd(cmd_list, i, lang, success_message) |
||||||
|
if i == 1 then |
||||||
|
started_commands = started_commands + 1 |
||||||
|
end |
||||||
|
if i == #cmd_list + 1 then |
||||||
|
finished_commands = finished_commands + 1 |
||||||
|
return print(get_job_status() .. " " .. success_message) |
||||||
|
end |
||||||
|
|
||||||
|
local attr = cmd_list[i] |
||||||
|
if attr.info then |
||||||
|
print(get_job_status() .. " " .. attr.info) |
||||||
|
end |
||||||
|
|
||||||
|
if attr.opts and attr.opts.args and M.command_extra_args[attr.cmd] then |
||||||
|
vim.list_extend(attr.opts.args, M.command_extra_args[attr.cmd]) |
||||||
|
end |
||||||
|
|
||||||
|
if type(attr.cmd) == "function" then |
||||||
|
local ok, err = pcall(attr.cmd) |
||||||
|
if ok then |
||||||
|
M.iter_cmd(cmd_list, i + 1, lang, success_message) |
||||||
|
else |
||||||
|
failed_commands = failed_commands + 1 |
||||||
|
finished_commands = finished_commands + 1 |
||||||
|
return api.nvim_err_writeln( |
||||||
|
(attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) .. "\n" .. vim.inspect(err) |
||||||
|
) |
||||||
|
end |
||||||
|
else |
||||||
|
local handle |
||||||
|
local stdout = luv.new_pipe(false) |
||||||
|
local stderr = luv.new_pipe(false) |
||||||
|
attr.opts.stdio = { nil, stdout, stderr } |
||||||
|
handle = luv.spawn( |
||||||
|
attr.cmd, |
||||||
|
attr.opts, |
||||||
|
vim.schedule_wrap(function(code) |
||||||
|
if code ~= 0 then |
||||||
|
stdout:read_stop() |
||||||
|
stderr:read_stop() |
||||||
|
end |
||||||
|
stdout:close() |
||||||
|
stderr:close() |
||||||
|
handle:close() |
||||||
|
if code ~= 0 then |
||||||
|
failed_commands = failed_commands + 1 |
||||||
|
finished_commands = finished_commands + 1 |
||||||
|
if complete_std_output[handle] and complete_std_output[handle] ~= "" then |
||||||
|
print(complete_std_output[handle]) |
||||||
|
end |
||||||
|
|
||||||
|
local err_msg = complete_error_output[handle] or "" |
||||||
|
api.nvim_err_writeln( |
||||||
|
"nvim-treesitter[" |
||||||
|
.. lang |
||||||
|
.. "]: " |
||||||
|
.. (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) |
||||||
|
.. "\n" |
||||||
|
.. err_msg |
||||||
|
) |
||||||
|
return |
||||||
|
end |
||||||
|
M.iter_cmd(cmd_list, i + 1, lang, success_message) |
||||||
|
end) |
||||||
|
) |
||||||
|
luv.read_start(stdout, onread(handle, false)) |
||||||
|
luv.read_start(stderr, onread(handle, true)) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function get_command(cmd) |
||||||
|
local options = "" |
||||||
|
if cmd.opts and cmd.opts.args then |
||||||
|
if M.command_extra_args[cmd.cmd] then |
||||||
|
vim.list_extend(cmd.opts.args, M.command_extra_args[cmd.cmd]) |
||||||
|
end |
||||||
|
for _, opt in ipairs(cmd.opts.args) do |
||||||
|
options = string.format("%s %s", options, opt) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local final = string.format("%s %s", cmd.cmd, options) |
||||||
|
if cmd.opts and cmd.opts.cwd then |
||||||
|
final = shell.make_directory_change_for_command(cmd.opts.cwd, final) |
||||||
|
end |
||||||
|
return final |
||||||
|
end |
||||||
|
|
||||||
|
local function iter_cmd_sync(cmd_list) |
||||||
|
for _, cmd in ipairs(cmd_list) do |
||||||
|
if cmd.info then |
||||||
|
print(cmd.info) |
||||||
|
end |
||||||
|
|
||||||
|
if type(cmd.cmd) == "function" then |
||||||
|
cmd.cmd() |
||||||
|
else |
||||||
|
local ret = vim.fn.system(get_command(cmd)) |
||||||
|
if vim.v.shell_error ~= 0 then |
||||||
|
print(ret) |
||||||
|
api.nvim_err_writeln( |
||||||
|
(cmd.err and cmd.err .. "\n" or "") .. "Failed to execute the following command:\n" .. vim.inspect(cmd) |
||||||
|
) |
||||||
|
return false |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
local function run_install(cache_folder, install_folder, lang, repo, with_sync, generate_from_grammar) |
||||||
|
parsers.reset_cache() |
||||||
|
|
||||||
|
local path_sep = utils.get_path_sep() |
||||||
|
|
||||||
|
local project_name = "tree-sitter-" .. lang |
||||||
|
local maybe_local_path = vim.fn.expand(repo.url) |
||||||
|
local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1 |
||||||
|
if from_local_path then |
||||||
|
repo.url = maybe_local_path |
||||||
|
end |
||||||
|
|
||||||
|
-- compile_location only needed for typescript installs. |
||||||
|
local compile_location |
||||||
|
if from_local_path then |
||||||
|
compile_location = repo.url |
||||||
|
else |
||||||
|
local repo_location = string.gsub(repo.location or project_name, "/", path_sep) |
||||||
|
compile_location = cache_folder .. path_sep .. repo_location |
||||||
|
end |
||||||
|
local parser_lib_name = install_folder .. path_sep .. lang .. ".so" |
||||||
|
|
||||||
|
generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar |
||||||
|
|
||||||
|
if generate_from_grammar and vim.fn.executable "tree-sitter" ~= 1 then |
||||||
|
api.nvim_err_writeln "tree-sitter CLI not found: `tree-sitter` is not executable!" |
||||||
|
if repo.requires_generate_from_grammar then |
||||||
|
api.nvim_err_writeln( |
||||||
|
"tree-sitter CLI is needed because `" |
||||||
|
.. lang |
||||||
|
.. "` is marked that it needs " |
||||||
|
.. "to be generated from the grammar definitions to be compatible with nvim!" |
||||||
|
) |
||||||
|
end |
||||||
|
return |
||||||
|
else |
||||||
|
if not M.ts_generate_args then |
||||||
|
local ts_cli_version = utils.ts_cli_version() |
||||||
|
if ts_cli_version and vim.split(ts_cli_version, " ")[1] > "0.20.2" then |
||||||
|
M.ts_generate_args = { "generate", "--abi", vim.treesitter.language_version } |
||||||
|
else |
||||||
|
M.ts_generate_args = { "generate" } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
if generate_from_grammar and vim.fn.executable "node" ~= 1 then |
||||||
|
api.nvim_err_writeln "Node JS not found: `node` is not executable!" |
||||||
|
return |
||||||
|
end |
||||||
|
local cc = shell.select_executable(M.compilers) |
||||||
|
if not cc then |
||||||
|
api.nvim_err_writeln('No C compiler found! "' .. table.concat( |
||||||
|
vim.tbl_filter(function(c) |
||||||
|
return type(c) == "string" |
||||||
|
end, M.compilers), |
||||||
|
'", "' |
||||||
|
) .. '" are not executable.') |
||||||
|
return |
||||||
|
end |
||||||
|
local revision = configs.get_update_strategy() == "lockfile" and get_revision(lang) |
||||||
|
|
||||||
|
local command_list = {} |
||||||
|
if not from_local_path then |
||||||
|
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) |
||||||
|
vim.list_extend( |
||||||
|
command_list, |
||||||
|
shell.select_download_commands(repo, project_name, cache_folder, revision, M.prefer_git) |
||||||
|
) |
||||||
|
end |
||||||
|
if generate_from_grammar then |
||||||
|
if repo.generate_requires_npm then |
||||||
|
if vim.fn.executable "npm" ~= 1 then |
||||||
|
api.nvim_err_writeln("`" .. lang .. "` requires NPM to be installed from grammar.js") |
||||||
|
return |
||||||
|
end |
||||||
|
vim.list_extend(command_list, { |
||||||
|
{ |
||||||
|
cmd = "npm", |
||||||
|
info = "Installing NPM dependencies of " .. lang .. " parser", |
||||||
|
err = "Error during `npm install` (required for parser generation of " .. lang .. " with npm dependencies)", |
||||||
|
opts = { |
||||||
|
args = { "install" }, |
||||||
|
cwd = compile_location, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}) |
||||||
|
end |
||||||
|
vim.list_extend(command_list, { |
||||||
|
{ |
||||||
|
cmd = vim.fn.exepath "tree-sitter", |
||||||
|
info = "Generating source files from grammar.js...", |
||||||
|
err = 'Error during "tree-sitter generate"', |
||||||
|
opts = { |
||||||
|
args = M.ts_generate_args, |
||||||
|
cwd = compile_location, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}) |
||||||
|
end |
||||||
|
vim.list_extend(command_list, { |
||||||
|
shell.select_compile_command(repo, cc, compile_location), |
||||||
|
shell.select_mv_cmd("parser.so", parser_lib_name, compile_location), |
||||||
|
{ |
||||||
|
cmd = function() |
||||||
|
vim.fn.writefile({ revision or "" }, utils.join_path(utils.get_parser_info_dir(), lang .. ".revision")) |
||||||
|
end, |
||||||
|
}, |
||||||
|
{ -- auto-attach modules after installation |
||||||
|
cmd = function() |
||||||
|
for _, buf in ipairs(vim.api.nvim_list_bufs()) do |
||||||
|
if parsers.get_buf_lang(buf) == lang then |
||||||
|
for _, mod in ipairs(require("nvim-treesitter.configs").available_modules()) do |
||||||
|
require("nvim-treesitter.configs").reattach_module(mod, buf) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end, |
||||||
|
}, |
||||||
|
}) |
||||||
|
if not from_local_path then |
||||||
|
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) |
||||||
|
end |
||||||
|
|
||||||
|
if with_sync then |
||||||
|
if iter_cmd_sync(command_list) == true then |
||||||
|
print("Treesitter parser for " .. lang .. " has been installed") |
||||||
|
end |
||||||
|
else |
||||||
|
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function install_lang(lang, ask_reinstall, cache_folder, install_folder, with_sync, generate_from_grammar) |
||||||
|
if is_installed(lang) and ask_reinstall ~= "force" then |
||||||
|
if not ask_reinstall then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local yesno = fn.input(lang .. " parser already available: would you like to reinstall ? y/n: ") |
||||||
|
print "\n " |
||||||
|
if not string.match(yesno, "^y.*") then |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local install_info = get_parser_install_info(lang, true) |
||||||
|
|
||||||
|
run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar) |
||||||
|
end |
||||||
|
|
||||||
|
local function install(options) |
||||||
|
options = options or {} |
||||||
|
local with_sync = options.with_sync |
||||||
|
local ask_reinstall = options.ask_reinstall |
||||||
|
local generate_from_grammar = options.generate_from_grammar |
||||||
|
local exclude_configured_parsers = options.exclude_configured_parsers |
||||||
|
|
||||||
|
return function(...) |
||||||
|
if fn.executable "git" == 0 then |
||||||
|
return api.nvim_err_writeln "Git is required on your system to run this command" |
||||||
|
end |
||||||
|
|
||||||
|
local cache_folder, err = utils.get_cache_dir() |
||||||
|
if err then |
||||||
|
return api.nvim_err_writeln(err) |
||||||
|
end |
||||||
|
|
||||||
|
local install_folder, err = utils.get_parser_install_dir() |
||||||
|
if err then |
||||||
|
return api.nvim_err_writeln(err) |
||||||
|
end |
||||||
|
|
||||||
|
local languages |
||||||
|
local ask |
||||||
|
if ... == "all" then |
||||||
|
languages = parsers.available_parsers() |
||||||
|
ask = false |
||||||
|
elseif ... == "maintained" then |
||||||
|
languages = parsers.maintained_parsers() |
||||||
|
ask = false |
||||||
|
else |
||||||
|
languages = vim.tbl_flatten { ... } |
||||||
|
ask = ask_reinstall |
||||||
|
end |
||||||
|
|
||||||
|
if exclude_configured_parsers then |
||||||
|
languages = utils.difference(languages, configs.get_ignored_parser_installs()) |
||||||
|
end |
||||||
|
|
||||||
|
if #languages > 1 then |
||||||
|
reset_progress_counter() |
||||||
|
end |
||||||
|
|
||||||
|
for _, lang in ipairs(languages) do |
||||||
|
install_lang(lang, ask, cache_folder, install_folder, with_sync, generate_from_grammar) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.update(options) |
||||||
|
options = options or {} |
||||||
|
return function(...) |
||||||
|
M.lockfile = {} |
||||||
|
reset_progress_counter() |
||||||
|
if ... and ... ~= "all" then |
||||||
|
local languages = vim.tbl_flatten { ... } |
||||||
|
local installed = 0 |
||||||
|
for _, lang in ipairs(languages) do |
||||||
|
if (not is_installed(lang)) or (needs_update(lang)) then |
||||||
|
installed = installed + 1 |
||||||
|
install { |
||||||
|
ask_reinstall = "force", |
||||||
|
with_sync = options.with_sync, |
||||||
|
}(lang) |
||||||
|
end |
||||||
|
end |
||||||
|
if installed == 0 then |
||||||
|
utils.notify "Parsers are up-to-date!" |
||||||
|
end |
||||||
|
else |
||||||
|
local parsers_to_update = configs.get_update_strategy() == "lockfile" and outdated_parsers() |
||||||
|
or info.installed_parsers() |
||||||
|
if #parsers_to_update == 0 then |
||||||
|
utils.notify "All parsers are up-to-date!" |
||||||
|
end |
||||||
|
for _, lang in pairs(parsers_to_update) do |
||||||
|
install { |
||||||
|
ask_reinstall = "force", |
||||||
|
exclude_configured_parsers = true, |
||||||
|
with_sync = options.with_sync, |
||||||
|
}(lang) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.uninstall(...) |
||||||
|
local path_sep = "/" |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
path_sep = "\\" |
||||||
|
end |
||||||
|
|
||||||
|
if vim.tbl_contains({ "all", "maintained" }, ...) then |
||||||
|
reset_progress_counter() |
||||||
|
local installed = info.installed_parsers() |
||||||
|
if ... == "maintained" then |
||||||
|
local maintained = parsers.maintained_parsers() |
||||||
|
installed = vim.tbl_filter(function(l) |
||||||
|
return vim.tbl_contains(maintained, l) |
||||||
|
end, installed) |
||||||
|
end |
||||||
|
for _, langitem in pairs(installed) do |
||||||
|
M.uninstall(langitem) |
||||||
|
end |
||||||
|
elseif ... then |
||||||
|
local languages = vim.tbl_flatten { ... } |
||||||
|
for _, lang in ipairs(languages) do |
||||||
|
local install_dir, err = utils.get_parser_install_dir() |
||||||
|
if err then |
||||||
|
return api.nvim_err_writeln(err) |
||||||
|
end |
||||||
|
|
||||||
|
local parser_lib = install_dir .. path_sep .. lang .. ".so" |
||||||
|
|
||||||
|
local command_list = { |
||||||
|
shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang), |
||||||
|
} |
||||||
|
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled") |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.write_lockfile(verbose, skip_langs) |
||||||
|
local sorted_parsers = {} |
||||||
|
-- Load previous lockfile |
||||||
|
load_lockfile() |
||||||
|
skip_langs = skip_langs or {} |
||||||
|
|
||||||
|
for k, v in pairs(parsers.get_parser_configs()) do |
||||||
|
table.insert(sorted_parsers, { name = k, parser = v }) |
||||||
|
end |
||||||
|
|
||||||
|
table.sort(sorted_parsers, function(a, b) |
||||||
|
return a.name < b.name |
||||||
|
end) |
||||||
|
|
||||||
|
for _, v in ipairs(sorted_parsers) do |
||||||
|
if not vim.tbl_contains(skip_langs, v.name) then |
||||||
|
-- I'm sure this can be done in aync way with iter_cmd |
||||||
|
local sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1] |
||||||
|
lockfile[v.name] = { revision = sha } |
||||||
|
if verbose then |
||||||
|
print(v.name .. ": " .. sha) |
||||||
|
end |
||||||
|
else |
||||||
|
print("Skipping " .. v.name) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if verbose then |
||||||
|
print(vim.inspect(lockfile)) |
||||||
|
end |
||||||
|
vim.fn.writefile( |
||||||
|
vim.fn.split(vim.fn.json_encode(lockfile), "\n"), |
||||||
|
utils.join_path(utils.get_package_path(), "lockfile.json") |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
M.ensure_installed = install { exclude_configured_parsers = true } |
||||||
|
M.ensure_installed_sync = install { with_sync = true, exclude_configured_parsers = true } |
||||||
|
|
||||||
|
M.commands = { |
||||||
|
TSInstall = { |
||||||
|
run = install { ask_reinstall = true }, |
||||||
|
["run!"] = install { ask_reinstall = "force" }, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-bang", |
||||||
|
"-complete=custom,nvim_treesitter#installable_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSInstallFromGrammar = { |
||||||
|
run = install { generate_from_grammar = true, ask_reinstall = true }, |
||||||
|
["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" }, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-bang", |
||||||
|
"-complete=custom,nvim_treesitter#installable_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSInstallSync = { |
||||||
|
run = install { with_sync = true, ask_reinstall = true }, |
||||||
|
["run!"] = install { with_sync = true, ask_reinstall = "force" }, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-bang", |
||||||
|
"-complete=custom,nvim_treesitter#installable_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSUpdate = { |
||||||
|
run = M.update {}, |
||||||
|
args = { |
||||||
|
"-nargs=*", |
||||||
|
"-complete=custom,nvim_treesitter#installed_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSUpdateSync = { |
||||||
|
run = M.update { with_sync = true }, |
||||||
|
args = { |
||||||
|
"-nargs=*", |
||||||
|
"-complete=custom,nvim_treesitter#installed_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
TSUninstall = { |
||||||
|
run = M.uninstall, |
||||||
|
args = { |
||||||
|
"-nargs=+", |
||||||
|
"-complete=custom,nvim_treesitter#installed_parsers", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,349 @@ |
|||||||
|
-- Functions to handle locals |
||||||
|
-- Locals are a generalization of definition and scopes |
||||||
|
-- its the way nvim-treesitter uses to "understand" the code |
||||||
|
|
||||||
|
local queries = require "nvim-treesitter.query" |
||||||
|
local ts_utils = require "nvim-treesitter.ts_utils" |
||||||
|
local api = vim.api |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
function M.collect_locals(bufnr) |
||||||
|
return queries.collect_group_results(bufnr, "locals") |
||||||
|
end |
||||||
|
|
||||||
|
-- Iterates matches from a locals query file. |
||||||
|
-- @param bufnr the buffer |
||||||
|
-- @param root the root node |
||||||
|
function M.iter_locals(bufnr, root) |
||||||
|
return queries.iter_group_results(bufnr, "locals", root) |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_locals(bufnr) |
||||||
|
return queries.get_matches(bufnr, "locals") |
||||||
|
end |
||||||
|
|
||||||
|
--- Creates unique id for a node based on text and range |
||||||
|
-- @param scope: the scope node of the definition |
||||||
|
-- @param bufnr: the buffer |
||||||
|
-- @param node_text: the node text to use |
||||||
|
-- @returns a string id |
||||||
|
function M.get_definition_id(scope, node_text) |
||||||
|
-- Add a valid starting character in case node text doesn't start with a valid one. |
||||||
|
return table.concat({ "k", node_text or "", scope:range() }, "_") |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_definitions(bufnr) |
||||||
|
local locals = M.get_locals(bufnr) |
||||||
|
|
||||||
|
local defs = {} |
||||||
|
|
||||||
|
for _, loc in ipairs(locals) do |
||||||
|
if loc.definition then |
||||||
|
table.insert(defs, loc.definition) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return defs |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_scopes(bufnr) |
||||||
|
local locals = M.get_locals(bufnr) |
||||||
|
|
||||||
|
local scopes = {} |
||||||
|
|
||||||
|
for _, loc in ipairs(locals) do |
||||||
|
if loc.scope and loc.scope.node then |
||||||
|
table.insert(scopes, loc.scope.node) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return scopes |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_references(bufnr) |
||||||
|
local locals = M.get_locals(bufnr) |
||||||
|
|
||||||
|
local refs = {} |
||||||
|
|
||||||
|
for _, loc in ipairs(locals) do |
||||||
|
if loc.reference and loc.reference.node then |
||||||
|
table.insert(refs, loc.reference.node) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return refs |
||||||
|
end |
||||||
|
|
||||||
|
--- Gets a table with all the scopes containing a node |
||||||
|
-- The order is from most specific to least (bottom up) |
||||||
|
function M.get_scope_tree(node, bufnr) |
||||||
|
local scopes = {} |
||||||
|
|
||||||
|
for scope in M.iter_scope_tree(node, bufnr) do |
||||||
|
table.insert(scopes, scope) |
||||||
|
end |
||||||
|
|
||||||
|
return scopes |
||||||
|
end |
||||||
|
|
||||||
|
--- Iterates over a nodes scopes moving from the bottom up |
||||||
|
function M.iter_scope_tree(node, bufnr) |
||||||
|
local last_node = node |
||||||
|
return function() |
||||||
|
if not last_node then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local scope = M.containing_scope(last_node, bufnr, false) or ts_utils.get_root_for_node(node) |
||||||
|
|
||||||
|
last_node = scope:parent() |
||||||
|
|
||||||
|
return scope |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Gets a table of all nodes and their 'kinds' from a locals list |
||||||
|
-- @param local_def the local list result |
||||||
|
-- @returns a list of node entries |
||||||
|
function M.get_local_nodes(local_def) |
||||||
|
local result = {} |
||||||
|
|
||||||
|
M.recurse_local_nodes(local_def, function(def, node, kind) |
||||||
|
table.insert(result, vim.tbl_extend("keep", { kind = kind }, def)) |
||||||
|
end) |
||||||
|
|
||||||
|
return result |
||||||
|
end |
||||||
|
|
||||||
|
-- Recurse locals results until a node is found. |
||||||
|
-- The accumulator function is given |
||||||
|
-- * The table of the node |
||||||
|
-- * The node |
||||||
|
-- * The full definition match `@definition.var.something` -> 'var.something' |
||||||
|
-- * The last definition match `@definition.var.something` -> 'something' |
||||||
|
-- @param The locals result |
||||||
|
-- @param The accumulator function |
||||||
|
-- @param The full match path to append to |
||||||
|
-- @param The last match |
||||||
|
function M.recurse_local_nodes(local_def, accumulator, full_match, last_match) |
||||||
|
if type(local_def) ~= "table" then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
if local_def.node then |
||||||
|
accumulator(local_def, local_def.node, full_match, last_match) |
||||||
|
else |
||||||
|
for match_key, def in pairs(local_def) do |
||||||
|
M.recurse_local_nodes(def, accumulator, full_match and (full_match .. "." .. match_key) or match_key, match_key) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
--- Get a single dimension table to look definition nodes. |
||||||
|
-- Keys are generated by using the range of the containing scope and the text of the definition node. |
||||||
|
-- This makes looking up a definition for a given scope a simple key lookup. |
||||||
|
-- |
||||||
|
-- This is memoized by buffer tick. If the function is called in succession |
||||||
|
-- without the buffer tick changing, then the previous result will be used |
||||||
|
-- since the syntax tree hasn't changed. |
||||||
|
-- |
||||||
|
-- Usage lookups require finding the definition of the node, so `find_definition` |
||||||
|
-- is called very frequently, which is why this lookup must be fast as possible. |
||||||
|
-- |
||||||
|
-- @param bufnr: the buffer |
||||||
|
-- @returns a table for looking up definitions |
||||||
|
M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr) |
||||||
|
local definitions = M.get_definitions(bufnr) |
||||||
|
local result = {} |
||||||
|
|
||||||
|
for _, definition in ipairs(definitions) do |
||||||
|
for _, node_entry in ipairs(M.get_local_nodes(definition)) do |
||||||
|
local scopes = M.get_definition_scopes(node_entry.node, bufnr, node_entry.scope) |
||||||
|
-- Always use the highest valid scope |
||||||
|
local scope = scopes[#scopes] |
||||||
|
local node_text = ts_utils.get_node_text(node_entry.node, bufnr)[1] |
||||||
|
local id = M.get_definition_id(scope, node_text) |
||||||
|
|
||||||
|
result[id] = node_entry |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return result |
||||||
|
end) |
||||||
|
|
||||||
|
--- Gets all the scopes of a definition based on the scope type |
||||||
|
-- Scope types can be |
||||||
|
-- |
||||||
|
-- "parent": Uses the parent of the containing scope, basically, skipping a scope |
||||||
|
-- "global": Uses the top most scope |
||||||
|
-- "local": Uses the containing scope of the definition. This is the default |
||||||
|
-- |
||||||
|
-- @param node: the definition node |
||||||
|
-- @param bufnr: the buffer |
||||||
|
-- @param scope_type: the scope type |
||||||
|
function M.get_definition_scopes(node, bufnr, scope_type) |
||||||
|
local scopes = {} |
||||||
|
local scope_count = 1 |
||||||
|
|
||||||
|
-- Definition is valid for the containing scope |
||||||
|
-- and the containing scope of that scope |
||||||
|
if scope_type == "parent" then |
||||||
|
scope_count = 2 |
||||||
|
-- Definition is valid in all parent scopes |
||||||
|
elseif scope_type == "global" then |
||||||
|
scope_count = nil |
||||||
|
end |
||||||
|
|
||||||
|
local i = 0 |
||||||
|
for scope in M.iter_scope_tree(node, bufnr) do |
||||||
|
table.insert(scopes, scope) |
||||||
|
i = i + 1 |
||||||
|
|
||||||
|
if scope_count and i >= scope_count then |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return scopes |
||||||
|
end |
||||||
|
|
||||||
|
function M.find_definition(node, bufnr) |
||||||
|
local def_lookup = M.get_definitions_lookup_table(bufnr) |
||||||
|
local node_text = ts_utils.get_node_text(node, bufnr)[1] |
||||||
|
|
||||||
|
for scope in M.iter_scope_tree(node, bufnr) do |
||||||
|
local id = M.get_definition_id(scope, node_text) |
||||||
|
|
||||||
|
if def_lookup[id] then |
||||||
|
local entry = def_lookup[id] |
||||||
|
|
||||||
|
return entry.node, scope, entry.kind |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return node, ts_utils.get_root_for_node(node), nil |
||||||
|
end |
||||||
|
|
||||||
|
-- Finds usages of a node in a given scope. |
||||||
|
-- @param node the node to find usages for |
||||||
|
-- @param scope_node the node to look within |
||||||
|
-- @returns a list of nodes |
||||||
|
function M.find_usages(node, scope_node, bufnr) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local node_text = ts_utils.get_node_text(node, bufnr)[1] |
||||||
|
|
||||||
|
if not node_text or #node_text < 1 then |
||||||
|
return {} |
||||||
|
end |
||||||
|
|
||||||
|
local scope_node = scope_node or ts_utils.get_root_for_node(node) |
||||||
|
local usages = {} |
||||||
|
|
||||||
|
for match in M.iter_locals(bufnr, scope_node) do |
||||||
|
if |
||||||
|
match.reference |
||||||
|
and match.reference.node |
||||||
|
and ts_utils.get_node_text(match.reference.node, bufnr)[1] == node_text |
||||||
|
then |
||||||
|
local def_node, _, kind = M.find_definition(match.reference.node, bufnr) |
||||||
|
|
||||||
|
if kind == nil or def_node == node then |
||||||
|
table.insert(usages, match.reference.node) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return usages |
||||||
|
end |
||||||
|
|
||||||
|
function M.containing_scope(node, bufnr, allow_scope) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local allow_scope = allow_scope == nil or allow_scope == true |
||||||
|
|
||||||
|
local scopes = M.get_scopes(bufnr) |
||||||
|
if not node or not scopes then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local iter_node = node |
||||||
|
|
||||||
|
while iter_node ~= nil and not vim.tbl_contains(scopes, iter_node) do |
||||||
|
iter_node = iter_node:parent() |
||||||
|
end |
||||||
|
|
||||||
|
return iter_node or (allow_scope and node or nil) |
||||||
|
end |
||||||
|
|
||||||
|
function M.nested_scope(node, cursor_pos) |
||||||
|
local bufnr = api.nvim_get_current_buf() |
||||||
|
|
||||||
|
local scopes = M.get_scopes(bufnr) |
||||||
|
if not node or not scopes then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local row = cursor_pos.row |
||||||
|
local col = cursor_pos.col |
||||||
|
local scope = M.containing_scope(node) |
||||||
|
|
||||||
|
for _, child in ipairs(ts_utils.get_named_children(scope)) do |
||||||
|
local row_, col_ = child:start() |
||||||
|
if vim.tbl_contains(scopes, child) and ((row_ + 1 == row and col_ > col) or row_ + 1 > row) then |
||||||
|
return child |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.next_scope(node) |
||||||
|
local bufnr = api.nvim_get_current_buf() |
||||||
|
|
||||||
|
local scopes = M.get_scopes(bufnr) |
||||||
|
if not node or not scopes then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local scope = M.containing_scope(node) |
||||||
|
|
||||||
|
local parent = scope:parent() |
||||||
|
if not parent then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local is_prev = true |
||||||
|
for _, child in ipairs(ts_utils.get_named_children(parent)) do |
||||||
|
if child == scope then |
||||||
|
is_prev = false |
||||||
|
elseif not is_prev and vim.tbl_contains(scopes, child) then |
||||||
|
return child |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.previous_scope(node) |
||||||
|
local bufnr = api.nvim_get_current_buf() |
||||||
|
|
||||||
|
local scopes = M.get_scopes(bufnr) |
||||||
|
if not node or not scopes then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local scope = M.containing_scope(node) |
||||||
|
|
||||||
|
local parent = scope:parent() |
||||||
|
if not parent then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local is_prev = true |
||||||
|
local children = ts_utils.get_named_children(parent) |
||||||
|
for i = #children, 1, -1 do |
||||||
|
if children[i] == scope then |
||||||
|
is_prev = false |
||||||
|
elseif not is_prev and vim.tbl_contains(scopes, children[i]) then |
||||||
|
return children[i] |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,390 @@ |
|||||||
|
local api = vim.api |
||||||
|
local tsq = require "vim.treesitter.query" |
||||||
|
local tsrange = require "nvim-treesitter.tsrange" |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local caching = require "nvim-treesitter.caching" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
local EMPTY_ITER = function() end |
||||||
|
|
||||||
|
M.built_in_query_groups = { "highlights", "locals", "folds", "indents", "injections" } |
||||||
|
|
||||||
|
-- Creates a function that checks whether a given query exists |
||||||
|
-- for a specific language. |
||||||
|
local function get_query_guard(query) |
||||||
|
return function(lang) |
||||||
|
return M.has_query_files(lang, query) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
for _, query in ipairs(M.built_in_query_groups) do |
||||||
|
M["has_" .. query] = get_query_guard(query) |
||||||
|
end |
||||||
|
|
||||||
|
function M.available_query_groups() |
||||||
|
local query_files = api.nvim_get_runtime_file("queries/*/*.scm", true) |
||||||
|
local groups = {} |
||||||
|
for _, f in ipairs(query_files) do |
||||||
|
groups[vim.fn.fnamemodify(f, ":t:r")] = true |
||||||
|
end |
||||||
|
local list = {} |
||||||
|
for k, _ in pairs(groups) do |
||||||
|
table.insert(list, k) |
||||||
|
end |
||||||
|
return list |
||||||
|
end |
||||||
|
|
||||||
|
do |
||||||
|
local query_cache = caching.create_buffer_cache() |
||||||
|
|
||||||
|
local function update_cached_matches(bufnr, changed_tick, query_group) |
||||||
|
query_cache.set(query_group, bufnr, { |
||||||
|
tick = changed_tick, |
||||||
|
cache = M.collect_group_results(bufnr, query_group) or {}, |
||||||
|
}) |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_matches(bufnr, query_group) |
||||||
|
bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
local cached_local = query_cache.get(query_group, bufnr) |
||||||
|
if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then |
||||||
|
update_cached_matches(bufnr, api.nvim_buf_get_changedtick(bufnr), query_group) |
||||||
|
end |
||||||
|
|
||||||
|
return query_cache.get(query_group, bufnr).cache |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function runtime_queries(lang, query_name) |
||||||
|
return api.nvim_get_runtime_file(string.format("queries/%s/%s.scm", lang, query_name), true) or {} |
||||||
|
end |
||||||
|
|
||||||
|
local query_files_cache = {} |
||||||
|
function M.has_query_files(lang, query_name) |
||||||
|
if not query_files_cache[lang] then |
||||||
|
query_files_cache[lang] = {} |
||||||
|
end |
||||||
|
if query_files_cache[lang][query_name] == nil then |
||||||
|
local files = runtime_queries(lang, query_name) |
||||||
|
query_files_cache[lang][query_name] = files and #files > 0 |
||||||
|
end |
||||||
|
return query_files_cache[lang][query_name] |
||||||
|
end |
||||||
|
|
||||||
|
do |
||||||
|
local mt = {} |
||||||
|
mt.__index = function(tbl, key) |
||||||
|
if rawget(tbl, key) == nil then |
||||||
|
rawset(tbl, key, {}) |
||||||
|
end |
||||||
|
return rawget(tbl, key) |
||||||
|
end |
||||||
|
|
||||||
|
-- cache will auto set the table for each lang if it is nil |
||||||
|
local cache = setmetatable({}, mt) |
||||||
|
|
||||||
|
--- Same as `vim.treesitter.query` except will return cached values |
||||||
|
function M.get_query(lang, query_name) |
||||||
|
if cache[lang][query_name] == nil then |
||||||
|
cache[lang][query_name] = tsq.get_query(lang, query_name) |
||||||
|
end |
||||||
|
|
||||||
|
return cache[lang][query_name] |
||||||
|
end |
||||||
|
|
||||||
|
--- Invalidates the query file cache. |
||||||
|
--- If lang and query_name is both present, will reload for only the lang and query_name. |
||||||
|
--- If only lang is present, will reload all query_names for that lang |
||||||
|
--- If none are present, will reload everything |
||||||
|
function M.invalidate_query_cache(lang, query_name) |
||||||
|
if lang and query_name then |
||||||
|
cache[lang][query_name] = nil |
||||||
|
if query_files_cache[lang] then |
||||||
|
query_files_cache[lang][query_name] = nil |
||||||
|
end |
||||||
|
elseif lang and not query_name then |
||||||
|
query_files_cache[lang] = nil |
||||||
|
for query_name, _ in pairs(cache[lang]) do |
||||||
|
M.invalidate_query_cache(lang, query_name) |
||||||
|
end |
||||||
|
elseif not lang and not query_name then |
||||||
|
query_files_cache = {} |
||||||
|
for lang, _ in pairs(cache) do |
||||||
|
for query_name, _ in pairs(cache[lang]) do |
||||||
|
M.invalidate_query_cache(lang, query_name) |
||||||
|
end |
||||||
|
end |
||||||
|
else |
||||||
|
error "Cannot have query_name by itself!" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
--- This function is meant for an autocommand and not to be used. Only use if file is a query file. |
||||||
|
function M.invalidate_query_file(fname) |
||||||
|
local fnamemodify = vim.fn.fnamemodify |
||||||
|
M.invalidate_query_cache(fnamemodify(fname, ":p:h:t"), fnamemodify(fname, ":t:r")) |
||||||
|
end |
||||||
|
|
||||||
|
local function prepare_query(bufnr, query_name, root, root_lang) |
||||||
|
local buf_lang = parsers.get_buf_lang(bufnr) |
||||||
|
|
||||||
|
if not buf_lang then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local parser = parsers.get_parser(bufnr, buf_lang) |
||||||
|
if not parser then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
if not root then |
||||||
|
local first_tree = parser:trees()[1] |
||||||
|
|
||||||
|
if first_tree then |
||||||
|
root = first_tree:root() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if not root then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local range = { root:range() } |
||||||
|
|
||||||
|
if not root_lang then |
||||||
|
local lang_tree = parser:language_for_range(range) |
||||||
|
|
||||||
|
if lang_tree then |
||||||
|
root_lang = lang_tree:lang() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if not root_lang then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local query = M.get_query(root_lang, query_name) |
||||||
|
if not query then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
return query, |
||||||
|
{ |
||||||
|
root = root, |
||||||
|
source = bufnr, |
||||||
|
start = range[1], |
||||||
|
-- The end row is exclusive so we need to add 1 to it. |
||||||
|
stop = range[3] + 1, |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row) |
||||||
|
-- A function that splits a string on '.' |
||||||
|
local function split(string) |
||||||
|
local t = {} |
||||||
|
for str in string.gmatch(string, "([^.]+)") do |
||||||
|
table.insert(t, str) |
||||||
|
end |
||||||
|
|
||||||
|
return t |
||||||
|
end |
||||||
|
-- Given a path (i.e. a List(String)) this functions inserts value at path |
||||||
|
local function insert_to_path(object, path, value) |
||||||
|
local curr_obj = object |
||||||
|
|
||||||
|
for index = 1, (#path - 1) do |
||||||
|
if curr_obj[path[index]] == nil then |
||||||
|
curr_obj[path[index]] = {} |
||||||
|
end |
||||||
|
|
||||||
|
curr_obj = curr_obj[path[index]] |
||||||
|
end |
||||||
|
|
||||||
|
curr_obj[path[#path]] = value |
||||||
|
end |
||||||
|
|
||||||
|
local matches = query:iter_matches(qnode, bufnr, start_row, end_row) |
||||||
|
|
||||||
|
local function iterator() |
||||||
|
local pattern, match = matches() |
||||||
|
if pattern ~= nil then |
||||||
|
local prepared_match = {} |
||||||
|
|
||||||
|
-- Extract capture names from each match |
||||||
|
for id, node in pairs(match) do |
||||||
|
local name = query.captures[id] -- name of the capture in the query |
||||||
|
if name ~= nil then |
||||||
|
local path = split(name .. ".node") |
||||||
|
insert_to_path(prepared_match, path, node) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Add some predicates for testing |
||||||
|
local preds = query.info.patterns[pattern] |
||||||
|
if preds then |
||||||
|
for _, pred in pairs(preds) do |
||||||
|
-- functions |
||||||
|
if pred[1] == "set!" and type(pred[2]) == "string" then |
||||||
|
insert_to_path(prepared_match, split(pred[2]), pred[3]) |
||||||
|
end |
||||||
|
if pred[1] == "make-range!" and type(pred[2]) == "string" and #pred == 4 then |
||||||
|
insert_to_path( |
||||||
|
prepared_match, |
||||||
|
split(pred[2] .. ".node"), |
||||||
|
tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]]) |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return prepared_match |
||||||
|
end |
||||||
|
end |
||||||
|
return iterator |
||||||
|
end |
||||||
|
|
||||||
|
--- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type) |
||||||
|
-- Works like M.get_references or M.get_scopes except you can choose the capture |
||||||
|
-- Can also be a nested capture like @definition.function to get all nodes defining a function. |
||||||
|
-- |
||||||
|
-- @param bufnr the buffer |
||||||
|
-- @param captures a single string or a list of strings |
||||||
|
-- @param query_group the name of query group (highlights or injections for example) |
||||||
|
-- @param root (optional) node from where to start the search |
||||||
|
-- @param lang (optional) the language from where to get the captures. |
||||||
|
-- Root nodes can have several languages. |
||||||
|
function M.get_capture_matches(bufnr, captures, query_group, root, lang) |
||||||
|
if type(captures) == "string" then |
||||||
|
captures = { captures } |
||||||
|
end |
||||||
|
local strip_captures = {} |
||||||
|
for i, capture in ipairs(captures) do |
||||||
|
if not capture:sub(1, 1) == "@" then |
||||||
|
error 'Captures must start with "@"' |
||||||
|
return |
||||||
|
end |
||||||
|
-- Remove leading "@". |
||||||
|
strip_captures[i] = capture:sub(2) |
||||||
|
end |
||||||
|
|
||||||
|
local matches = {} |
||||||
|
for match in M.iter_group_results(bufnr, query_group, root, lang) do |
||||||
|
for _, capture in ipairs(strip_captures) do |
||||||
|
local insert = utils.get_at_path(match, capture) |
||||||
|
if insert then |
||||||
|
table.insert(matches, insert) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
return matches |
||||||
|
end |
||||||
|
|
||||||
|
function M.iter_captures(bufnr, query_name, root, lang) |
||||||
|
local query, params = prepare_query(bufnr, query_name, root, lang) |
||||||
|
if not query then |
||||||
|
return EMPTY_ITER |
||||||
|
end |
||||||
|
|
||||||
|
local iter = query:iter_captures(params.root, params.source, params.start, params.stop) |
||||||
|
|
||||||
|
local function wrapped_iter() |
||||||
|
local id, node, metadata = iter() |
||||||
|
if not id then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local name = query.captures[id] |
||||||
|
if string.sub(name, 1, 1) == "_" then |
||||||
|
return wrapped_iter() |
||||||
|
end |
||||||
|
|
||||||
|
return name, node, metadata |
||||||
|
end |
||||||
|
|
||||||
|
return wrapped_iter |
||||||
|
end |
||||||
|
|
||||||
|
function M.find_best_match(bufnr, capture_string, query_group, filter_predicate, scoring_function, root) |
||||||
|
if string.sub(capture_string, 1, 1) == "@" then |
||||||
|
--remove leading "@" |
||||||
|
capture_string = string.sub(capture_string, 2) |
||||||
|
end |
||||||
|
|
||||||
|
local best |
||||||
|
local best_score |
||||||
|
|
||||||
|
for maybe_match in M.iter_group_results(bufnr, query_group, root) do |
||||||
|
local match = utils.get_at_path(maybe_match, capture_string) |
||||||
|
|
||||||
|
if match and filter_predicate(match) then |
||||||
|
local current_score = scoring_function(match) |
||||||
|
if not best then |
||||||
|
best = match |
||||||
|
best_score = current_score |
||||||
|
end |
||||||
|
if current_score > best_score then |
||||||
|
best = match |
||||||
|
best_score = current_score |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
return best |
||||||
|
end |
||||||
|
|
||||||
|
-- Iterates matches from a query file. |
||||||
|
-- @param bufnr the buffer |
||||||
|
-- @param query_group the query file to use |
||||||
|
-- @param root the root node |
||||||
|
-- @param root the root node lang, if known |
||||||
|
function M.iter_group_results(bufnr, query_group, root, root_lang) |
||||||
|
local query, params = prepare_query(bufnr, query_group, root, root_lang) |
||||||
|
if not query then |
||||||
|
return EMPTY_ITER |
||||||
|
end |
||||||
|
|
||||||
|
return M.iter_prepared_matches(query, params.root, params.source, params.start, params.stop) |
||||||
|
end |
||||||
|
|
||||||
|
function M.collect_group_results(bufnr, query_group, root, lang) |
||||||
|
local matches = {} |
||||||
|
|
||||||
|
for prepared_match in M.iter_group_results(bufnr, query_group, root, lang) do |
||||||
|
table.insert(matches, prepared_match) |
||||||
|
end |
||||||
|
|
||||||
|
return matches |
||||||
|
end |
||||||
|
|
||||||
|
--- Same as get_capture_matches except this will recursively get matches for every language in the tree. |
||||||
|
-- @param bufnr The bufnr |
||||||
|
-- @param capture_or_fn The capture to get. If a function is provided then that |
||||||
|
-- function will be used to resolve both the capture and query argument. |
||||||
|
-- The function can return `nil` to ignore that tree. |
||||||
|
-- @param query_type The query to get the capture from. This is ignore if a function is provided |
||||||
|
-- for the captuer argument. |
||||||
|
function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type) |
||||||
|
local type_fn = type(capture_or_fn) == "function" and capture_or_fn |
||||||
|
or function() |
||||||
|
return capture_or_fn, query_type |
||||||
|
end |
||||||
|
local parser = parsers.get_parser(bufnr) |
||||||
|
local matches = {} |
||||||
|
|
||||||
|
if parser then |
||||||
|
parser:for_each_tree(function(tree, lang_tree) |
||||||
|
local lang = lang_tree:lang() |
||||||
|
local capture, type_ = type_fn(lang, tree, lang_tree) |
||||||
|
|
||||||
|
if capture then |
||||||
|
vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang)) |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
return matches |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,130 @@ |
|||||||
|
local query = require "vim.treesitter.query" |
||||||
|
|
||||||
|
local function error(str) |
||||||
|
vim.api.nvim_err_writeln(str) |
||||||
|
end |
||||||
|
|
||||||
|
local function valid_args(name, pred, count, strict_count) |
||||||
|
local arg_count = #pred - 1 |
||||||
|
|
||||||
|
if strict_count then |
||||||
|
if arg_count ~= count then |
||||||
|
error(string.format("%s must have exactly %d arguments", name, count)) |
||||||
|
return false |
||||||
|
end |
||||||
|
elseif arg_count < count then |
||||||
|
error(string.format("%s must have at least %d arguments", name, count)) |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
query.add_predicate("nth?", function(match, pattern, bufnr, pred) |
||||||
|
if not valid_args("nth?", pred, 2, true) then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local node = match[pred[2]] |
||||||
|
local n = tonumber(pred[3]) |
||||||
|
if node and node:parent() and node:parent():named_child_count() > n then |
||||||
|
return node:parent():named_child(n) == node |
||||||
|
end |
||||||
|
|
||||||
|
return false |
||||||
|
end) |
||||||
|
|
||||||
|
local function has_ancestor(match, pattern, bufnr, pred) |
||||||
|
if not valid_args(pred[1], pred, 2) then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local node = match[pred[2]] |
||||||
|
local ancestor_types = { unpack(pred, 3) } |
||||||
|
if not node then |
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
local just_direct_parent = pred[1]:find("has-parent", 1, true) |
||||||
|
|
||||||
|
node = node:parent() |
||||||
|
while node do |
||||||
|
if vim.tbl_contains(ancestor_types, node:type()) then |
||||||
|
return true |
||||||
|
end |
||||||
|
if just_direct_parent then |
||||||
|
node = nil |
||||||
|
else |
||||||
|
node = node:parent() |
||||||
|
end |
||||||
|
end |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
query.add_predicate("has-ancestor?", has_ancestor) |
||||||
|
|
||||||
|
query.add_predicate("has-parent?", has_ancestor) |
||||||
|
|
||||||
|
query.add_predicate("is?", function(match, pattern, bufnr, pred) |
||||||
|
if not valid_args("is?", pred, 2) then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
-- Avoid circular dependencies |
||||||
|
local locals = require "nvim-treesitter.locals" |
||||||
|
local node = match[pred[2]] |
||||||
|
local types = { unpack(pred, 3) } |
||||||
|
|
||||||
|
if not node then |
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
local _, _, kind = locals.find_definition(node, bufnr) |
||||||
|
|
||||||
|
return vim.tbl_contains(types, kind) |
||||||
|
end) |
||||||
|
|
||||||
|
query.add_predicate("has-type?", function(match, pattern, bufnr, pred) |
||||||
|
if not valid_args(pred[1], pred, 2) then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local node = match[pred[2]] |
||||||
|
local types = { unpack(pred, 3) } |
||||||
|
|
||||||
|
if not node then |
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
return vim.tbl_contains(types, node:type()) |
||||||
|
end) |
||||||
|
|
||||||
|
-- Just avoid some annoying warnings for this directive |
||||||
|
query.add_directive("make-range!", function() end) |
||||||
|
|
||||||
|
query.add_directive("downcase!", function(match, _, bufnr, pred, metadata) |
||||||
|
local text, key, value |
||||||
|
|
||||||
|
if #pred == 3 then |
||||||
|
-- (#downcase! @capture "key") |
||||||
|
key = pred[3] |
||||||
|
value = metadata[pred[2]][key] |
||||||
|
else |
||||||
|
-- (#downcase! "key") |
||||||
|
key = pred[2] |
||||||
|
value = metadata[key] |
||||||
|
end |
||||||
|
|
||||||
|
if type(value) == "string" then |
||||||
|
text = value |
||||||
|
else |
||||||
|
local node = match[value] |
||||||
|
text = query.get_node_text(node, bufnr) or "" |
||||||
|
end |
||||||
|
|
||||||
|
if #pred == 3 then |
||||||
|
metadata[pred[2]][key] = string.lower(text) |
||||||
|
else |
||||||
|
metadata[key] = string.lower(text) |
||||||
|
end |
||||||
|
end) |
@ -0,0 +1,257 @@ |
|||||||
|
local fn = vim.fn |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
function M.select_mkdir_cmd(directory, cwd, info_msg) |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
return { |
||||||
|
cmd = "cmd", |
||||||
|
opts = { |
||||||
|
args = { "/C", "mkdir", directory }, |
||||||
|
cwd = cwd, |
||||||
|
}, |
||||||
|
info = info_msg, |
||||||
|
err = "Could not create " .. directory, |
||||||
|
} |
||||||
|
else |
||||||
|
return { |
||||||
|
cmd = "mkdir", |
||||||
|
opts = { |
||||||
|
args = { directory }, |
||||||
|
cwd = cwd, |
||||||
|
}, |
||||||
|
info = info_msg, |
||||||
|
err = "Could not create " .. directory, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_rm_file_cmd(file, info_msg) |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
return { |
||||||
|
cmd = "cmd", |
||||||
|
opts = { |
||||||
|
args = { "/C", "if", "exist", file, "del", file }, |
||||||
|
}, |
||||||
|
info = info_msg, |
||||||
|
err = "Could not delete " .. file, |
||||||
|
} |
||||||
|
else |
||||||
|
return { |
||||||
|
cmd = "rm", |
||||||
|
opts = { |
||||||
|
args = { file }, |
||||||
|
}, |
||||||
|
info = info_msg, |
||||||
|
err = "Could not delete " .. file, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_executable(executables) |
||||||
|
return vim.tbl_filter(function(c) |
||||||
|
return c ~= vim.NIL and fn.executable(c) == 1 |
||||||
|
end, executables)[1] |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_compiler_args(repo, compiler) |
||||||
|
if string.match(compiler, "cl$") or string.match(compiler, "cl.exe$") then |
||||||
|
return { |
||||||
|
"/Fe:", |
||||||
|
"parser.so", |
||||||
|
"/Isrc", |
||||||
|
repo.files, |
||||||
|
"-Os", |
||||||
|
"/LD", |
||||||
|
} |
||||||
|
elseif string.match(compiler, "zig$") or string.match(compiler, "zig.exe$") then |
||||||
|
return { |
||||||
|
"c++", |
||||||
|
"-o", |
||||||
|
"parser.so", |
||||||
|
repo.files, |
||||||
|
"-lc", |
||||||
|
"-Isrc", |
||||||
|
"-shared", |
||||||
|
"-Os", |
||||||
|
} |
||||||
|
else |
||||||
|
local args = { |
||||||
|
"-o", |
||||||
|
"parser.so", |
||||||
|
"-I./src", |
||||||
|
repo.files, |
||||||
|
"-shared", |
||||||
|
"-Os", |
||||||
|
"-lstdc++", |
||||||
|
} |
||||||
|
if fn.has "win32" == 0 then |
||||||
|
table.insert(args, "-fPIC") |
||||||
|
end |
||||||
|
return args |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_compile_command(repo, cc, compile_location) |
||||||
|
if string.match(cc, "cl$") or string.match(cc, "cl.exe$") or not repo.use_makefile or fn.has "win32" == 1 then |
||||||
|
return { |
||||||
|
cmd = cc, |
||||||
|
info = "Compiling...", |
||||||
|
err = "Error during compilation", |
||||||
|
opts = { |
||||||
|
args = vim.tbl_flatten(M.select_compiler_args(repo, cc)), |
||||||
|
cwd = compile_location, |
||||||
|
}, |
||||||
|
} |
||||||
|
else |
||||||
|
return { |
||||||
|
cmd = "make", |
||||||
|
info = "Compiling...", |
||||||
|
err = "Error during compilation", |
||||||
|
opts = { |
||||||
|
args = { |
||||||
|
"--makefile=" .. utils.join_path(utils.get_package_path(), "scripts", "compile_parsers.makefile"), |
||||||
|
"CC=" .. cc, |
||||||
|
"CXX_STANDARD=" .. repo.cxx_standard, |
||||||
|
}, |
||||||
|
cwd = compile_location, |
||||||
|
}, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_install_rm_cmd(cache_folder, project_name) |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
local dir = cache_folder .. "\\" .. project_name |
||||||
|
return { |
||||||
|
cmd = "cmd", |
||||||
|
opts = { |
||||||
|
args = { "/C", "if", "exist", dir, "rmdir", "/s", "/q", dir }, |
||||||
|
}, |
||||||
|
} |
||||||
|
else |
||||||
|
return { |
||||||
|
cmd = "rm", |
||||||
|
opts = { |
||||||
|
args = { "-rf", cache_folder .. "/" .. project_name }, |
||||||
|
}, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_mv_cmd(from, to, cwd) |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
return { |
||||||
|
cmd = "cmd", |
||||||
|
opts = { |
||||||
|
args = { "/C", "move", "/Y", from, to }, |
||||||
|
cwd = cwd, |
||||||
|
}, |
||||||
|
} |
||||||
|
else |
||||||
|
return { |
||||||
|
cmd = "mv", |
||||||
|
opts = { |
||||||
|
args = { from, to }, |
||||||
|
cwd = cwd, |
||||||
|
}, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.select_download_commands(repo, project_name, cache_folder, revision, prefer_git) |
||||||
|
local can_use_tar = vim.fn.executable "tar" == 1 and vim.fn.executable "curl" == 1 |
||||||
|
local is_github = repo.url:find("github.com", 1, true) |
||||||
|
local is_gitlab = repo.url:find("gitlab.com", 1, true) |
||||||
|
|
||||||
|
revision = revision or repo.branch or "master" |
||||||
|
|
||||||
|
if can_use_tar and (is_github or is_gitlab) and not prefer_git then |
||||||
|
local path_sep = utils.get_path_sep() |
||||||
|
local url = repo.url:gsub(".git$", "") |
||||||
|
|
||||||
|
return { |
||||||
|
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), |
||||||
|
{ |
||||||
|
cmd = "curl", |
||||||
|
info = "Downloading...", |
||||||
|
err = "Error during download, please verify your internet connection", |
||||||
|
opts = { |
||||||
|
args = { |
||||||
|
"--silent", |
||||||
|
"-L", -- follow redirects |
||||||
|
is_github and url .. "/archive/" .. revision .. ".tar.gz" |
||||||
|
or url .. "/-/archive/" .. revision .. "/" .. project_name .. "-" .. revision .. ".tar.gz", |
||||||
|
"--output", |
||||||
|
project_name .. ".tar.gz", |
||||||
|
}, |
||||||
|
cwd = cache_folder, |
||||||
|
}, |
||||||
|
}, |
||||||
|
M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"), |
||||||
|
{ |
||||||
|
cmd = "tar", |
||||||
|
info = "Extracting...", |
||||||
|
err = "Error during tarball extraction.", |
||||||
|
opts = { |
||||||
|
args = { |
||||||
|
"-xvzf", |
||||||
|
project_name .. ".tar.gz", |
||||||
|
"-C", |
||||||
|
project_name .. "-tmp", |
||||||
|
}, |
||||||
|
cwd = cache_folder, |
||||||
|
}, |
||||||
|
}, |
||||||
|
M.select_rm_file_cmd(cache_folder .. path_sep .. project_name .. ".tar.gz"), |
||||||
|
M.select_mv_cmd( |
||||||
|
utils.join_path(project_name .. "-tmp", url:match "[^/]-$" .. "-" .. revision), |
||||||
|
project_name, |
||||||
|
cache_folder |
||||||
|
), |
||||||
|
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), |
||||||
|
} |
||||||
|
else |
||||||
|
local git_folder = utils.join_path(cache_folder, project_name) |
||||||
|
local clone_error = "Error during download, please verify your internet connection" |
||||||
|
|
||||||
|
return { |
||||||
|
{ |
||||||
|
cmd = "git", |
||||||
|
info = "Downloading...", |
||||||
|
err = clone_error, |
||||||
|
opts = { |
||||||
|
args = { |
||||||
|
"clone", |
||||||
|
repo.url, |
||||||
|
project_name, |
||||||
|
}, |
||||||
|
cwd = cache_folder, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
cmd = "git", |
||||||
|
info = "Checking out locked revision", |
||||||
|
err = "Error while checking out revision", |
||||||
|
opts = { |
||||||
|
args = { |
||||||
|
"checkout", |
||||||
|
revision, |
||||||
|
}, |
||||||
|
cwd = git_folder, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.make_directory_change_for_command(dir, command) |
||||||
|
if fn.has "win32" == 1 then |
||||||
|
return string.format("pushd %s & %s & popd", dir, command) |
||||||
|
else |
||||||
|
return string.format("cd %s;\n %s", dir, command) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,392 @@ |
|||||||
|
local api = vim.api |
||||||
|
|
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
local utils = require "nvim-treesitter.utils" |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
--- Gets the actual text content of a node |
||||||
|
-- @param node the node to get the text from |
||||||
|
-- @param bufnr the buffer containing the node |
||||||
|
-- @return list of lines of text of the node |
||||||
|
function M.get_node_text(node, bufnr) |
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf() |
||||||
|
if not node then |
||||||
|
return {} |
||||||
|
end |
||||||
|
|
||||||
|
-- We have to remember that end_col is end-exclusive |
||||||
|
local start_row, start_col, end_row, end_col = M.get_node_range(node) |
||||||
|
|
||||||
|
if start_row ~= end_row then |
||||||
|
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false) |
||||||
|
lines[1] = string.sub(lines[1], start_col + 1) |
||||||
|
-- end_row might be just after the last line. In this case the last line is not truncated. |
||||||
|
if #lines == end_row - start_row then |
||||||
|
lines[#lines] = string.sub(lines[#lines], 1, end_col) |
||||||
|
end |
||||||
|
return lines |
||||||
|
else |
||||||
|
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1] |
||||||
|
-- If line is nil then the line is empty |
||||||
|
return line and { string.sub(line, start_col + 1, end_col) } or {} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
--- Determines whether a node is the parent of another |
||||||
|
-- @param dest the possible parent |
||||||
|
-- @param source the possible child node |
||||||
|
function M.is_parent(dest, source) |
||||||
|
if not (dest and source) then |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local current = source |
||||||
|
while current ~= nil do |
||||||
|
if current == dest then |
||||||
|
return true |
||||||
|
end |
||||||
|
|
||||||
|
current = current:parent() |
||||||
|
end |
||||||
|
|
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
--- Get next node with same parent |
||||||
|
-- @param node node |
||||||
|
-- @param allow_switch_parents allow switching parents if last node |
||||||
|
-- @param allow_next_parent allow next parent if last node and next parent without children |
||||||
|
function M.get_next_node(node, allow_switch_parents, allow_next_parent) |
||||||
|
local destination_node |
||||||
|
local parent = node:parent() |
||||||
|
|
||||||
|
if not parent then |
||||||
|
return |
||||||
|
end |
||||||
|
local found_pos = 0 |
||||||
|
for i = 0, parent:named_child_count() - 1, 1 do |
||||||
|
if parent:named_child(i) == node then |
||||||
|
found_pos = i |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
if parent:named_child_count() > found_pos + 1 then |
||||||
|
destination_node = parent:named_child(found_pos + 1) |
||||||
|
elseif allow_switch_parents then |
||||||
|
local next_node = M.get_next_node(node:parent()) |
||||||
|
if next_node and next_node:named_child_count() > 0 then |
||||||
|
destination_node = next_node:named_child(0) |
||||||
|
elseif next_node and allow_next_parent then |
||||||
|
destination_node = next_node |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return destination_node |
||||||
|
end |
||||||
|
|
||||||
|
--- Get previous node with same parent |
||||||
|
-- @param node node |
||||||
|
-- @param allow_switch_parents allow switching parents if first node |
||||||
|
-- @param allow_previous_parent allow previous parent if first node and previous parent without children |
||||||
|
function M.get_previous_node(node, allow_switch_parents, allow_previous_parent) |
||||||
|
local destination_node |
||||||
|
local parent = node:parent() |
||||||
|
if not parent then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local found_pos = 0 |
||||||
|
for i = 0, parent:named_child_count() - 1, 1 do |
||||||
|
if parent:named_child(i) == node then |
||||||
|
found_pos = i |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
if 0 < found_pos then |
||||||
|
destination_node = parent:named_child(found_pos - 1) |
||||||
|
elseif allow_switch_parents then |
||||||
|
local previous_node = M.get_previous_node(node:parent()) |
||||||
|
if previous_node and previous_node:named_child_count() > 0 then |
||||||
|
destination_node = previous_node:named_child(previous_node:named_child_count() - 1) |
||||||
|
elseif previous_node and allow_previous_parent then |
||||||
|
destination_node = previous_node |
||||||
|
end |
||||||
|
end |
||||||
|
return destination_node |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_named_children(node) |
||||||
|
local nodes = {} |
||||||
|
for i = 0, node:named_child_count() - 1, 1 do |
||||||
|
nodes[i + 1] = node:named_child(i) |
||||||
|
end |
||||||
|
return nodes |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_node_at_cursor(winnr) |
||||||
|
winnr = winnr or 0 |
||||||
|
local cursor = api.nvim_win_get_cursor(winnr) |
||||||
|
local cursor_range = { cursor[1] - 1, cursor[2] } |
||||||
|
|
||||||
|
local buf = vim.api.nvim_win_get_buf(winnr) |
||||||
|
local root_lang_tree = parsers.get_parser(buf) |
||||||
|
if not root_lang_tree then |
||||||
|
return |
||||||
|
end |
||||||
|
local root = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree) |
||||||
|
|
||||||
|
if not root then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2]) |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_root_for_position(line, col, root_lang_tree) |
||||||
|
if not root_lang_tree then |
||||||
|
if not parsers.has_parser() then |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
root_lang_tree = parsers.get_parser() |
||||||
|
end |
||||||
|
|
||||||
|
local lang_tree = root_lang_tree:language_for_range { line, col, line, col } |
||||||
|
|
||||||
|
for _, tree in ipairs(lang_tree:trees()) do |
||||||
|
local root = tree:root() |
||||||
|
|
||||||
|
if root and M.is_in_node_range(root, line, col) then |
||||||
|
return root, tree, lang_tree |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- This isn't a likely scenario, since the position must belong to a tree somewhere. |
||||||
|
return nil, nil, lang_tree |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_root_for_node(node) |
||||||
|
local parent = node |
||||||
|
local result = node |
||||||
|
|
||||||
|
while parent ~= nil do |
||||||
|
result = parent |
||||||
|
parent = result:parent() |
||||||
|
end |
||||||
|
|
||||||
|
return result |
||||||
|
end |
||||||
|
|
||||||
|
function M.highlight_node(node, buf, hl_namespace, hl_group) |
||||||
|
if not node then |
||||||
|
return |
||||||
|
end |
||||||
|
M.highlight_range({ node:range() }, buf, hl_namespace, hl_group) |
||||||
|
end |
||||||
|
|
||||||
|
--- Get a compatible vim range (1 index based) from a TS node range. |
||||||
|
-- |
||||||
|
-- TS nodes start with 0 and the end col is ending exclusive. |
||||||
|
-- They also treat a EOF/EOL char as a char ending in the first |
||||||
|
-- col of the next row. |
||||||
|
function M.get_vim_range(range, buf) |
||||||
|
local srow, scol, erow, ecol = unpack(range) |
||||||
|
srow = srow + 1 |
||||||
|
scol = scol + 1 |
||||||
|
erow = erow + 1 |
||||||
|
|
||||||
|
if ecol == 0 then |
||||||
|
-- Use the value of the last col of the previous row instead. |
||||||
|
erow = erow - 1 |
||||||
|
if not buf or buf == 0 then |
||||||
|
ecol = vim.fn.col { erow, "$" } - 1 |
||||||
|
else |
||||||
|
ecol = #api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1] |
||||||
|
end |
||||||
|
end |
||||||
|
return srow, scol, erow, ecol |
||||||
|
end |
||||||
|
|
||||||
|
function M.highlight_range(range, buf, hl_namespace, hl_group) |
||||||
|
local start_row, start_col, end_row, end_col = unpack(range) |
||||||
|
vim.highlight.range(buf, hl_namespace, hl_group, { start_row, start_col }, { end_row, end_col }) |
||||||
|
end |
||||||
|
|
||||||
|
-- Set visual selection to node |
||||||
|
-- @param selection_mode One of "charwise" (default) or "v", "linewise" or "V", |
||||||
|
-- "blockwise" or "<C-v>" (as a string with 5 characters or a single character) |
||||||
|
function M.update_selection(buf, node, selection_mode) |
||||||
|
selection_mode = selection_mode or "charwise" |
||||||
|
local start_row, start_col, end_row, end_col = M.get_vim_range({ M.get_node_range(node) }, buf) |
||||||
|
|
||||||
|
vim.fn.setpos(".", { buf, start_row, start_col, 0 }) |
||||||
|
|
||||||
|
-- Start visual selection in appropriate mode |
||||||
|
local v_table = { charwise = "v", linewise = "V", blockwise = "<C-v>" } |
||||||
|
---- Call to `nvim_replace_termcodes()` is needed for sending appropriate |
||||||
|
---- command to enter blockwise mode |
||||||
|
local mode_string = vim.api.nvim_replace_termcodes(v_table[selection_mode] or selection_mode, true, true, true) |
||||||
|
vim.cmd("normal! " .. mode_string) |
||||||
|
vim.fn.setpos(".", { buf, end_row, end_col, 0 }) |
||||||
|
end |
||||||
|
|
||||||
|
-- Byte length of node range |
||||||
|
function M.node_length(node) |
||||||
|
local _, _, start_byte = node:start() |
||||||
|
local _, _, end_byte = node:end_() |
||||||
|
return end_byte - start_byte |
||||||
|
end |
||||||
|
|
||||||
|
--- Determines whether (line, col) position is in node range |
||||||
|
-- @param node Node defining the range |
||||||
|
-- @param line A line (0-based) |
||||||
|
-- @param col A column (0-based) |
||||||
|
function M.is_in_node_range(node, line, col) |
||||||
|
local start_line, start_col, end_line, end_col = node:range() |
||||||
|
if line >= start_line and line <= end_line then |
||||||
|
if line == start_line and line == end_line then |
||||||
|
return col >= start_col and col < end_col |
||||||
|
elseif line == start_line then |
||||||
|
return col >= start_col |
||||||
|
elseif line == end_line then |
||||||
|
return col < end_col |
||||||
|
else |
||||||
|
return true |
||||||
|
end |
||||||
|
else |
||||||
|
return false |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_node_range(node_or_range) |
||||||
|
if type(node_or_range) == "table" then |
||||||
|
return unpack(node_or_range) |
||||||
|
else |
||||||
|
return node_or_range:range() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.node_to_lsp_range(node) |
||||||
|
local start_line, start_col, end_line, end_col = M.get_node_range(node) |
||||||
|
local rtn = {} |
||||||
|
rtn.start = { line = start_line, character = start_col } |
||||||
|
rtn["end"] = { line = end_line, character = end_col } |
||||||
|
return rtn |
||||||
|
end |
||||||
|
|
||||||
|
--- Memoizes a function based on the buffer tick of the provided bufnr. |
||||||
|
-- The cache entry is cleared when the buffer is detached to avoid memory leaks. |
||||||
|
-- @param fn: the fn to memoize, taking the bufnr as first argument |
||||||
|
-- @param options: |
||||||
|
-- - bufnr: extracts a bufnr from the given arguments. |
||||||
|
-- - key: extracts the cache key from the given arguments. |
||||||
|
-- @returns a memoized function |
||||||
|
function M.memoize_by_buf_tick(fn, options) |
||||||
|
options = options or {} |
||||||
|
|
||||||
|
local cache = {} |
||||||
|
local bufnr_fn = utils.to_func(options.bufnr or utils.identity) |
||||||
|
local key_fn = utils.to_func(options.key or utils.identity) |
||||||
|
|
||||||
|
return function(...) |
||||||
|
local bufnr = bufnr_fn(...) |
||||||
|
local key = key_fn(...) |
||||||
|
local tick = api.nvim_buf_get_changedtick(bufnr) |
||||||
|
|
||||||
|
if cache[key] then |
||||||
|
if cache[key].last_tick == tick then |
||||||
|
return cache[key].result |
||||||
|
end |
||||||
|
else |
||||||
|
local function detach_handler() |
||||||
|
cache[key] = nil |
||||||
|
end |
||||||
|
|
||||||
|
-- Clean up logic only! |
||||||
|
api.nvim_buf_attach(bufnr, false, { |
||||||
|
on_detach = detach_handler, |
||||||
|
on_reload = detach_handler, |
||||||
|
}) |
||||||
|
end |
||||||
|
|
||||||
|
cache[key] = { |
||||||
|
result = fn(...), |
||||||
|
last_tick = tick, |
||||||
|
} |
||||||
|
|
||||||
|
return cache[key].result |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second) |
||||||
|
if not node_or_range1 or not node_or_range2 then |
||||||
|
return |
||||||
|
end |
||||||
|
local range1 = M.node_to_lsp_range(node_or_range1) |
||||||
|
local range2 = M.node_to_lsp_range(node_or_range2) |
||||||
|
|
||||||
|
local text1 = M.get_node_text(node_or_range1) |
||||||
|
local text2 = M.get_node_text(node_or_range2) |
||||||
|
|
||||||
|
local edit1 = { range = range1, newText = table.concat(text2, "\n") } |
||||||
|
local edit2 = { range = range2, newText = table.concat(text1, "\n") } |
||||||
|
vim.lsp.util.apply_text_edits({ edit1, edit2 }, bufnr, "utf-8") |
||||||
|
|
||||||
|
if cursor_to_second then |
||||||
|
utils.set_jump() |
||||||
|
|
||||||
|
local char_delta = 0 |
||||||
|
local line_delta = 0 |
||||||
|
if |
||||||
|
range1["end"].line < range2.start.line |
||||||
|
or (range1["end"].line == range2.start.line and range1["end"].character < range2.start.character) |
||||||
|
then |
||||||
|
line_delta = #text2 - #text1 |
||||||
|
end |
||||||
|
|
||||||
|
if range1["end"].line == range2.start.line and range1["end"].character < range2.start.character then |
||||||
|
if line_delta ~= 0 then |
||||||
|
--- why? |
||||||
|
--correction_after_line_change = -range2.start.character |
||||||
|
--text_now_before_range2 = #(text2[#text2]) |
||||||
|
--space_between_ranges = range2.start.character - range1["end"].character |
||||||
|
--char_delta = correction_after_line_change + text_now_before_range2 + space_between_ranges |
||||||
|
--- Equivalent to: |
||||||
|
char_delta = #text2[#text2] - range1["end"].character |
||||||
|
|
||||||
|
-- add range1.start.character if last line of range1 (now text2) does not start at 0 |
||||||
|
if range1.start.line == range2.start.line + line_delta then |
||||||
|
char_delta = char_delta + range1.start.character |
||||||
|
end |
||||||
|
else |
||||||
|
char_delta = #text2[#text2] - #text1[#text1] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
api.nvim_win_set_cursor( |
||||||
|
api.nvim_get_current_win(), |
||||||
|
{ range2.start.line + 1 + line_delta, range2.start.character + char_delta } |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.goto_node(node, goto_end, avoid_set_jump) |
||||||
|
if not node then |
||||||
|
return |
||||||
|
end |
||||||
|
if not avoid_set_jump then |
||||||
|
utils.set_jump() |
||||||
|
end |
||||||
|
local range = { M.get_vim_range { node:range() } } |
||||||
|
local position |
||||||
|
if not goto_end then |
||||||
|
position = { range[1], range[2] } |
||||||
|
else |
||||||
|
position = { range[3], range[4] } |
||||||
|
end |
||||||
|
-- Position is 1, 0 indexed. |
||||||
|
api.nvim_win_set_cursor(0, { position[1], position[2] - 1 }) |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,154 @@ |
|||||||
|
local M = {} |
||||||
|
local TSRange = {} |
||||||
|
TSRange.__index = TSRange |
||||||
|
|
||||||
|
local api = vim.api |
||||||
|
local ts_utils = require "nvim-treesitter.ts_utils" |
||||||
|
local parsers = require "nvim-treesitter.parsers" |
||||||
|
|
||||||
|
local function get_byte_offset(buf, row, col) |
||||||
|
return api.nvim_buf_get_offset(buf, row) + vim.fn.byteidx(api.nvim_buf_get_lines(buf, row, row + 1, false)[1], col) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange.new(buf, start_row, start_col, end_row, end_col) |
||||||
|
return setmetatable({ |
||||||
|
start_pos = { start_row, start_col, get_byte_offset(buf, start_row, start_col) }, |
||||||
|
end_pos = { end_row, end_col, get_byte_offset(buf, end_row, end_col) }, |
||||||
|
buf = buf, |
||||||
|
[1] = start_row, |
||||||
|
[2] = start_col, |
||||||
|
[3] = end_row, |
||||||
|
[4] = end_col, |
||||||
|
}, TSRange) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange.from_nodes(buf, start_node, end_node) |
||||||
|
TSRange.__index = TSRange |
||||||
|
local start_pos = start_node and { start_node:start() } or { end_node:start() } |
||||||
|
local end_pos = end_node and { end_node:end_() } or { start_node:end_() } |
||||||
|
return setmetatable({ |
||||||
|
start_pos = { start_pos[1], start_pos[2], start_pos[3] }, |
||||||
|
end_pos = { end_pos[1], end_pos[2], end_pos[3] }, |
||||||
|
buf = buf, |
||||||
|
[1] = start_pos[1], |
||||||
|
[2] = start_pos[2], |
||||||
|
[3] = end_pos[1], |
||||||
|
[4] = end_pos[2], |
||||||
|
}, TSRange) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange.from_table(buf, range) |
||||||
|
return setmetatable({ |
||||||
|
start_pos = { range[1], range[2], get_byte_offset(buf, range[1], range[2]) }, |
||||||
|
end_pos = { range[3], range[4], get_byte_offset(buf, range[3], range[4]) }, |
||||||
|
buf = buf, |
||||||
|
[1] = range[1], |
||||||
|
[2] = range[2], |
||||||
|
[3] = range[3], |
||||||
|
[4] = range[4], |
||||||
|
}, TSRange) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:parent() |
||||||
|
local root_lang_tree = parsers.get_parser(self.buf) |
||||||
|
local root = ts_utils.get_root_for_position(self[1], self[2], root_lang_tree) |
||||||
|
|
||||||
|
return root |
||||||
|
and root:named_descendant_for_range(self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2]) |
||||||
|
or nil |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:field() end |
||||||
|
|
||||||
|
function TSRange:child_count() |
||||||
|
return #self:collect_children() |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:named_child_count() |
||||||
|
return #self:collect_children(function(c) |
||||||
|
return c:named() |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:iter_children() |
||||||
|
local raw_iterator = self:parent().iter_children() |
||||||
|
return function() |
||||||
|
while true do |
||||||
|
local node = raw_iterator() |
||||||
|
if not node then |
||||||
|
return |
||||||
|
end |
||||||
|
local _, _, start_byte = node:start() |
||||||
|
local _, _, end_byte = node:end_() |
||||||
|
if start_byte >= self.start_pos[3] and end_byte <= self.end_pos[3] then |
||||||
|
return node |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:collect_children(filter_fun) |
||||||
|
local children = {} |
||||||
|
for _, c in self:iter_children() do |
||||||
|
if not filter_fun or filter_fun(c) then |
||||||
|
table.insert(children, c) |
||||||
|
end |
||||||
|
end |
||||||
|
return children |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:child(index) |
||||||
|
return self:collect_children()[index + 1] |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:named_child(index) |
||||||
|
return self:collect_children(function(c) |
||||||
|
return c.named() |
||||||
|
end)[index + 1] |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:start() |
||||||
|
return unpack(self.start_pos) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:end_() |
||||||
|
return unpack(self.end_pos) |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:range() |
||||||
|
return self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2] |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:type() |
||||||
|
return "nvim-treesitter-range" |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:symbol() |
||||||
|
return -1 |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:named() |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:missing() |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:has_error() |
||||||
|
return #self:collect_children(function(c) |
||||||
|
return c:has_error() |
||||||
|
end) > 0 and true or false |
||||||
|
end |
||||||
|
|
||||||
|
function TSRange:sexpr() |
||||||
|
return table.concat( |
||||||
|
vim.tbl_map(function(c) |
||||||
|
return c:sexpr() |
||||||
|
end, self:collect_children()), |
||||||
|
" " |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
M.TSRange = TSRange |
||||||
|
return M |
@ -0,0 +1,205 @@ |
|||||||
|
local api = vim.api |
||||||
|
local fn = vim.fn |
||||||
|
local luv = vim.loop |
||||||
|
|
||||||
|
local M = {} |
||||||
|
|
||||||
|
-- Wrapper around vim.notify with common options set. |
||||||
|
function M.notify(msg, log_level, opts) |
||||||
|
local default_opts = { title = "nvim-treesitter" } |
||||||
|
vim.notify(msg, log_level, vim.tbl_extend("force", default_opts, opts or {})) |
||||||
|
end |
||||||
|
|
||||||
|
function M.setup_commands(mod, commands) |
||||||
|
for command_name, def in pairs(commands) do |
||||||
|
local call_fn = string.format( |
||||||
|
"lua require'nvim-treesitter.%s'.commands.%s['run<bang>'](<f-args>)", |
||||||
|
mod, |
||||||
|
command_name |
||||||
|
) |
||||||
|
local parts = vim.tbl_flatten { |
||||||
|
"command!", |
||||||
|
def.args, |
||||||
|
command_name, |
||||||
|
call_fn, |
||||||
|
} |
||||||
|
api.nvim_command(table.concat(parts, " ")) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_path_sep() |
||||||
|
return fn.has "win32" == 1 and "\\" or "/" |
||||||
|
end |
||||||
|
|
||||||
|
-- Returns a function that joins the given arguments with separator. Arguments |
||||||
|
-- can't be nil. Example: |
||||||
|
--[[ |
||||||
|
print(M.generate_join(" ")("foo", "bar")) |
||||||
|
--]] |
||||||
|
-- prints "foo bar" |
||||||
|
function M.generate_join(separator) |
||||||
|
return function(...) |
||||||
|
return table.concat({ ... }, separator) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
M.join_path = M.generate_join(M.get_path_sep()) |
||||||
|
|
||||||
|
local join_space = M.generate_join " " |
||||||
|
|
||||||
|
function M.get_package_path() |
||||||
|
-- Path to this source file, removing the leading '@' |
||||||
|
local source = string.sub(debug.getinfo(1, "S").source, 2) |
||||||
|
|
||||||
|
-- Path to the package root |
||||||
|
return fn.fnamemodify(source, ":p:h:h:h") |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_cache_dir() |
||||||
|
local cache_dir = fn.stdpath "data" |
||||||
|
|
||||||
|
if luv.fs_access(cache_dir, "RW") then |
||||||
|
return cache_dir |
||||||
|
elseif luv.fs_access("/tmp", "RW") then |
||||||
|
return "/tmp" |
||||||
|
end |
||||||
|
|
||||||
|
return nil, join_space("Invalid cache rights,", fn.stdpath "data", "or /tmp should be read/write") |
||||||
|
end |
||||||
|
|
||||||
|
-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in |
||||||
|
-- runtimepath |
||||||
|
function M.get_site_dir() |
||||||
|
local path_sep = M.get_path_sep() |
||||||
|
return M.join_path(fn.stdpath "data", path_sep, "site") |
||||||
|
end |
||||||
|
|
||||||
|
-- Try the package dir of the nvim-treesitter plugin first, followed by the |
||||||
|
-- "site" dir from "runtimepath". "site" dir will be created if it doesn't |
||||||
|
-- exist. Using only the package dir won't work when the plugin is installed |
||||||
|
-- with Nix, since the "/nix/store" is read-only. |
||||||
|
function M.get_parser_install_dir(folder_name) |
||||||
|
folder_name = folder_name or "parser" |
||||||
|
local package_path = M.get_package_path() |
||||||
|
local package_path_parser_dir = M.join_path(package_path, folder_name) |
||||||
|
|
||||||
|
-- If package_path is read/write, use that |
||||||
|
if luv.fs_access(package_path_parser_dir, "RW") then |
||||||
|
return package_path_parser_dir |
||||||
|
end |
||||||
|
|
||||||
|
local site_dir = M.get_site_dir() |
||||||
|
local path_sep = M.get_path_sep() |
||||||
|
local parser_dir = M.join_path(site_dir, path_sep, folder_name) |
||||||
|
|
||||||
|
-- Try creating and using parser_dir if it doesn't exist |
||||||
|
if not luv.fs_stat(parser_dir) then |
||||||
|
local ok, error = pcall(vim.fn.mkdir, parser_dir, "p", "0755") |
||||||
|
if not ok then |
||||||
|
return nil, join_space("Couldn't create parser dir", parser_dir, ":", error) |
||||||
|
end |
||||||
|
|
||||||
|
return parser_dir |
||||||
|
end |
||||||
|
|
||||||
|
-- parser_dir exists, use it if it's read/write |
||||||
|
if luv.fs_access(parser_dir, "RW") then |
||||||
|
return parser_dir |
||||||
|
end |
||||||
|
|
||||||
|
-- package_path isn't read/write, parser_dir exists but isn't read/write |
||||||
|
-- either, give up |
||||||
|
return nil, join_space("Invalid cache rights,", package_path, "or", parser_dir, "should be read/write") |
||||||
|
end |
||||||
|
|
||||||
|
function M.get_parser_info_dir() |
||||||
|
return M.get_parser_install_dir "parser-info" |
||||||
|
end |
||||||
|
|
||||||
|
-- Gets a property at path |
||||||
|
-- @param tbl the table to access |
||||||
|
-- @param path the '.' separated path |
||||||
|
-- @returns the value at path or nil |
||||||
|
function M.get_at_path(tbl, path) |
||||||
|
if path == "" then |
||||||
|
return tbl |
||||||
|
end |
||||||
|
local segments = vim.split(path, ".", true) |
||||||
|
local result = tbl |
||||||
|
|
||||||
|
for _, segment in ipairs(segments) do |
||||||
|
if type(result) == "table" then |
||||||
|
result = result[segment] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return result |
||||||
|
end |
||||||
|
|
||||||
|
-- Prints a warning message |
||||||
|
-- @param text the text message |
||||||
|
function M.print_warning(text) |
||||||
|
api.nvim_command(string.format([[echohl WarningMsg | echo "%s" | echohl None]], text)) |
||||||
|
end |
||||||
|
|
||||||
|
function M.set_jump() |
||||||
|
vim.cmd "normal! m'" |
||||||
|
end |
||||||
|
|
||||||
|
function M.index_of(tbl, obj) |
||||||
|
for i, o in ipairs(tbl) do |
||||||
|
if o == obj then |
||||||
|
return i |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Filters a list based on the given predicate |
||||||
|
-- @param tbl The list to filter |
||||||
|
-- @param predicate The predicate to filter with |
||||||
|
function M.filter(tbl, predicate) |
||||||
|
local result = {} |
||||||
|
|
||||||
|
for i, v in ipairs(tbl) do |
||||||
|
if predicate(v, i) then |
||||||
|
table.insert(result, v) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return result |
||||||
|
end |
||||||
|
|
||||||
|
-- Returns a list of all values from the first list |
||||||
|
-- that are not present in the second list. |
||||||
|
-- @params tbl1 The first table |
||||||
|
-- @params tbl2 The second table |
||||||
|
function M.difference(tbl1, tbl2) |
||||||
|
return M.filter(tbl1, function(v) |
||||||
|
return not vim.tbl_contains(tbl2, v) |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
function M.identity(a) |
||||||
|
return a |
||||||
|
end |
||||||
|
|
||||||
|
function M.constant(a) |
||||||
|
return function() |
||||||
|
return a |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function M.to_func(a) |
||||||
|
return type(a) == "function" and a or M.constant(a) |
||||||
|
end |
||||||
|
|
||||||
|
function M.ts_cli_version() |
||||||
|
if fn.executable "tree-sitter" == 1 then |
||||||
|
local handle = io.popen "tree-sitter -V" |
||||||
|
local result = handle:read "*a" |
||||||
|
handle:close() |
||||||
|
return vim.split(result, "\n")[1]:match "[^tree%psitter ].*" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return M |
@ -0,0 +1,2 @@ |
|||||||
|
* |
||||||
|
!.gitignore |
@ -0,0 +1,2 @@ |
|||||||
|
* |
||||||
|
!.gitignore |
@ -0,0 +1,96 @@ |
|||||||
|
" Last Change: 2020 Aug 13 |
||||||
|
|
||||||
|
if exists('g:loaded_nvim_treesitter') |
||||||
|
finish |
||||||
|
endif |
||||||
|
|
||||||
|
augroup NvimTreesitter |
||||||
|
" on every query file write we want to set an autocommand that will reload the cache |
||||||
|
autocmd FileType query |
||||||
|
\ autocmd! NvimTreesitter BufWritePost <buffer> call v:lua.require('nvim-treesitter.query').invalidate_query_file(expand('%:p')) |
||||||
|
augroup END |
||||||
|
|
||||||
|
let g:loaded_nvim_treesitter = 1 |
||||||
|
|
||||||
|
lua require'nvim-treesitter'.setup() |
||||||
|
|
||||||
|
function s:has_attr(attr, mode) |
||||||
|
let norm_color = synIDattr(hlID('Normal'), a:attr, a:mode) |
||||||
|
return strlen(norm_color) > 0 |
||||||
|
endfunction |
||||||
|
|
||||||
|
" if the ctermfg or guifg is not known by nvim then using the |
||||||
|
" fg or foreground highlighting value will cause an E419 error |
||||||
|
" so we check to see if either highlight has been set if not default to NONE |
||||||
|
let cterm_normal = s:has_attr('fg', 'cterm') ? 'fg' : 'NONE' |
||||||
|
let gui_normal = s:has_attr('fg', 'gui') ? 'foreground' : 'NONE' |
||||||
|
|
||||||
|
execute 'highlight default TSNone term=NONE cterm=NONE gui=NONE guifg='.gui_normal.' ctermfg='.cterm_normal |
||||||
|
|
||||||
|
highlight default link TSPunctDelimiter Delimiter |
||||||
|
highlight default link TSPunctBracket Delimiter |
||||||
|
highlight default link TSPunctSpecial Delimiter |
||||||
|
|
||||||
|
highlight default link TSConstant Constant |
||||||
|
highlight default link TSConstBuiltin Special |
||||||
|
highlight default link TSConstMacro Define |
||||||
|
highlight default link TSString String |
||||||
|
highlight default link TSStringRegex String |
||||||
|
highlight default link TSStringEscape SpecialChar |
||||||
|
highlight default link TSStringSpecial SpecialChar |
||||||
|
highlight default link TSCharacter Character |
||||||
|
highlight default link TSNumber Number |
||||||
|
highlight default link TSBoolean Boolean |
||||||
|
highlight default link TSFloat Float |
||||||
|
|
||||||
|
highlight default link TSFunction Function |
||||||
|
highlight default link TSFuncBuiltin Special |
||||||
|
highlight default link TSFuncMacro Macro |
||||||
|
highlight default link TSParameter Identifier |
||||||
|
highlight default link TSParameterReference TSParameter |
||||||
|
highlight default link TSMethod Function |
||||||
|
highlight default link TSField Identifier |
||||||
|
highlight default link TSProperty Identifier |
||||||
|
highlight default link TSConstructor Special |
||||||
|
highlight default link TSAnnotation PreProc |
||||||
|
highlight default link TSAttribute PreProc |
||||||
|
highlight default link TSNamespace Include |
||||||
|
highlight default link TSSymbol Identifier |
||||||
|
|
||||||
|
highlight default link TSConditional Conditional |
||||||
|
highlight default link TSRepeat Repeat |
||||||
|
highlight default link TSLabel Label |
||||||
|
highlight default link TSOperator Operator |
||||||
|
highlight default link TSKeyword Keyword |
||||||
|
highlight default link TSKeywordFunction Keyword |
||||||
|
highlight default link TSKeywordOperator TSOperator |
||||||
|
highlight default link TSKeywordReturn TSKeyword |
||||||
|
highlight default link TSException Exception |
||||||
|
|
||||||
|
highlight default link TSType Type |
||||||
|
highlight default link TSTypeBuiltin Type |
||||||
|
highlight default link TSInclude Include |
||||||
|
|
||||||
|
highlight default link TSVariableBuiltin Special |
||||||
|
|
||||||
|
highlight default link TSText TSNone |
||||||
|
highlight default TSStrong term=bold cterm=bold gui=bold |
||||||
|
highlight default TSEmphasis term=italic cterm=italic gui=italic |
||||||
|
highlight default TSUnderline term=underline cterm=underline gui=underline |
||||||
|
highlight default TSStrike term=strikethrough cterm=strikethrough gui=strikethrough |
||||||
|
highlight default link TSMath Special |
||||||
|
highlight default link TSTextReference Constant |
||||||
|
highlight default link TSEnvironment Macro |
||||||
|
highlight default link TSEnvironmentName Type |
||||||
|
highlight default link TSTitle Title |
||||||
|
highlight default link TSLiteral String |
||||||
|
highlight default link TSURI Underlined |
||||||
|
|
||||||
|
highlight default link TSComment Comment |
||||||
|
highlight default link TSNote SpecialComment |
||||||
|
highlight default link TSWarning Todo |
||||||
|
highlight default link TSDanger WarningMsg |
||||||
|
|
||||||
|
highlight default link TSTag Label |
||||||
|
highlight default link TSTagDelimiter Delimiter |
||||||
|
highlight default link TSTagAttribute TSProperty |
@ -0,0 +1,8 @@ |
|||||||
|
[ |
||||||
|
(function_definition) |
||||||
|
(if_statement) |
||||||
|
(case_statement) |
||||||
|
(for_statement) |
||||||
|
(while_statement) |
||||||
|
(c_style_for_statement) |
||||||
|
] @fold |
@ -0,0 +1,129 @@ |
|||||||
|
(simple_expansion) @none |
||||||
|
(expansion |
||||||
|
"${" @punctuation.special |
||||||
|
"}" @punctuation.special) @none |
||||||
|
[ |
||||||
|
"(" |
||||||
|
")" |
||||||
|
"((" |
||||||
|
"))" |
||||||
|
"{" |
||||||
|
"}" |
||||||
|
"[" |
||||||
|
"]" |
||||||
|
"[[" |
||||||
|
"]]" |
||||||
|
] @punctuation.bracket |
||||||
|
|
||||||
|
[ |
||||||
|
";" |
||||||
|
";;" |
||||||
|
(heredoc_start) |
||||||
|
] @punctuation.delimiter |
||||||
|
|
||||||
|
[ |
||||||
|
"$" |
||||||
|
] @punctuation.special |
||||||
|
|
||||||
|
[ |
||||||
|
">" |
||||||
|
"<" |
||||||
|
"&" |
||||||
|
"&&" |
||||||
|
"|" |
||||||
|
"||" |
||||||
|
"=" |
||||||
|
"=~" |
||||||
|
"==" |
||||||
|
"!=" |
||||||
|
] @operator |
||||||
|
|
||||||
|
[ |
||||||
|
(string) |
||||||
|
(raw_string) |
||||||
|
(heredoc_body) |
||||||
|
] @string |
||||||
|
|
||||||
|
(variable_assignment (word) @string) |
||||||
|
|
||||||
|
[ |
||||||
|
"if" |
||||||
|
"then" |
||||||
|
"else" |
||||||
|
"elif" |
||||||
|
"fi" |
||||||
|
"case" |
||||||
|
"in" |
||||||
|
"esac" |
||||||
|
] @conditional |
||||||
|
|
||||||
|
[ |
||||||
|
"for" |
||||||
|
"do" |
||||||
|
"done" |
||||||
|
"while" |
||||||
|
] @repeat |
||||||
|
|
||||||
|
[ |
||||||
|
"declare" |
||||||
|
"export" |
||||||
|
"local" |
||||||
|
"readonly" |
||||||
|
"unset" |
||||||
|
] @keyword |
||||||
|
|
||||||
|
"function" @keyword.function |
||||||
|
|
||||||
|
(special_variable_name) @constant |
||||||
|
|
||||||
|
((word) @constant.builtin |
||||||
|
(#match? @constant.builtin "^SIG(INT|TERM|QUIT|TIN|TOU|STP|HUP)$")) |
||||||
|
|
||||||
|
((word) @boolean |
||||||
|
(#match? @boolean "^(true|false)$")) |
||||||
|
|
||||||
|
(comment) @comment |
||||||
|
(test_operator) @string |
||||||
|
|
||||||
|
(command_substitution |
||||||
|
[ "$(" ")" ] @punctuation.bracket) |
||||||
|
|
||||||
|
(process_substitution |
||||||
|
[ "<(" ")" ] @punctuation.bracket) |
||||||
|
|
||||||
|
|
||||||
|
(function_definition |
||||||
|
name: (word) @function) |
||||||
|
|
||||||
|
(command_name (word) @function) |
||||||
|
|
||||||
|
((command_name (word) @function.builtin) |
||||||
|
(#any-of? @function.builtin |
||||||
|
"cd" "echo" "eval" "exit" "getopts" |
||||||
|
"pushd" "popd" "return" "set" "shift")) |
||||||
|
|
||||||
|
(command |
||||||
|
argument: [ |
||||||
|
(word) @parameter |
||||||
|
(concatenation (word) @parameter) |
||||||
|
]) |
||||||
|
|
||||||
|
((word) @number |
||||||
|
(#lua-match? @number "^[0-9]+$")) |
||||||
|
|
||||||
|
(file_redirect |
||||||
|
descriptor: (file_descriptor) @operator |
||||||
|
destination: (word) @parameter) |
||||||
|
|
||||||
|
(expansion |
||||||
|
[ "${" "}" ] @punctuation.bracket) |
||||||
|
|
||||||
|
(variable_name) @variable |
||||||
|
|
||||||
|
((variable_name) @constant |
||||||
|
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) |
||||||
|
|
||||||
|
(case_item |
||||||
|
value: (word) @parameter) |
||||||
|
|
||||||
|
(regex) @string.regex |
@ -0,0 +1 @@ |
|||||||
|
(comment) @comment |
@ -0,0 +1,13 @@ |
|||||||
|
; Scopes |
||||||
|
(function_definition) @scope |
||||||
|
|
||||||
|
; Definitions |
||||||
|
(variable_assignment |
||||||
|
name: (variable_name) @definition.var) |
||||||
|
|
||||||
|
(function_definition |
||||||
|
name: (word) @definition.function) |
||||||
|
|
||||||
|
; References |
||||||
|
(variable_name) @reference |
||||||
|
(word) @reference |
@ -0,0 +1,4 @@ |
|||||||
|
[ |
||||||
|
(transaction) |
||||||
|
(heading) |
||||||
|
] @fold |
@ -0,0 +1,26 @@ |
|||||||
|
|
||||||
|
(date) @field |
||||||
|
(txn) @attribute |
||||||
|
|
||||||
|
(account) @type |
||||||
|
|
||||||
|
(amount) @number |
||||||
|
(incomplete_amount) @number |
||||||
|
(amount_tolerance) @number |
||||||
|
|
||||||
|
(currency) @property |
||||||
|
|
||||||
|
(key) @label |
||||||
|
(string) @string |
||||||
|
|
||||||
|
(tag) @constant |
||||||
|
(link) @constant |
||||||
|
|
||||||
|
(comment) @comment |
||||||
|
|
||||||
|
[ |
||||||
|
(balance) (open) (close) (commodity) (pad) |
||||||
|
(event) (price) (note) (document) (query) |
||||||
|
(custom) (pushtag) (poptag) (pushmeta) |
||||||
|
(popmeta) (option) (include) (plugin) |
||||||
|
] @keyword |
@ -0,0 +1,3 @@ |
|||||||
|
[ |
||||||
|
(entry) |
||||||
|
] @fold |
@ -0,0 +1,49 @@ |
|||||||
|
; CREDITS @pfoerster (adapted from https://github.com/latex-lsp/tree-sitter-bibtex) |
||||||
|
|
||||||
|
[ |
||||||
|
(string_type) |
||||||
|
(preamble_type) |
||||||
|
(entry_type) |
||||||
|
] @keyword |
||||||
|
|
||||||
|
[ |
||||||
|
(junk) |
||||||
|
(comment) |
||||||
|
] @comment |
||||||
|
|
||||||
|
[ |
||||||
|
"=" |
||||||
|
"#" |
||||||
|
] @operator |
||||||
|
|
||||||
|
(command) @function.builtin |
||||||
|
|
||||||
|
(number) @number |
||||||
|
|
||||||
|
(field |
||||||
|
name: (identifier) @field) |
||||||
|
|
||||||
|
(token |
||||||
|
(identifier) @parameter) |
||||||
|
|
||||||
|
[ |
||||||
|
(brace_word) |
||||||
|
(quote_word) |
||||||
|
] @string |
||||||
|
|
||||||
|
[ |
||||||
|
(key_brace) |
||||||
|
(key_paren) |
||||||
|
] @symbol |
||||||
|
|
||||||
|
(string |
||||||
|
name: (identifier) @constant) |
||||||
|
|
||||||
|
[ |
||||||
|
"{" |
||||||
|
"}" |
||||||
|
"(" |
||||||
|
")" |
||||||
|
] @punctuation.bracket |
||||||
|
|
||||||
|
"," @punctuation.delimiter |
@ -0,0 +1,10 @@ |
|||||||
|
[ |
||||||
|
(entry) |
||||||
|
] @indent |
||||||
|
|
||||||
|
[ |
||||||
|
"{" |
||||||
|
"}" |
||||||
|
] @branch |
||||||
|
|
||||||
|
(comment) @ignore |
@ -0,0 +1,18 @@ |
|||||||
|
[ |
||||||
|
(for_statement) |
||||||
|
(if_statement) |
||||||
|
(while_statement) |
||||||
|
(switch_statement) |
||||||
|
(case_statement) |
||||||
|
(function_definition) |
||||||
|
(struct_specifier) |
||||||
|
(enum_specifier) |
||||||
|
(comment) |
||||||
|
(preproc_if) |
||||||
|
(preproc_elif) |
||||||
|
(preproc_else) |
||||||
|
(preproc_ifdef) |
||||||
|
(initializer_list) |
||||||
|
(compound_statement) |
||||||
|
] @fold |
||||||
|
|
@ -0,0 +1,183 @@ |
|||||||
|
(identifier) @variable |
||||||
|
|
||||||
|
[ |
||||||
|
"const" |
||||||
|
"default" |
||||||
|
"enum" |
||||||
|
"extern" |
||||||
|
"inline" |
||||||
|
"static" |
||||||
|
"struct" |
||||||
|
"typedef" |
||||||
|
"union" |
||||||
|
"volatile" |
||||||
|
"goto" |
||||||
|
"register" |
||||||
|
] @keyword |
||||||
|
|
||||||
|
"sizeof" @keyword.operator |
||||||
|
"return" @keyword.return |
||||||
|
|
||||||
|
[ |
||||||
|
"while" |
||||||
|
"for" |
||||||
|
"do" |
||||||
|
"continue" |
||||||
|
"break" |
||||||
|
] @repeat |
||||||
|
|
||||||
|
[ |
||||||
|
"if" |
||||||
|
"else" |
||||||
|
"case" |
||||||
|
"switch" |
||||||
|
] @conditional |
||||||
|
|
||||||
|
"#define" @constant.macro |
||||||
|
[ |
||||||
|
"#if" |
||||||
|
"#ifdef" |
||||||
|
"#ifndef" |
||||||
|
"#else" |
||||||
|
"#elif" |
||||||
|
"#endif" |
||||||
|
(preproc_directive) |
||||||
|
] @keyword |
||||||
|
|
||||||
|
"#include" @include |
||||||
|
|
||||||
|
[ |
||||||
|
"=" |
||||||
|
|
||||||
|
"-" |
||||||
|
"*" |
||||||
|
"/" |
||||||
|
"+" |
||||||
|
"%" |
||||||
|
|
||||||
|
"~" |
||||||
|
"|" |
||||||
|
"&" |
||||||
|
"^" |
||||||
|
"<<" |
||||||
|
">>" |
||||||
|
|
||||||
|
"->" |
||||||
|
|
||||||
|
"<" |
||||||
|
"<=" |
||||||
|
">=" |
||||||
|
">" |
||||||
|
"==" |
||||||
|
"!=" |
||||||
|
|
||||||
|
"!" |
||||||
|
"&&" |
||||||
|
"||" |
||||||
|
|
||||||
|
"-=" |
||||||
|
"+=" |
||||||
|
"*=" |
||||||
|
"/=" |
||||||
|
"%=" |
||||||
|
"|=" |
||||||
|
"&=" |
||||||
|
"^=" |
||||||
|
">>=" |
||||||
|
"<<=" |
||||||
|
"--" |
||||||
|
"++" |
||||||
|
] @operator |
||||||
|
|
||||||
|
[ |
||||||
|
(true) |
||||||
|
(false) |
||||||
|
] @boolean |
||||||
|
|
||||||
|
[ "." ";" ":" "," ] @punctuation.delimiter |
||||||
|
|
||||||
|
"..." @punctuation.special |
||||||
|
|
||||||
|
(conditional_expression [ "?" ":" ] @conditional) |
||||||
|
|
||||||
|
|
||||||
|
[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket |
||||||
|
|
||||||
|
(string_literal) @string |
||||||
|
(system_lib_string) @string |
||||||
|
(escape_sequence) @string.escape |
||||||
|
|
||||||
|
(null) @constant.builtin |
||||||
|
(number_literal) @number |
||||||
|
(char_literal) @character |
||||||
|
|
||||||
|
[ |
||||||
|
(preproc_arg) |
||||||
|
(preproc_defined) |
||||||
|
] @function.macro |
||||||
|
|
||||||
|
(((field_expression |
||||||
|
(field_identifier) @property)) @_parent |
||||||
|
(#not-has-parent? @_parent template_method function_declarator call_expression)) |
||||||
|
|
||||||
|
(((field_identifier) @property) |
||||||
|
(#has-ancestor? @property field_declaration) |
||||||
|
(#not-has-ancestor? @property function_declarator)) |
||||||
|
|
||||||
|
(statement_identifier) @label |
||||||
|
|
||||||
|
[ |
||||||
|
(type_identifier) |
||||||
|
(primitive_type) |
||||||
|
(sized_type_specifier) |
||||||
|
(type_descriptor) |
||||||
|
] @type |
||||||
|
|
||||||
|
(sizeof_expression value: (parenthesized_expression (identifier) @type)) |
||||||
|
|
||||||
|
((identifier) @constant |
||||||
|
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) |
||||||
|
|
||||||
|
;; Preproc def / undef |
||||||
|
(preproc_def |
||||||
|
name: (_) @constant) |
||||||
|
(preproc_call |
||||||
|
directive: (preproc_directive) @_u |
||||||
|
argument: (_) @constant |
||||||
|
(#eq? @_u "#undef")) |
||||||
|
|
||||||
|
(call_expression |
||||||
|
function: (identifier) @function) |
||||||
|
(call_expression |
||||||
|
function: (field_expression |
||||||
|
field: (field_identifier) @function)) |
||||||
|
(function_declarator |
||||||
|
declarator: (identifier) @function) |
||||||
|
(preproc_function_def |
||||||
|
name: (identifier) @function.macro) |
||||||
|
|
||||||
|
(comment) @comment |
||||||
|
|
||||||
|
;; Parameters |
||||||
|
(parameter_declaration |
||||||
|
declarator: (identifier) @parameter) |
||||||
|
|
||||||
|
(parameter_declaration |
||||||
|
declarator: (pointer_declarator) @parameter) |
||||||
|
|
||||||
|
(preproc_params (identifier) @parameter) |
||||||
|
|
||||||
|
[ |
||||||
|
"__attribute__" |
||||||
|
"__cdecl" |
||||||
|
"__clrcall" |
||||||
|
"__stdcall" |
||||||
|
"__fastcall" |
||||||
|
"__thiscall" |
||||||
|
"__vectorcall" |
||||||
|
"_unaligned" |
||||||
|
"__unaligned" |
||||||
|
"__declspec" |
||||||
|
] @attribute |
||||||
|
|
||||||
|
(ERROR) @error |
@ -0,0 +1,47 @@ |
|||||||
|
[ |
||||||
|
(init_declarator) |
||||||
|
(compound_statement) |
||||||
|
(preproc_arg) |
||||||
|
(field_declaration_list) |
||||||
|
(case_statement) |
||||||
|
(conditional_expression) |
||||||
|
(enumerator_list) |
||||||
|
(struct_specifier) |
||||||
|
(compound_literal_expression) |
||||||
|
(parameter_list) |
||||||
|
(initializer_list) |
||||||
|
(concatenated_string) |
||||||
|
(while_statement) |
||||||
|
(for_statement) |
||||||
|
(switch_statement) |
||||||
|
] @indent |
||||||
|
(if_statement condition: (_) @indent) |
||||||
|
((if_statement |
||||||
|
consequence: (_) @_consequence |
||||||
|
(#not-has-type? @_consequence compound_statement) |
||||||
|
) @indent) |
||||||
|
(init_declarator |
||||||
|
value: [ |
||||||
|
(binary_expression) |
||||||
|
] @indent) |
||||||
|
|
||||||
|
(compound_statement "}" @indent_end) |
||||||
|
|
||||||
|
[ |
||||||
|
"#define" |
||||||
|
"#ifdef" |
||||||
|
"#if" |
||||||
|
"#else" |
||||||
|
"else" |
||||||
|
"#endif" |
||||||
|
")" |
||||||
|
"}" |
||||||
|
] @branch |
||||||
|
|
||||||
|
[ |
||||||
|
(comment) |
||||||
|
(preproc_arg) |
||||||
|
(string_literal) |
||||||
|
] @ignore |
||||||
|
|
||||||
|
(binary_expression) @auto |
@ -0,0 +1,3 @@ |
|||||||
|
(preproc_arg) @c |
||||||
|
|
||||||
|
(comment) @comment |
@ -0,0 +1,53 @@ |
|||||||
|
;; Functions definitions |
||||||
|
(function_declarator |
||||||
|
declarator: (identifier) @definition.function) |
||||||
|
(preproc_function_def |
||||||
|
name: (identifier) @definition.macro) @scope |
||||||
|
|
||||||
|
(preproc_def |
||||||
|
name: (identifier) @definition.macro) |
||||||
|
(pointer_declarator |
||||||
|
declarator: (identifier) @definition.var) |
||||||
|
(parameter_declaration |
||||||
|
declarator: (identifier) @definition.parameter) |
||||||
|
(init_declarator |
||||||
|
declarator: (identifier) @definition.var) |
||||||
|
(array_declarator |
||||||
|
declarator: (identifier) @definition.var) |
||||||
|
(declaration |
||||||
|
declarator: (identifier) @definition.var) |
||||||
|
(enum_specifier |
||||||
|
name: (_) @definition.type |
||||||
|
(enumerator_list |
||||||
|
(enumerator name: (identifier) @definition.var))) |
||||||
|
|
||||||
|
;; Type / Struct / Enum |
||||||
|
(field_declaration |
||||||
|
declarator: (field_identifier) @definition.field) |
||||||
|
(type_definition |
||||||
|
declarator: (type_identifier) @definition.type) |
||||||
|
(struct_specifier |
||||||
|
name: (type_identifier) @definition.type) |
||||||
|
|
||||||
|
;; goto |
||||||
|
(labeled_statement (statement_identifier) @definition) |
||||||
|
|
||||||
|
;; References |
||||||
|
(identifier) @reference |
||||||
|
((field_identifier) @reference |
||||||
|
(set! reference.kind "field")) |
||||||
|
((type_identifier) @reference |
||||||
|
(set! reference.kind "type")) |
||||||
|
|
||||||
|
(goto_statement (statement_identifier) @reference) |
||||||
|
|
||||||
|
;; Scope |
||||||
|
[ |
||||||
|
(for_statement) |
||||||
|
(if_statement) |
||||||
|
(while_statement) |
||||||
|
(translation_unit) |
||||||
|
(function_definition) |
||||||
|
(compound_statement) ; a block in curly braces |
||||||
|
(struct_specifier) |
||||||
|
] @scope |
@ -0,0 +1,15 @@ |
|||||||
|
body: [ |
||||||
|
(declaration_list) |
||||||
|
(switch_body) |
||||||
|
(enum_member_declaration_list) |
||||||
|
] @fold |
||||||
|
|
||||||
|
accessors: [ |
||||||
|
(accessor_list) |
||||||
|
] @fold |
||||||
|
|
||||||
|
initializer: [ |
||||||
|
(initializer_expression) |
||||||
|
] @fold |
||||||
|
|
||||||
|
(block) @fold |
@ -0,0 +1,390 @@ |
|||||||
|
(identifier) @variable |
||||||
|
|
||||||
|
((identifier) @keyword |
||||||
|
(#eq? @keyword "value") |
||||||
|
(#has-ancestor? @keyword accessor_declaration)) |
||||||
|
|
||||||
|
(method_declaration |
||||||
|
name: (identifier) @method) |
||||||
|
|
||||||
|
(local_function_statement |
||||||
|
name: (identifier) @method) |
||||||
|
|
||||||
|
(method_declaration |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(local_function_statement |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(interpolation) @none |
||||||
|
|
||||||
|
(invocation_expression |
||||||
|
(member_access_expression |
||||||
|
name: (identifier) @method)) |
||||||
|
|
||||||
|
(invocation_expression |
||||||
|
function: (conditional_access_expression |
||||||
|
(member_binding_expression |
||||||
|
name: (identifier) @method))) |
||||||
|
|
||||||
|
(namespace_declaration |
||||||
|
name: [(qualified_name) (identifier)] @namespace) |
||||||
|
|
||||||
|
(qualified_name |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(invocation_expression |
||||||
|
(identifier) @method) |
||||||
|
|
||||||
|
(field_declaration |
||||||
|
(variable_declaration |
||||||
|
(variable_declarator |
||||||
|
(identifier) @field))) |
||||||
|
|
||||||
|
(initializer_expression |
||||||
|
(assignment_expression |
||||||
|
left: (identifier) @field)) |
||||||
|
|
||||||
|
(parameter_list |
||||||
|
(parameter |
||||||
|
name: (identifier) @parameter)) |
||||||
|
|
||||||
|
(parameter_list |
||||||
|
(parameter |
||||||
|
type: (identifier) @type)) |
||||||
|
|
||||||
|
(integer_literal) @number |
||||||
|
(real_literal) @float |
||||||
|
|
||||||
|
(null_literal) @constant.builtin |
||||||
|
(character_literal) @character |
||||||
|
|
||||||
|
[ |
||||||
|
(string_literal) |
||||||
|
(verbatim_string_literal) |
||||||
|
(interpolated_string_expression) |
||||||
|
] @string |
||||||
|
|
||||||
|
(boolean_literal) @boolean |
||||||
|
|
||||||
|
[ |
||||||
|
(predefined_type) |
||||||
|
(void_keyword) |
||||||
|
] @type.builtin |
||||||
|
|
||||||
|
(implicit_type) @keyword |
||||||
|
|
||||||
|
(comment) @comment |
||||||
|
|
||||||
|
(using_directive |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(property_declaration |
||||||
|
name: (identifier) @property) |
||||||
|
|
||||||
|
(property_declaration |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(nullable_type |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(catch_declaration |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(interface_declaration |
||||||
|
name: (identifier) @type) |
||||||
|
(class_declaration |
||||||
|
name: (identifier) @type) |
||||||
|
(record_declaration |
||||||
|
name: (identifier) @type) |
||||||
|
(enum_declaration |
||||||
|
name: (identifier) @type) |
||||||
|
(constructor_declaration |
||||||
|
name: (identifier) @constructor) |
||||||
|
(constructor_initializer [ |
||||||
|
"base" @constructor |
||||||
|
]) |
||||||
|
|
||||||
|
(variable_declaration |
||||||
|
(identifier) @type) |
||||||
|
(object_creation_expression |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
; Generic Types. |
||||||
|
(type_of_expression |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(type_argument_list |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(base_list |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(type_constraint |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(object_creation_expression |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(property_declaration |
||||||
|
(generic_name |
||||||
|
(identifier) @type)) |
||||||
|
|
||||||
|
(_ |
||||||
|
type: (generic_name |
||||||
|
(identifier) @type)) |
||||||
|
; Generic Method invocation with generic type |
||||||
|
(invocation_expression |
||||||
|
function: (generic_name |
||||||
|
. (identifier) @method)) |
||||||
|
|
||||||
|
(invocation_expression |
||||||
|
(member_access_expression |
||||||
|
(generic_name |
||||||
|
(identifier) @method))) |
||||||
|
|
||||||
|
(base_list |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(type_argument_list |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(type_parameter_list |
||||||
|
(type_parameter) @type) |
||||||
|
|
||||||
|
(type_parameter_constraints_clause |
||||||
|
target: (identifier) @type) |
||||||
|
|
||||||
|
(attribute |
||||||
|
name: (identifier) @attribute) |
||||||
|
|
||||||
|
(for_each_statement |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(tuple_element |
||||||
|
type: (identifier) @type) |
||||||
|
|
||||||
|
(tuple_expression |
||||||
|
(argument |
||||||
|
(declaration_expression |
||||||
|
type: (identifier) @type))) |
||||||
|
|
||||||
|
(as_expression |
||||||
|
right: (identifier) @type) |
||||||
|
|
||||||
|
(type_of_expression |
||||||
|
(identifier) @type) |
||||||
|
|
||||||
|
(name_colon |
||||||
|
(identifier) @parameter) |
||||||
|
|
||||||
|
(warning_directive) @text.warning |
||||||
|
(error_directive) @exception |
||||||
|
|
||||||
|
(define_directive |
||||||
|
(identifier) @constant) @constant.macro |
||||||
|
(undef_directive |
||||||
|
(identifier) @constant) @constant.macro |
||||||
|
|
||||||
|
(line_directive) @constant.macro |
||||||
|
(line_directive |
||||||
|
(preproc_integer_literal) @constant |
||||||
|
(preproc_string_literal)? @string) |
||||||
|
|
||||||
|
(pragma_directive |
||||||
|
(identifier) @constant) @constant.macro |
||||||
|
(pragma_directive |
||||||
|
(preproc_string_literal) @string) @constant.macro |
||||||
|
|
||||||
|
[ |
||||||
|
(nullable_directive) |
||||||
|
(region_directive) |
||||||
|
(endregion_directive) |
||||||
|
] @constant.macro |
||||||
|
|
||||||
|
[ |
||||||
|
"if" |
||||||
|
"else" |
||||||
|
"switch" |
||||||
|
"break" |
||||||
|
"case" |
||||||
|
(if_directive) |
||||||
|
(elif_directive) |
||||||
|
(else_directive) |
||||||
|
(endif_directive) |
||||||
|
] @conditional |
||||||
|
|
||||||
|
(if_directive |
||||||
|
(identifier) @constant) |
||||||
|
(elif_directive |
||||||
|
(identifier) @constant) |
||||||
|
|
||||||
|
[ |
||||||
|
"while" |
||||||
|
"for" |
||||||
|
"do" |
||||||
|
"continue" |
||||||
|
"goto" |
||||||
|
"foreach" |
||||||
|
] @repeat |
||||||
|
|
||||||
|
[ |
||||||
|
"try" |
||||||
|
"catch" |
||||||
|
"throw" |
||||||
|
"finally" |
||||||
|
] @exception |
||||||
|
|
||||||
|
[ |
||||||
|
"+" |
||||||
|
"?" |
||||||
|
":" |
||||||
|
"++" |
||||||
|
"-" |
||||||
|
"--" |
||||||
|
"&" |
||||||
|
"&&" |
||||||
|
"|" |
||||||
|
"||" |
||||||
|
"!" |
||||||
|
"!=" |
||||||
|
"==" |
||||||
|
"*" |
||||||
|
"/" |
||||||
|
"%" |
||||||
|
"<" |
||||||
|
"<=" |
||||||
|
">" |
||||||
|
">=" |
||||||
|
"=" |
||||||
|
"-=" |
||||||
|
"+=" |
||||||
|
"*=" |
||||||
|
"/=" |
||||||
|
"%=" |
||||||
|
"^" |
||||||
|
"^=" |
||||||
|
"&=" |
||||||
|
"|=" |
||||||
|
"~" |
||||||
|
">>" |
||||||
|
"<<" |
||||||
|
"<<=" |
||||||
|
">>=" |
||||||
|
"=>" |
||||||
|
] @operator |
||||||
|
|
||||||
|
[ |
||||||
|
";" |
||||||
|
"." |
||||||
|
"," |
||||||
|
":" |
||||||
|
] @punctuation.delimiter |
||||||
|
|
||||||
|
[ |
||||||
|
"[" |
||||||
|
"]" |
||||||
|
"{" |
||||||
|
"}" |
||||||
|
"(" |
||||||
|
")" |
||||||
|
"<" |
||||||
|
">" |
||||||
|
] @punctuation.bracket |
||||||
|
|
||||||
|
[ |
||||||
|
(this_expression) |
||||||
|
(base_expression) |
||||||
|
] @variable.builtin |
||||||
|
|
||||||
|
[ |
||||||
|
"using" |
||||||
|
] @include |
||||||
|
|
||||||
|
(alias_qualified_name |
||||||
|
(identifier "global") @include) |
||||||
|
|
||||||
|
[ |
||||||
|
"with" |
||||||
|
"new" |
||||||
|
"typeof" |
||||||
|
"nameof" |
||||||
|
"sizeof" |
||||||
|
"is" |
||||||
|
"as" |
||||||
|
"and" |
||||||
|
"or" |
||||||
|
"not" |
||||||
|
"stackalloc" |
||||||
|
"in" |
||||||
|
"out" |
||||||
|
"ref" |
||||||
|
] @keyword.operator |
||||||
|
|
||||||
|
[ |
||||||
|
"lock" |
||||||
|
"params" |
||||||
|
"operator" |
||||||
|
"default" |
||||||
|
"abstract" |
||||||
|
"const" |
||||||
|
"extern" |
||||||
|
"implicit" |
||||||
|
"explicit" |
||||||
|
"internal" |
||||||
|
"override" |
||||||
|
"private" |
||||||
|
"protected" |
||||||
|
"public" |
||||||
|
"internal" |
||||||
|
"partial" |
||||||
|
"readonly" |
||||||
|
"sealed" |
||||||
|
"static" |
||||||
|
"virtual" |
||||||
|
"volatile" |
||||||
|
"async" |
||||||
|
"await" |
||||||
|
"class" |
||||||
|
"delegate" |
||||||
|
"enum" |
||||||
|
"interface" |
||||||
|
"namespace" |
||||||
|
"struct" |
||||||
|
"get" |
||||||
|
"set" |
||||||
|
"init" |
||||||
|
"where" |
||||||
|
"record" |
||||||
|
"event" |
||||||
|
"add" |
||||||
|
"remove" |
||||||
|
"checked" |
||||||
|
"unchecked" |
||||||
|
"fixed" |
||||||
|
] @keyword |
||||||
|
|
||||||
|
(parameter_modifier) @operator |
||||||
|
|
||||||
|
(query_expression |
||||||
|
(_ [ |
||||||
|
"from" |
||||||
|
"orderby" |
||||||
|
"select" |
||||||
|
"group" |
||||||
|
"by" |
||||||
|
"ascending" |
||||||
|
"descending" |
||||||
|
"equals" |
||||||
|
"let" |
||||||
|
] @keyword)) |
||||||
|
|
||||||
|
[ |
||||||
|
"return" |
||||||
|
"yield" |
||||||
|
] @keyword.return |
@ -0,0 +1 @@ |
|||||||
|
(comment) @comment |
@ -0,0 +1,41 @@ |
|||||||
|
;; Definitions |
||||||
|
(variable_declarator |
||||||
|
. (identifier) @definition.var) |
||||||
|
|
||||||
|
(variable_declarator |
||||||
|
(tuple_pattern |
||||||
|
(identifier) @definition.var)) |
||||||
|
|
||||||
|
(declaration_expression |
||||||
|
name: (identifier) @definition.var) |
||||||
|
|
||||||
|
(for_each_statement |
||||||
|
left: (identifier) @definition.var) |
||||||
|
|
||||||
|
(for_each_statement |
||||||
|
left: (tuple_pattern |
||||||
|
(identifier) @definition.var)) |
||||||
|
|
||||||
|
(parameter |
||||||
|
(identifier) @definition.parameter) |
||||||
|
|
||||||
|
(method_declaration |
||||||
|
name: (identifier) @definition.method) |
||||||
|
|
||||||
|
(local_function_statement |
||||||
|
name: (identifier) @definition.method) |
||||||
|
|
||||||
|
(property_declaration |
||||||
|
name: (identifier) @definition) |
||||||
|
|
||||||
|
(type_parameter |
||||||
|
(identifier) @definition.type) |
||||||
|
|
||||||
|
(class_declaration |
||||||
|
name: (identifier) @definition) |
||||||
|
|
||||||
|
;; References |
||||||
|
(identifier) @reference |
||||||
|
|
||||||
|
;; Scope |
||||||
|
(block) @scope |
@ -0,0 +1 @@ |
|||||||
|
(source (list_lit) @fold) |
@ -0,0 +1,338 @@ |
|||||||
|
;; >> Explanation |
||||||
|
;; Parsers for lisps are a bit weird in that they just return the raw forms. |
||||||
|
;; This means we have to do a bit of extra work in the queries to get things |
||||||
|
;; highlighted as they should be. |
||||||
|
;; |
||||||
|
;; For the most part this means that some things have to be assigned multiple |
||||||
|
;; groups. |
||||||
|
;; By doing this we can add a basic capture and then later refine it with more |
||||||
|
;; specialied captures. |
||||||
|
;; This can mean that sometimes things are highlighted weirdly because they |
||||||
|
;; have multiple highlight groups applied to them. |
||||||
|
|
||||||
|
|
||||||
|
;; >> Literals |
||||||
|
|
||||||
|
( |
||||||
|
(dis_expr) @comment |
||||||
|
(#set! "priority" 105) ; Higher priority to mark the whole sexpr as a comment |
||||||
|
) |
||||||
|
(kwd_lit) @symbol |
||||||
|
(str_lit) @string |
||||||
|
(num_lit) @number |
||||||
|
(char_lit) @character |
||||||
|
(bool_lit) @boolean |
||||||
|
(nil_lit) @constant.builtin |
||||||
|
(comment) @comment |
||||||
|
(regex_lit) @string.regex |
||||||
|
|
||||||
|
["'" "`"] @string.escape |
||||||
|
|
||||||
|
["~" "~@" "#"] @punctuation.special |
||||||
|
|
||||||
|
["{" "}" "[" "]" "(" ")"] @punctuation.bracket |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; >> Symbols |
||||||
|
|
||||||
|
; General symbol highlighting |
||||||
|
(sym_lit) @variable |
||||||
|
|
||||||
|
; General function calls |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @function) |
||||||
|
(anon_fn_lit |
||||||
|
. |
||||||
|
(sym_lit) @function) |
||||||
|
|
||||||
|
; Quoted symbols |
||||||
|
(quoting_lit |
||||||
|
(sym_lit) @symbol) |
||||||
|
(syn_quoting_lit |
||||||
|
(sym_lit) @symbol) |
||||||
|
|
||||||
|
; Used in destructure pattern |
||||||
|
((sym_lit) @parameter |
||||||
|
(#lua-match? @parameter "^[&]")) |
||||||
|
|
||||||
|
; Inline function variables |
||||||
|
((sym_lit) @variable.builtin |
||||||
|
(#match? @variable.builtin "^[%]")) |
||||||
|
|
||||||
|
; Constructor |
||||||
|
((sym_lit) @constructor |
||||||
|
(#match? @constructor "^-\\>[^\\>].*")) |
||||||
|
|
||||||
|
; Dynamic variables |
||||||
|
((sym_lit) @variable.builtin |
||||||
|
(#match? @variable.builtin "^[*].+[*]$")) |
||||||
|
|
||||||
|
; Gensym |
||||||
|
;; Might not be needed |
||||||
|
((sym_lit) @variable |
||||||
|
(#match? @variable "^.*#$")) |
||||||
|
|
||||||
|
; Types |
||||||
|
;; TODO: improve? |
||||||
|
((sym_lit) @type |
||||||
|
(#match? @type "^[A-Z][^/]*$")) |
||||||
|
;; Symbols with `.` but not `/` |
||||||
|
((sym_lit) @type |
||||||
|
(#match? @type "^[^/]+[.][^/]*$")) |
||||||
|
|
||||||
|
; Interop |
||||||
|
((sym_lit) @method |
||||||
|
(#match? @method "^\\.[^-]")) |
||||||
|
((sym_lit) @field |
||||||
|
(#match? @field "^\\.-")) |
||||||
|
((sym_lit) @field |
||||||
|
(#match? @field "^[A-Z].*/.+")) |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @method |
||||||
|
(#match? @method "^[A-Z].*/.+")) |
||||||
|
;; TODO: Special casing for the `.` macro |
||||||
|
|
||||||
|
; Operators |
||||||
|
((sym_lit) @operator |
||||||
|
(#any-of? @operator |
||||||
|
"*" "*'" "+" "+'" "-" "-'" "/" |
||||||
|
"<" "<=" ">" ">=" "=" "==")) |
||||||
|
((sym_lit) @keyword.operator |
||||||
|
(#any-of? @keyword.operator |
||||||
|
"not" "not=" "and" "or")) |
||||||
|
|
||||||
|
; Definition functions |
||||||
|
((sym_lit) @keyword |
||||||
|
(#lua-match? @keyword "^def.*$")) |
||||||
|
((sym_lit) @keyword |
||||||
|
(#eq? @keyword "declare")) |
||||||
|
((sym_lit) @keyword.function |
||||||
|
(#match? @keyword.function "^(defn|defn-|fn|fn[*])$")) |
||||||
|
|
||||||
|
; Comment |
||||||
|
((sym_lit) @comment |
||||||
|
(#any-of? @comment "comment")) |
||||||
|
|
||||||
|
; Conditionals |
||||||
|
((sym_lit) @conditional |
||||||
|
(#any-of? @conditional |
||||||
|
"case" "cond" "cond->" "cond->>" "condp")) |
||||||
|
((sym_lit) @conditional |
||||||
|
(#match? @conditional "^if(\\-.*)?$")) |
||||||
|
((sym_lit) @conditional |
||||||
|
(#match? @conditional "^when(\\-.*)?$")) |
||||||
|
|
||||||
|
; Repeats |
||||||
|
((sym_lit) @repeat |
||||||
|
(#any-of? @repeat |
||||||
|
"doseq" "dotimes" "for" "loop" "recur" "while")) |
||||||
|
|
||||||
|
; Exception |
||||||
|
((sym_lit) @exception |
||||||
|
(#any-of? @exception |
||||||
|
"throw" "try" "catch" "finally" "ex-info")) |
||||||
|
|
||||||
|
; Includes |
||||||
|
((sym_lit) @include |
||||||
|
(#any-of? @include "ns" "import" "require" "use")) |
||||||
|
|
||||||
|
; Builtin macros |
||||||
|
;; TODO: Do all these items belong here? |
||||||
|
((sym_lit) @function.macro |
||||||
|
(#any-of? @function.macro |
||||||
|
"." ".." "->" "->>" "amap" "areduce" "as->" "assert" |
||||||
|
"binding" "bound-fn" "delay" "do" "dosync" |
||||||
|
"doto" "extend-protocol" "extend-type" "future" |
||||||
|
"gen-class" "gen-interface" "io!" "lazy-cat" |
||||||
|
"lazy-seq" "let" "letfn" "locking" "memfn" "monitor-enter" |
||||||
|
"monitor-exit" "proxy" "proxy-super" "pvalues" |
||||||
|
"refer-clojure" "reify" "set!" "some->" "some->>" "sync" |
||||||
|
"time" "unquote" "unquote-splicing" "var" "vswap!" |
||||||
|
"ex-cause" "ex-data" "ex-message")) |
||||||
|
((sym_lit) @function.macro |
||||||
|
(#match? @function.macro "^with\\-.*$")) |
||||||
|
|
||||||
|
; All builtin functions |
||||||
|
; (->> (ns-publics *ns*) |
||||||
|
; (keep (fn [[s v]] (when-not (:macro (meta v)) s))) |
||||||
|
; sort |
||||||
|
; cp/print)) |
||||||
|
;; ...and then lots of manual filtering... |
||||||
|
((sym_lit) @function.builtin |
||||||
|
(#any-of? @function.builtin |
||||||
|
"->ArrayChunk" "->Eduction" "->Vec" "->VecNode" "->VecSeq" |
||||||
|
"-cache-protocol-fn" "-reset-methods" "PrintWriter-on" |
||||||
|
"StackTraceElement->vec" "Throwable->map" "accessor" |
||||||
|
"aclone" "add-classpath" "add-tap" "add-watch" "agent" |
||||||
|
"agent-error" "agent-errors" "aget" "alength" "alias" |
||||||
|
"all-ns" "alter" "alter-meta!" "alter-var-root" "ancestors" |
||||||
|
"any?" "apply" "array-map" "aset" "aset-boolean" "aset-byte" |
||||||
|
"aset-char" "aset-double" "aset-float" "aset-int" |
||||||
|
"aset-long" "aset-short" "assoc" "assoc!" "assoc-in" |
||||||
|
"associative?" "atom" "await" "await-for" "await1" |
||||||
|
"bases" "bean" "bigdec" "bigint" "biginteger" "bit-and" |
||||||
|
"bit-and-not" "bit-clear" "bit-flip" "bit-not" "bit-or" |
||||||
|
"bit-set" "bit-shift-left" "bit-shift-right" "bit-test" |
||||||
|
"bit-xor" "boolean" "boolean-array" "boolean?" |
||||||
|
"booleans" "bound-fn*" "bound?" "bounded-count" |
||||||
|
"butlast" "byte" "byte-array" "bytes" "bytes?" |
||||||
|
"cast" "cat" "char" "char-array" "char-escape-string" |
||||||
|
"char-name-string" "char?" "chars" "chunk" "chunk-append" |
||||||
|
"chunk-buffer" "chunk-cons" "chunk-first" "chunk-next" |
||||||
|
"chunk-rest" "chunked-seq?" "class" "class?" |
||||||
|
"clear-agent-errors" "clojure-version" "coll?" |
||||||
|
"commute" "comp" "comparator" "compare" "compare-and-set!" |
||||||
|
"compile" "complement" "completing" "concat" "conj" |
||||||
|
"conj!" "cons" "constantly" "construct-proxy" "contains?" |
||||||
|
"count" "counted?" "create-ns" "create-struct" "cycle" |
||||||
|
"dec" "dec'" "decimal?" "dedupe" "default-data-readers" |
||||||
|
"delay?" "deliver" "denominator" "deref" "derive" |
||||||
|
"descendants" "destructure" "disj" "disj!" "dissoc" |
||||||
|
"dissoc!" "distinct" "distinct?" "doall" "dorun" "double" |
||||||
|
"double-array" "eduction" "empty" "empty?" "ensure" "ensure-reduced" |
||||||
|
"enumeration-seq" "error-handler" "error-mode" "eval" |
||||||
|
"even?" "every-pred" "every?" "extend" "extenders" "extends?" |
||||||
|
"false?" "ffirst" "file-seq" "filter" "filterv" "find" |
||||||
|
"find-keyword" "find-ns" "find-protocol-impl" |
||||||
|
"find-protocol-method" "find-var" "first" "flatten" |
||||||
|
"float" "float-array" "float?" "floats" "flush" "fn?" |
||||||
|
"fnext" "fnil" "force" "format" "frequencies" |
||||||
|
"future-call" "future-cancel" "future-cancelled?" |
||||||
|
"future-done?" "future?" "gensym" "get" "get-in" |
||||||
|
"get-method" "get-proxy-class" "get-thread-bindings" |
||||||
|
"get-validator" "group-by" "halt-when" "hash" |
||||||
|
"hash-combine" "hash-map" "hash-ordered-coll" "hash-set" |
||||||
|
"hash-unordered-coll" "ident?" "identical?" "identity" |
||||||
|
"ifn?" "in-ns" "inc" "inc'" "indexed?" "init-proxy" |
||||||
|
"inst-ms" "inst-ms*" "inst?" "instance?" "int" "int-array" |
||||||
|
"int?" "integer?" "interleave" "intern" "interpose" "into" |
||||||
|
"into-array" "ints" "isa?" "iterate" "iterator-seq" "juxt" |
||||||
|
"keep" "keep-indexed" "key" "keys" "keyword" "keyword?" |
||||||
|
"last" "line-seq" "list" "list*" "list?" "load" "load-file" |
||||||
|
"load-reader" "load-string" "loaded-libs" "long" "long-array" |
||||||
|
"longs" "macroexpand" "macroexpand-1" "make-array" "make-hierarchy" |
||||||
|
"map" "map-entry?" "map-indexed" "map?" "mapcat" "mapv" |
||||||
|
"max" "max-key" "memoize" "merge" "merge-with" "meta" |
||||||
|
"method-sig" "methods" "min" "min-key" "mix-collection-hash" |
||||||
|
"mod" "munge" "name" "namespace" "namespace-munge" "nat-int?" |
||||||
|
"neg-int?" "neg?" "newline" "next" "nfirst" "nil?" "nnext" |
||||||
|
"not-any?" "not-empty" "not-every?" "ns-aliases" |
||||||
|
"ns-imports" "ns-interns" "ns-map" "ns-name" "ns-publics" |
||||||
|
"ns-refers" "ns-resolve" "ns-unalias" "ns-unmap" "nth" |
||||||
|
"nthnext" "nthrest" "num" "number?" "numerator" "object-array" |
||||||
|
"odd?" "parents" "partial" "partition" "partition-all" |
||||||
|
"partition-by" "pcalls" "peek" "persistent!" "pmap" "pop" |
||||||
|
"pop!" "pop-thread-bindings" "pos-int?" "pos?" "pr" |
||||||
|
"pr-str" "prefer-method" "prefers" "primitives-classnames" |
||||||
|
"print" "print-ctor" "print-dup" "print-method" "print-simple" |
||||||
|
"print-str" "printf" "println" "println-str" "prn" "prn-str" |
||||||
|
"promise" "proxy-call-with-super" "proxy-mappings" "proxy-name" |
||||||
|
"push-thread-bindings" "qualified-ident?" "qualified-keyword?" |
||||||
|
"qualified-symbol?" "quot" "rand" "rand-int" "rand-nth" "random-sample" |
||||||
|
"range" "ratio?" "rational?" "rationalize" "re-find" "re-groups" |
||||||
|
"re-matcher" "re-matches" "re-pattern" "re-seq" "read" |
||||||
|
"read+string" "read-line" "read-string" "reader-conditional" |
||||||
|
"reader-conditional?" "realized?" "record?" "reduce" |
||||||
|
"reduce-kv" "reduced" "reduced?" "reductions" "ref" "ref-history-count" |
||||||
|
"ref-max-history" "ref-min-history" "ref-set" "refer" |
||||||
|
"release-pending-sends" "rem" "remove" "remove-all-methods" |
||||||
|
"remove-method" "remove-ns" "remove-tap" "remove-watch" |
||||||
|
"repeat" "repeatedly" "replace" "replicate" |
||||||
|
"requiring-resolve" "reset!" "reset-meta!" "reset-vals!" |
||||||
|
"resolve" "rest" "restart-agent" "resultset-seq" "reverse" |
||||||
|
"reversible?" "rseq" "rsubseq" "run!" "satisfies?" |
||||||
|
"second" "select-keys" "send" "send-off" "send-via" |
||||||
|
"seq" "seq?" "seqable?" "seque" "sequence" "sequential?" |
||||||
|
"set" "set-agent-send-executor!" "set-agent-send-off-executor!" |
||||||
|
"set-error-handler!" "set-error-mode!" "set-validator!" |
||||||
|
"set?" "short" "short-array" "shorts" "shuffle" |
||||||
|
"shutdown-agents" "simple-ident?" "simple-keyword?" |
||||||
|
"simple-symbol?" "slurp" "some" "some-fn" "some?" |
||||||
|
"sort" "sort-by" "sorted-map" "sorted-map-by" |
||||||
|
"sorted-set" "sorted-set-by" "sorted?" "special-symbol?" |
||||||
|
"spit" "split-at" "split-with" "str" "string?" |
||||||
|
"struct" "struct-map" "subs" "subseq" "subvec" "supers" |
||||||
|
"swap!" "swap-vals!" "symbol" "symbol?" "tagged-literal" |
||||||
|
"tagged-literal?" "take" "take-last" "take-nth" "take-while" |
||||||
|
"tap>" "test" "the-ns" "thread-bound?" "to-array" |
||||||
|
"to-array-2d" "trampoline" "transduce" "transient" |
||||||
|
"tree-seq" "true?" "type" "unchecked-add" "unchecked-add-int" |
||||||
|
"unchecked-byte" "unchecked-char" "unchecked-dec" |
||||||
|
"unchecked-dec-int" "unchecked-divide-int" "unchecked-double" |
||||||
|
"unchecked-float" "unchecked-inc" "unchecked-inc-int" |
||||||
|
"unchecked-int" "unchecked-long" "unchecked-multiply" |
||||||
|
"unchecked-multiply-int" "unchecked-negate" "unchecked-negate-int" |
||||||
|
"unchecked-remainder-int" "unchecked-short" "unchecked-subtract" |
||||||
|
"unchecked-subtract-int" "underive" "unquote" |
||||||
|
"unquote-splicing" "unreduced" "unsigned-bit-shift-right" |
||||||
|
"update" "update-in" "update-proxy" "uri?" "uuid?" |
||||||
|
"val" "vals" "var-get" "var-set" "var?" "vary-meta" "vec" |
||||||
|
"vector" "vector-of" "vector?" "volatile!" "volatile?" |
||||||
|
"vreset!" "with-bindings*" "with-meta" "with-redefs-fn" "xml-seq" |
||||||
|
"zero?" "zipmap")) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; >> Context based highlighting |
||||||
|
|
||||||
|
; def-likes |
||||||
|
; Correctly highlight docstrings |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_keyword ; Don't really want to highlight twice |
||||||
|
(#lua-match? @_keyword "^def.*") |
||||||
|
. |
||||||
|
(sym_lit) |
||||||
|
. |
||||||
|
;; TODO: Add @comment highlight |
||||||
|
(str_lit)? |
||||||
|
. |
||||||
|
(_)) |
||||||
|
|
||||||
|
; Function definitions |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_keyword.function |
||||||
|
(#any-of? @_keyword.function "fn" "fn*" "defn" "defn-") |
||||||
|
. |
||||||
|
(sym_lit)? @function |
||||||
|
. |
||||||
|
;; TODO: Add @comment highlight |
||||||
|
(str_lit)?) |
||||||
|
;; TODO: Fix parameter highlighting |
||||||
|
;; I think there's a bug here in nvim-treesitter |
||||||
|
;; TODO: Reproduce bug and file ticket |
||||||
|
;. |
||||||
|
;[(vec_lit |
||||||
|
; (sym_lit)* @parameter) |
||||||
|
; (list_lit |
||||||
|
; (vec_lit |
||||||
|
; (sym_lit)* @parameter))]) |
||||||
|
|
||||||
|
;[((list_lit |
||||||
|
; (vec_lit |
||||||
|
; (sym_lit) @parameter) |
||||||
|
; (_) |
||||||
|
; + |
||||||
|
; ((vec_lit |
||||||
|
; (sym_lit) @parameter) |
||||||
|
; (_))) |
||||||
|
|
||||||
|
|
||||||
|
; Meta punctuation |
||||||
|
;; NOTE: When the above `Function definitions` query captures the |
||||||
|
;; the @function it also captures the child meta_lit |
||||||
|
;; We capture the meta_lit symbol (^) after so that the later |
||||||
|
;; highlighting overrides the former |
||||||
|
"^" @punctuation.special |
||||||
|
|
||||||
|
;; namespaces |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_include |
||||||
|
(#eq? @_include "ns") |
||||||
|
. |
||||||
|
(sym_lit) @namespace) |
@ -0,0 +1 @@ |
|||||||
|
(comment) @comment |
@ -0,0 +1 @@ |
|||||||
|
; placeholder file to get incremental selection to work |
@ -0,0 +1,7 @@ |
|||||||
|
[ |
||||||
|
(if_condition) |
||||||
|
(foreach_loop) |
||||||
|
(while_loop) |
||||||
|
(function_def) |
||||||
|
(macro_def) |
||||||
|
] @fold |
@ -0,0 +1,100 @@ |
|||||||
|
[ |
||||||
|
(quoted_argument) |
||||||
|
(bracket_argument) |
||||||
|
] @string |
||||||
|
|
||||||
|
(variable_ref) @none |
||||||
|
(variable) @variable |
||||||
|
|
||||||
|
[ |
||||||
|
(bracket_comment) |
||||||
|
(line_comment) |
||||||
|
] @comment |
||||||
|
|
||||||
|
(normal_command (identifier) @function) |
||||||
|
|
||||||
|
["ENV" "CACHE"] @symbol |
||||||
|
["$" "{" "}" "<" ">"] @punctuation.special |
||||||
|
["(" ")"] @punctuation.bracket |
||||||
|
|
||||||
|
[ |
||||||
|
(function) |
||||||
|
(endfunction) |
||||||
|
(macro) |
||||||
|
(endmacro) |
||||||
|
] @keyword.function |
||||||
|
|
||||||
|
[ |
||||||
|
(if) |
||||||
|
(elseif) |
||||||
|
(else) |
||||||
|
(endif) |
||||||
|
] @conditional |
||||||
|
|
||||||
|
[ |
||||||
|
(foreach) |
||||||
|
(endforeach) |
||||||
|
(while) |
||||||
|
(endwhile) |
||||||
|
] @repeat |
||||||
|
|
||||||
|
(function_command |
||||||
|
(function) |
||||||
|
. (argument) @function |
||||||
|
(argument)* @parameter |
||||||
|
) |
||||||
|
|
||||||
|
(macro_command |
||||||
|
(macro) |
||||||
|
. (argument) @function.macro |
||||||
|
(argument)* @parameter |
||||||
|
) |
||||||
|
|
||||||
|
(normal_command |
||||||
|
(identifier) @function.builtin |
||||||
|
. (argument) @variable |
||||||
|
(#match? @function.builtin "\\c^(set)$")) |
||||||
|
|
||||||
|
(normal_command |
||||||
|
(identifier) @function.builtin |
||||||
|
. (argument) |
||||||
|
. (argument) |
||||||
|
(argument) @constant |
||||||
|
(#any-of? @constant "PARENT_SCOPE" "CACHE" "FORCE") |
||||||
|
(#match? @function.builtin "\\c^(set)$") |
||||||
|
) |
||||||
|
|
||||||
|
((argument) @boolean |
||||||
|
(#match? @boolean "\\c^(1|on|yes|true|y|0|off|no|false|n|ignore|notfound|.*-notfound)$") |
||||||
|
) |
||||||
|
|
||||||
|
(if_command |
||||||
|
(if) |
||||||
|
(argument) @keyword.operator |
||||||
|
(#any-of? @keyword.operator "NOT" "AND" "OR" |
||||||
|
"COMMAND" "POLICY" "TARGET" "TEST" "DEFINED" "IN_LIST" |
||||||
|
"EXISTS" "IS_NEWER_THAN" "IS_DIRECTORY" "IS_SYMLINK" "IS_ABSOLUTE" |
||||||
|
"MATCHES" |
||||||
|
"LESS" "GREATER" "EQUAL" "LESS_EQUAL" "GREATER_EQUAL" |
||||||
|
"STRLESS" "STRGREATER" "STREQUAL" "STRLESS_EQUAL" "STRGREATER_EQUAL" |
||||||
|
"VERSION_LESS" "VERSION_GREATER" "VERSION_EQUAL" "VERSION_LESS_EQUAL" "VERSION_GREATER_EQUAL" |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
(normal_command |
||||||
|
(identifier) @function.builtin |
||||||
|
. (argument) |
||||||
|
(argument) @constant |
||||||
|
(#any-of? @constant "ALL" "COMMAND" "DEPENDS" "BYPRODUCTS" "WORKING_DIRECTORY" "COMMENT" |
||||||
|
"JOB_POOL" "VERBATIM" "USES_TERMINAL" "COMMAND_EXPAND_LISTS" "SOURCES") |
||||||
|
(#match? @function.builtin "\\c^(add_custom_target)$") |
||||||
|
) |
||||||
|
|
||||||
|
(normal_command |
||||||
|
(identifier) @function.builtin |
||||||
|
(argument) @constant |
||||||
|
(#any-of? @constant "OUTPUT" "COMMAND" "MAIN_DEPENDENCY" "DEPENDS" "BYPRODUCTS" "IMPLICIT_DEPENDS" "WORKING_DIRECTORY" |
||||||
|
"COMMENT" "DEPFILE" "JOB_POOL" "VERBATIM" "APPEND" "USES_TERMINAL" "COMMAND_EXPAND_LISTS") |
||||||
|
(#match? @function.builtin "\\c^(add_custom_command)$") |
||||||
|
) |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
[ |
||||||
|
"(" |
||||||
|
")" |
||||||
|
] @punctuation.bracket |
||||||
|
|
||||||
|
":" @punctuation.delimiter |
||||||
|
|
||||||
|
(tag (name) @text.note (user)? @constant) |
||||||
|
|
||||||
|
((tag ((name) @text.warning)) |
||||||
|
(#any-of? @text.warning "TODO" "HACK" "WARNING")) |
||||||
|
|
||||||
|
("text" @text.warning |
||||||
|
(#any-of? @text.warning "TODO" "HACK" "WARNING")) |
||||||
|
|
||||||
|
((tag ((name) @text.danger)) |
||||||
|
(#any-of? @text.danger "FIXME" "XXX" "BUG")) |
||||||
|
|
||||||
|
("text" @text.danger |
||||||
|
(#any-of? @text.danger "FIXME" "XXX" "BUG")) |
||||||
|
|
||||||
|
; Issue number (#123) |
||||||
|
("text" @number (#lua-match? @number "^#[0-9]+$")) |
||||||
|
; User mention (@user) |
||||||
|
("text" @constant (#lua-match? @constant "^[@][a-zA-Z0-9_-]+$")) |
@ -0,0 +1 @@ |
|||||||
|
(source (list_lit) @fold) |
@ -0,0 +1,187 @@ |
|||||||
|
(sym_lit) @variable |
||||||
|
|
||||||
|
;; A highlighting for functions/macros in th cl namespace is available in theHamsta/nvim-treesitter-commonlisp |
||||||
|
;(list_lit . (sym_lit) @function.builtin (#cl-standard-function? @function.builtin)) |
||||||
|
;(list_lit . (sym_lit) @function.builtin (#cl-standard-macro? @function.macro)) |
||||||
|
|
||||||
|
(dis_expr) @comment |
||||||
|
|
||||||
|
(defun_keyword) @function.macro |
||||||
|
(defun_header |
||||||
|
function_name: (_) @function) |
||||||
|
(defun_header |
||||||
|
lambda_list: (list_lit (sym_lit) @parameter)) |
||||||
|
(defun_header |
||||||
|
keyword: (defun_keyword "defmethod") |
||||||
|
lambda_list: (list_lit (list_lit . (sym_lit) . (sym_lit) @symbol))) |
||||||
|
(defun_header |
||||||
|
lambda_list: (list_lit (list_lit . (sym_lit) @parameter . (_)))) |
||||||
|
(defun_header |
||||||
|
specifier: (sym_lit) @symbol) |
||||||
|
|
||||||
|
[":" "::" "."] @punctuation.special |
||||||
|
|
||||||
|
[ |
||||||
|
(accumulation_verb) |
||||||
|
(for_clause_word) |
||||||
|
"for" |
||||||
|
"and" |
||||||
|
"finally" |
||||||
|
"thereis" |
||||||
|
"always" |
||||||
|
"when" |
||||||
|
"if" |
||||||
|
"unless" |
||||||
|
"else" |
||||||
|
"do" |
||||||
|
"loop" |
||||||
|
"below" |
||||||
|
"in" |
||||||
|
"from" |
||||||
|
"across" |
||||||
|
"repeat" |
||||||
|
"being" |
||||||
|
"into" |
||||||
|
"with" |
||||||
|
"as" |
||||||
|
"while" |
||||||
|
"until" |
||||||
|
"return" |
||||||
|
"initially" |
||||||
|
] @function.macro |
||||||
|
"=" @operator |
||||||
|
|
||||||
|
(include_reader_macro) @symbol |
||||||
|
["#C" "#c"] @number |
||||||
|
|
||||||
|
[(kwd_lit) (self_referential_reader_macro)] @symbol |
||||||
|
|
||||||
|
(package_lit |
||||||
|
package: (_) @namespace) |
||||||
|
"cl" @namespace |
||||||
|
|
||||||
|
(str_lit) @string |
||||||
|
|
||||||
|
(num_lit) @number |
||||||
|
|
||||||
|
((sym_lit) @boolean (#match? @boolean "^(t|T)$")) |
||||||
|
|
||||||
|
(nil_lit) @constant.builtin |
||||||
|
|
||||||
|
(comment) @comment |
||||||
|
|
||||||
|
;; dynamic variables |
||||||
|
((sym_lit) @variable.builtin |
||||||
|
(#match? @variable.builtin "^[*].+[*]$")) |
||||||
|
|
||||||
|
;; quote |
||||||
|
"'" @string.escape |
||||||
|
(format_specifier) @string.escape |
||||||
|
(quoting_lit) @string.escape |
||||||
|
|
||||||
|
;; syntax quote |
||||||
|
"`" @string.escape |
||||||
|
"," @string.escape |
||||||
|
",@" @string.escape |
||||||
|
(syn_quoting_lit) @string.escape |
||||||
|
(unquoting_lit) @none |
||||||
|
(unquote_splicing_lit) @none |
||||||
|
|
||||||
|
|
||||||
|
["(" ")"] @punctuation.bracket |
||||||
|
|
||||||
|
(block_comment) @comment |
||||||
|
|
||||||
|
|
||||||
|
(with_clause |
||||||
|
type: (_) @type) |
||||||
|
(for_clause |
||||||
|
type: (_) @type) |
||||||
|
|
||||||
|
;; defun-like things |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @function.macro |
||||||
|
. |
||||||
|
(sym_lit) @function |
||||||
|
(#eq? @function.macro "deftest")) |
||||||
|
|
||||||
|
;;; Macros and Special Operators |
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @function.macro |
||||||
|
;; For a complete and more efficient version install theHamsta/nvim-treesitter-commonlisp |
||||||
|
(#any-of? @function.macro |
||||||
|
"let" |
||||||
|
"function" |
||||||
|
"the" |
||||||
|
"unwind-protect" |
||||||
|
"labels" |
||||||
|
"flet" |
||||||
|
"tagbody" |
||||||
|
"go" |
||||||
|
"symbol-macrolet" |
||||||
|
"symbol-macrolet" |
||||||
|
"progn" |
||||||
|
"prog1" |
||||||
|
"error" |
||||||
|
"or" |
||||||
|
"and" |
||||||
|
"defvar" |
||||||
|
"defparameter" |
||||||
|
"in-package" |
||||||
|
"defpackage" |
||||||
|
"case" |
||||||
|
"ecase" |
||||||
|
"typecase" |
||||||
|
"etypecase" |
||||||
|
"defstruct" |
||||||
|
"defclass" |
||||||
|
"if" |
||||||
|
"when" |
||||||
|
"unless" |
||||||
|
"cond" |
||||||
|
"switch" |
||||||
|
"declaim" |
||||||
|
"optimize")) |
||||||
|
|
||||||
|
;; constant |
||||||
|
((sym_lit) @constant |
||||||
|
(#match? @constant "^[+].+[+]$")) |
||||||
|
|
||||||
|
(var_quoting_lit |
||||||
|
marker: "#'" @symbol |
||||||
|
value: (_) @symbol) |
||||||
|
|
||||||
|
["#" "#p" "#P"] @symbol |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @function.builtin |
||||||
|
;; For a complete and more efficient version install theHamsta/nvim-treesitter-commonlisp |
||||||
|
(#any-of? @function.builtin |
||||||
|
"mapcar" |
||||||
|
"reduce" |
||||||
|
"remove-if-not" |
||||||
|
"cons" |
||||||
|
"car" |
||||||
|
"last" |
||||||
|
"nth" |
||||||
|
"equal" |
||||||
|
"cdr" |
||||||
|
"first" |
||||||
|
"rest" |
||||||
|
"format")) |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @operator |
||||||
|
(#match? @operator "^([+*-+=<>]|<=|>=|/=)$")) |
||||||
|
|
||||||
|
|
||||||
|
((sym_lit) @symbol |
||||||
|
(#match? @symbol "^[&]")) |
||||||
|
|
||||||
|
[(array_dimension) "#0A" "#0a"] @number |
||||||
|
|
||||||
|
(char_lit) @character |
@ -0,0 +1,72 @@ |
|||||||
|
|
||||||
|
(defun_header |
||||||
|
function_name: (sym_lit) @definition.function (#set! definition.function.scope "parent")) |
||||||
|
(defun_header |
||||||
|
lambda_list: (list_lit (sym_lit) @definition.parameter)) |
||||||
|
|
||||||
|
(defun_header |
||||||
|
keyword: (defun_keyword "defmethod") |
||||||
|
lambda_list: (list_lit (list_lit . (sym_lit) . (sym_lit) @definition.type))) |
||||||
|
(defun_header |
||||||
|
lambda_list: (list_lit (list_lit . (sym_lit) @definition.parameter . (_)))) |
||||||
|
|
||||||
|
(sym_lit) @reference |
||||||
|
|
||||||
|
(defun) @scope |
||||||
|
|
||||||
|
((list_lit . (sym_lit) @_defvar . (sym_lit) @definition.var) |
||||||
|
(#match? @_defvar "^(cl:)?(defvar|defparameter)$")) |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_deftest |
||||||
|
. |
||||||
|
(sym_lit) @definition.function |
||||||
|
(#match? @_deftest "^(deftest)$")) @scope |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_deftest |
||||||
|
. |
||||||
|
(sym_lit) @definition.function |
||||||
|
(#match? @_deftest "^(deftest)$")) @scope |
||||||
|
|
||||||
|
(for_clause . (sym_lit) @definition.var) |
||||||
|
(with_clause . (sym_lit) @definition.var) |
||||||
|
(loop_macro) @scope |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_let (#match? @_let "(cl:|cffi:)?(with-accessors|with-foreign-objects|let[*]?)") |
||||||
|
. |
||||||
|
(list_lit (list_lit . (sym_lit) @definition.var))) @scope |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(sym_lit) @_let (#match? @_let "(cl:|alexandria:)?(with-gensyms|dotimes|with-foreign-object)") |
||||||
|
. |
||||||
|
(list_lit . (sym_lit) @definition.var)) @scope |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(kwd_lit) @_import_from (#eq? @_import_from ":import-from") |
||||||
|
. |
||||||
|
(_) |
||||||
|
(kwd_lit (kwd_symbol) @definition.import)) |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(kwd_lit) @_import_from (#eq? @_import_from ":import-from") |
||||||
|
. |
||||||
|
(_) |
||||||
|
(sym_lit) @definition.import) |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(kwd_lit) @_use (#eq? @_use ":use") |
||||||
|
(kwd_lit (kwd_symbol) @definition.import)) |
||||||
|
|
||||||
|
(list_lit |
||||||
|
. |
||||||
|
(kwd_lit) @_use (#eq? @_use ":use") |
||||||
|
(sym_lit) @definition.import) |
@ -0,0 +1,14 @@ |
|||||||
|
; inherits: c |
||||||
|
|
||||||
|
[ |
||||||
|
(for_range_loop) |
||||||
|
(class_specifier) |
||||||
|
(field_declaration |
||||||
|
type: (enum_specifier) |
||||||
|
default_value: (initializer_list)) |
||||||
|
(template_declaration) |
||||||
|
(namespace_definition) |
||||||
|
(try_statement) |
||||||
|
(catch_clause) |
||||||
|
(lambda_expression) |
||||||
|
] @fold |
@ -0,0 +1,167 @@ |
|||||||
|
; inherits: c |
||||||
|
|
||||||
|
((identifier) @field |
||||||
|
(#match? @field "(^_|^m_|_$)")) |
||||||
|
|
||||||
|
(parameter_declaration |
||||||
|
declarator: (reference_declarator) @parameter) |
||||||
|
; function(Foo ...foo) |
||||||
|
(variadic_parameter_declaration |
||||||
|
declarator: (variadic_declarator |
||||||
|
(_) @parameter)) |
||||||
|
; int foo = 0 |
||||||
|
(optional_parameter_declaration |
||||||
|
declarator: (_) @parameter) |
||||||
|
|
||||||
|
;(field_expression) @parameter ;; How to highlight this? |
||||||
|
(template_function |
||||||
|
name: (identifier) @function) |
||||||
|
|
||||||
|
(template_method |
||||||
|
name: (field_identifier) @method) |
||||||
|
|
||||||
|
(((field_expression |
||||||
|
(field_identifier) @method)) @_parent |
||||||
|
(#has-parent? @_parent template_method function_declarator call_expression)) |
||||||
|
|
||||||
|
(field_initializer |
||||||
|
(field_identifier) @property) |
||||||
|
|
||||||
|
(function_declarator |
||||||
|
declarator: (field_identifier) @method) |
||||||
|
|
||||||
|
(concept_definition |
||||||
|
name: (identifier) @type) |
||||||
|
|
||||||
|
(namespace_identifier) @namespace |
||||||
|
((namespace_identifier) @type |
||||||
|
(#lua-match? @type "^[A-Z]")) |
||||||
|
((namespace_identifier) @constant |
||||||
|
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) |
||||||
|
(namespace_definition |
||||||
|
name: (identifier) @namespace) |
||||||
|
|
||||||
|
(using_declaration . "using" . "namespace" . [(qualified_identifier) (identifier)] @namespace) |
||||||
|
|
||||||
|
(destructor_name |
||||||
|
(identifier) @method) |
||||||
|
|
||||||
|
(function_declarator |
||||||
|
declarator: (qualified_identifier |
||||||
|
name: (identifier) @function)) |
||||||
|
((function_declarator |
||||||
|
declarator: (qualified_identifier |
||||||
|
name: (identifier) @constructor)) |
||||||
|
(#lua-match? @constructor "^[A-Z]")) |
||||||
|
|
||||||
|
(operator_name) @function |
||||||
|
"static_assert" @function.builtin |
||||||
|
|
||||||
|
(call_expression |
||||||
|
function: (qualified_identifier |
||||||
|
name: (identifier) @function)) |
||||||
|
|
||||||
|
(call_expression |
||||||
|
function: (field_expression |
||||||
|
field: (field_identifier) @function)) |
||||||
|
|
||||||
|
((call_expression |
||||||
|
function: (identifier) @constructor) |
||||||
|
(#lua-match? @constructor "^[A-Z]")) |
||||||
|
((call_expression |
||||||
|
function: (qualified_identifier |
||||||
|
name: (identifier) @constructor)) |
||||||
|
(#lua-match? @constructor "^[A-Z]")) |
||||||
|
|
||||||
|
((call_expression |
||||||
|
function: (field_expression |
||||||
|
field: (field_identifier) @constructor)) |
||||||
|
(#lua-match? @constructor "^[A-Z]")) |
||||||
|
|
||||||
|
;; constructing a type in an initializer list: Constructor (): **SuperType (1)** |
||||||
|
((field_initializer |
||||||
|
(field_identifier) @constructor |
||||||
|
(argument_list)) |
||||||
|
(#lua-match? @constructor "^[A-Z]")) |
||||||
|
|
||||||
|
|
||||||
|
; Constants |
||||||
|
|
||||||
|
(this) @variable.builtin |
||||||
|
(nullptr) @constant |
||||||
|
|
||||||
|
(true) @boolean |
||||||
|
(false) @boolean |
||||||
|
|
||||||
|
; Literals |
||||||
|
|
||||||
|
(raw_string_literal) @string |
||||||
|
|
||||||
|
; Keywords |
||||||
|
|
||||||
|
[ |
||||||
|
"try" |
||||||
|
"catch" |
||||||
|
"noexcept" |
||||||
|
"throw" |
||||||
|
] @exception |
||||||
|
|
||||||
|
|
||||||
|
[ |
||||||
|
"class" |
||||||
|
"decltype" |
||||||
|
"constexpr" |
||||||
|
"explicit" |
||||||
|
"final" |
||||||
|
"friend" |
||||||
|
"mutable" |
||||||
|
"namespace" |
||||||
|
"override" |
||||||
|
"private" |
||||||
|
"protected" |
||||||
|
"public" |
||||||
|
"template" |
||||||
|
"typename" |
||||||
|
"using" |
||||||
|
"virtual" |
||||||
|
"co_await" |
||||||
|
"concept" |
||||||
|
"requires" |
||||||
|
"consteval" |
||||||
|
"constinit" |
||||||
|
(auto) |
||||||
|
] @keyword |
||||||
|
|
||||||
|
[ |
||||||
|
"co_yield" |
||||||
|
"co_return" |
||||||
|
] @keyword.return |
||||||
|
|
||||||
|
[ |
||||||
|
"new" |
||||||
|
"delete" |
||||||
|
|
||||||
|
;; these keywords are not supported by the parser |
||||||
|
;"eq" |
||||||
|
;"not_eq" |
||||||
|
; |
||||||
|
;"compl" |
||||||
|
;"and" |
||||||
|
;"or" |
||||||
|
; |
||||||
|
;"bitand" |
||||||
|
;"bitand_eq" |
||||||
|
;"bitor" |
||||||
|
;"bitor_eq" |
||||||
|
;"xor" |
||||||
|
;"xor_eq" |
||||||
|
] @keyword.operator |
||||||
|
|
||||||
|
[ |
||||||
|
"<=>" |
||||||
|
"::" |
||||||
|
] @operator |
||||||
|
|
||||||
|
(attribute_declaration) @attribute |
||||||
|
|
||||||
|
(literal_suffix) @operator |
@ -0,0 +1,8 @@ |
|||||||
|
; inherits: c |
||||||
|
|
||||||
|
[ |
||||||
|
(class_specifier) |
||||||
|
(condition_clause) |
||||||
|
] @indent |
||||||
|
|
||||||
|
(access_specifier) @branch |
@ -0,0 +1,3 @@ |
|||||||
|
(preproc_arg) @cpp |
||||||
|
|
||||||
|
(comment) @comment |
@ -0,0 +1,71 @@ |
|||||||
|
; inherits: c |
||||||
|
|
||||||
|
;; Parameters |
||||||
|
(variadic_parameter_declaration |
||||||
|
declarator: (variadic_declarator |
||||||
|
(identifier) @definition.parameter)) |
||||||
|
(optional_parameter_declaration |
||||||
|
declarator: (identifier) @definition.parameter) |
||||||
|
;; Class / struct definitions |
||||||
|
(class_specifier) @scope |
||||||
|
|
||||||
|
(reference_declarator |
||||||
|
(identifier) @definition.var) |
||||||
|
|
||||||
|
(variadic_declarator |
||||||
|
(identifier) @definition.var) |
||||||
|
|
||||||
|
(struct_specifier |
||||||
|
name: (qualified_identifier |
||||||
|
name: (type_identifier) @definition.type)) |
||||||
|
|
||||||
|
(class_specifier |
||||||
|
name: (type_identifier) @definition.type) |
||||||
|
|
||||||
|
(concept_definition |
||||||
|
name: (identifier) @definition.type) |
||||||
|
|
||||||
|
(class_specifier |
||||||
|
name: (qualified_identifier |
||||||
|
name: (type_identifier) @definition.type)) |
||||||
|
|
||||||
|
(alias_declaration |
||||||
|
name: (type_identifier) @definition.type) |
||||||
|
|
||||||
|
;template <typename T> |
||||||
|
(type_parameter_declaration |
||||||
|
(type_identifier) @definition.type) |
||||||
|
(template_declaration) @scope |
||||||
|
|
||||||
|
;; Namespaces |
||||||
|
(namespace_definition |
||||||
|
name: (identifier) @definition.namespace |
||||||
|
body: (_) @scope) |
||||||
|
|
||||||
|
((namespace_identifier) @reference |
||||||
|
(set! reference.kind "namespace")) |
||||||
|
|
||||||
|
;; Function definitions |
||||||
|
(template_function |
||||||
|
name: (identifier) @definition.function) @scope |
||||||
|
|
||||||
|
(template_method |
||||||
|
name: (field_identifier) @definition.method) @scope |
||||||
|
|
||||||
|
(function_declarator |
||||||
|
declarator: (qualified_identifier |
||||||
|
name: (identifier) @definition.function)) @scope |
||||||
|
|
||||||
|
(field_declaration |
||||||
|
declarator: (function_declarator |
||||||
|
(field_identifier) @definition.method)) |
||||||
|
|
||||||
|
(lambda_expression) @scope |
||||||
|
|
||||||
|
;; Control structures |
||||||
|
(try_statement |
||||||
|
body: (_) @scope) |
||||||
|
|
||||||
|
(catch_clause) @scope |
||||||
|
|
||||||
|
(requires_expression) @scope |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue