From 692b051b09935653befdb8f7ba8afdb640adf17b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 12 Jun 2023 09:54:30 -0600 Subject: feat!: drop modules, general refactor and cleanup --- plugin/filetypes.lua | 66 +++++++++++++++++++++ plugin/nvim-treesitter.lua | 104 ++++++++++++++++++++++++-------- plugin/query_predicates.lua | 141 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 26 deletions(-) create mode 100644 plugin/filetypes.lua create mode 100644 plugin/query_predicates.lua (limited to 'plugin') diff --git a/plugin/filetypes.lua b/plugin/filetypes.lua new file mode 100644 index 000000000..a4c236ec3 --- /dev/null +++ b/plugin/filetypes.lua @@ -0,0 +1,66 @@ +local filetypes = { + angular = { 'htmlangular' }, + bash = { 'sh' }, + bibtex = { 'bib' }, + c_sharp = { 'cs', 'csharp', 'c-sharp' }, + commonlisp = { 'lisp' }, + cooklang = { 'cook' }, + devicetree = { 'dts' }, + diff = { 'gitdiff' }, + eex = { 'eelixir' }, + elixir = { 'ex' }, + embedded_template = { 'eruby' }, + erlang = { 'erl' }, + facility = { 'fsd' }, + faust = { 'dsp' }, + gdshader = { 'gdshaderinc' }, + git_config = { 'gitconfig' }, + git_rebase = { 'gitrebase' }, + glimmer = { 'handlebars', 'html.handlebars' }, + godot_resource = { 'gdresource' }, + haskell = { 'hs' }, + haskell_persistent = { 'haskellpersistent' }, + idris = { 'idris2' }, + janet_simple = { 'janet' }, + javascript = { 'javascriptreact', 'ecma', 'jsx', 'js' }, + javascript_glimmer = { 'javascript.glimmer' }, + linkerscript = { 'ld' }, + latex = { 'tex' }, + m68k = { 'asm68k' }, + make = { 'automake' }, + markdown = { 'pandoc', 'quarto', 'rmd' }, + muttrc = { 'neomuttrc' }, + ocaml_interface = { 'ocamlinterface' }, + perl = { 'pl' }, + poe_filter = { 'poefilter' }, + properties = { 'jproperties' }, + python = { 'py', 'gyp' }, + qmljs = { 'qml' }, + runescript = { 'clientscript' }, + scala = { 'sbt' }, + slang = { 'shaderslang' }, + sqp = { 'mysqp' }, + ssh_config = { 'sshconfig' }, + starlark = { 'bzl' }, + surface = { 'sface' }, + t32 = { 'trace32' }, + tcl = { 'expect' }, + terraform = { 'terraform-vars' }, + textproto = { 'pbtxt' }, + tlaplus = { 'tla' }, + tsx = { 'typescriptreact', 'typescript.tsx' }, + typescript = { 'ts' }, + typescript_glimmer = { 'typescript.glimmer' }, + typst = { 'typ' }, + udev = { 'udevrules' }, + uxntal = { 'tal', 'uxn' }, + v = { 'vlang' }, + verilog = { 'systemverilog' }, + vhs = { 'tape' }, + xml = { 'xsd', 'xslt', 'svg' }, + xresources = { 'xdefaults' }, +} + +for lang, ft in pairs(filetypes) do + vim.treesitter.language.register(lang, ft) +end diff --git a/plugin/nvim-treesitter.lua b/plugin/nvim-treesitter.lua index 4ea3925fd..3ab264d41 100644 --- a/plugin/nvim-treesitter.lua +++ b/plugin/nvim-treesitter.lua @@ -1,34 +1,86 @@ --- Last Change: 2022 Apr 16 - if vim.g.loaded_nvim_treesitter then return end vim.g.loaded_nvim_treesitter = true --- setup modules -require("nvim-treesitter").setup() - local api = vim.api --- define autocommands -local augroup = api.nvim_create_augroup("NvimTreesitter", {}) - -api.nvim_create_autocmd("Filetype", { - pattern = "query", - group = augroup, - callback = function() - api.nvim_clear_autocmds { - group = augroup, - event = "BufWritePost", - } - api.nvim_create_autocmd("BufWritePost", { - group = augroup, - buffer = 0, - callback = function(opts) - require("nvim-treesitter.query").invalidate_query_file(opts.file) - end, - desc = "Invalidate query file", - }) - end, - desc = "Reload query", +local function complete_available_parsers(arglead) + return vim.iter.filter(function(v) + return v:find(arglead) + end, require('nvim-treesitter.parsers').get_available()) +end + +local function complete_installed_parsers(arglead) + return vim.iter.filter(function(v) + return v:find(arglead) + end, require('nvim-treesitter.config').installed_parsers()) +end + +-- create user commands +api.nvim_create_user_command('TSInstallInfo', function() + require('nvim-treesitter.install').info() +end, { nargs = 0, desc = 'List available treesitter parsers' }) + +api.nvim_create_user_command('TSInstall', function(args) + require('nvim-treesitter.install').install(args.fargs, { force = args.bang }) +end, { + nargs = '+', + bang = true, + bar = true, + complete = complete_available_parsers, + desc = 'Install treesitter parsers', +}) + +api.nvim_create_user_command('TSInstallFromGrammar', function(args) + require('nvim-treesitter.install').install(args.fargs, { + generate_from_grammar = true, + force = args.bang, + }) +end, { + nargs = '+', + bang = true, + bar = true, + complete = complete_available_parsers, + desc = 'Install treesitter parsers from grammar', +}) + +api.nvim_create_user_command('TSInstallSync', function(args) + require('nvim-treesitter.install').install(args.fargs, { + with_sync = true, + force = args.bang, + }) +end, { + nargs = '+', + bang = true, + bar = true, + complete = complete_available_parsers, + desc = 'Install treesitter parsers synchronously', +}) + +api.nvim_create_user_command('TSUpdate', function(args) + require('nvim-treesitter.install').update(args.fargs) +end, { + nargs = '*', + bar = true, + complete = complete_installed_parsers, + desc = 'Update installed treesitter parsers', +}) + +api.nvim_create_user_command('TSUpdateSync', function(args) + require('nvim-treesitter.install').update(args.fargs, { with_sync = true }) +end, { + nargs = '*', + bar = true, + complete = complete_installed_parsers, + desc = 'Update installed treesitter parsers synchronously', +}) + +api.nvim_create_user_command('TSUninstall', function(args) + require('nvim-treesitter.install').uninstall(args.fargs) +end, { + nargs = '+', + bar = true, + complete = complete_installed_parsers, + desc = 'Uninstall treesitter parsers', }) diff --git a/plugin/query_predicates.lua b/plugin/query_predicates.lua new file mode 100644 index 000000000..abffea2f0 --- /dev/null +++ b/plugin/query_predicates.lua @@ -0,0 +1,141 @@ +local query = vim.treesitter.query + +-- register custom predicates + +---@param match (TSNode|nil)[] +---@param pred string[] +---@return boolean|nil +query.add_predicate('kind-eq?', function(match, _, _, pred) + local node = match[pred[2]] + if not node then + return true + end + + local types = { unpack(pred, 3) } + return vim.list_contains(types, node:type()) +end) + +-- register custom directives + +local mimetype_aliases = { + ['importmap'] = 'json', + ['module'] = 'javascript', + ['application/ecmascript'] = 'javascript', + ['text/ecmascript'] = 'javascript', +} + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive('set-lang-from-mimetype!', function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + local type_attr_value = vim.treesitter.get_node_text(node, bufnr) + local configured = mimetype_aliases[type_attr_value] + if configured then + metadata['injection.language'] = configured + else + local parts = vim.split(type_attr_value, '/', {}) + metadata['injection.language'] = parts[#parts] + end +end) + +local injection_aliases = { + ex = 'elixir', + pl = 'perl', + sh = 'bash', + uxn = 'uxntal', + ts = 'typescript', +} + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive('set-lang-from-info-string!', function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + + local injection_alias = vim.treesitter.get_node_text(node, bufnr) + local filetype = vim.filetype.match({ filename = 'a.' .. injection_alias }) + metadata['injection.language'] = filetype or injection_aliases[injection_alias] or injection_alias +end) + +query.add_directive('downcase!', function(match, _, bufnr, pred, metadata) + local text, key, value ---@type string|string[], string, string|integer + + 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 = vim.treesitter.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) + +-- Trim blank lines from end of the region +-- Arguments are the captures to trim. +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@param metadata table +---TODO(clason): upstream +query.add_directive('trim!', function(match, _, bufnr, pred, metadata) + for _, id in ipairs({ select(2, unpack(pred)) }) do + local node = match[id] + if not node then + return + end + + local start_row, start_col, end_row, end_col = node:range() + + -- Don't trim if region ends in middle of a line + if end_col ~= 0 then + return + end + + while true do + -- As we only care when end_col == 0, always inspect one line above end_row. + local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1] + + if end_line ~= '' then + break + end + + end_row = end_row - 1 + end + + -- If this produces an invalid range, we just skip it. + if start_row < end_row or (start_row == end_row and start_col <= end_col) then + if not metadata[id] then + metadata[id] = {} + end + metadata[id].range = { start_row, start_col, end_row, end_col } + end + end +end) -- cgit v1.2.3-70-g09d2