nvim-config

Log | Files | Refs | Submodules | README

commit 689818327c485e5cd413e94a61b1428f90e5d285
parent 6827d3da3d6fea36f83f09bf5139740ba7e7d682
Author: Thomas Vigouroux <thomas.vigouroux@protonmail.com>
Date:   Tue, 25 Jul 2023 15:38:59 +0200

feat: update tex config

Diffstat:
Mafter/ftplugin/tex.lua | 126++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Alua/mytsutils.lua | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mqueries/latex/highlights.scm | 1+
3 files changed, 155 insertions(+), 52 deletions(-)

diff --git a/after/ftplugin/tex.lua b/after/ftplugin/tex.lua @@ -21,39 +21,9 @@ vim.api.nvim_buf_set_keymap(0, 'i', '<BS>', '', { callback = backspace }) local edit = require "architext.edit" local ts_utils = require 'nvim-treesitter.ts_utils' +local utls = require "mytsutils" local p = require 'nvim-treesitter.parsers' -local function index_by_name(query, match, key) - for id, node, _ in pairs(match) do - if query.captures[id] == key then - return node - end - end -end - -local function find_smallest_match(line, col, query, bufnr) - local root = ts_utils.get_root_for_position(line, col) - - local smallest - local smallnode - local smalllen - for _, match, _ in query:iter_matches(root, bufnr, line, line + 1) do - local node = index_by_name(query, match, "_root") - if node then - if vim.treesitter.node_contains(node, { line, col, line, col }) then - local thislen = ts_utils.node_length(node) - if not smallest or smalllen > thislen then - smallest = match - smalllen = thislen - smallnode = node - end - end - end - end - - return smallest, smallnode -end - local function with_current_position(func) return function() local curbuf = vim.api.nvim_get_current_buf() @@ -77,16 +47,16 @@ do ]]) vim.keymap.set('n', '<LocalLeader>re', with_current_position(function(curbuf, _, cursor) - local match, node = find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf) + local match, node = utls.find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf) - if not match then + if not match or not node then print("Not in an environment") return end local start_row, _, end_row, _ = node:range() - local default = vim.treesitter.get_node_text(index_by_name(query, match, "envbegin"), curbuf) + local default = vim.treesitter.get_node_text(utls.index_by_name(query, match, "envbegin"), curbuf) vim.ui.input({ prompt = "Replacement: ", default = default }, function(replacement) if replacement then @@ -96,7 +66,7 @@ do end), { buffer = true }) vim.keymap.set('n', '<LocalLeader>se', with_current_position(function(curbuf, _, cursor) - local match, node = find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf) + local match, node = utls.find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf) if not match then print("Not in an environment") @@ -105,7 +75,7 @@ do local start_row, _, end_row, _ = node:range() - local current = vim.treesitter.get_node_text(index_by_name(query, match, "envbegin"), curbuf) + local current = vim.treesitter.get_node_text(utls.index_by_name(query, match, "envbegin"), curbuf) if vim.endswith(current, "*") then current = current:sub(0, #current - 1) @@ -117,23 +87,75 @@ do end), { buffer = true }) end -do - local query = vim.treesitter.query.parse("latex", [[ - [ - (inline_formula) - (displayed_equation) - (math_environment) - ] @_root - ]]) +--- Sets up a set of mappings for ts based text objects +--- +--- The matches must have an `outer` capture to denote the 'a' mapping selection, and either `inner` +--- or `inner.left` and `inner.right` captures to denote the 'i' mapping selection. +---@param query Query The query to use +---@param tail string The "tail" of the mapping like '(' in 'a(' +local function query_textobject(query, tail) + vim.keymap.set({ 'o', 'v' }, 'a' .. tail, with_current_position(function(curbuf, _, cursor) + local _, node = utls.find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf, 'outer') + if node then + utls.select(node) + end + end), { buffer = true }) + + vim.keymap.set({ 'o', 'v' }, 'i' .. tail, with_current_position(function(curbuf, _, cursor) + local match = utls.find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf, 'outer') + if not match then return end - vim.keymap.set({ 'o', 'v' }, 'am', with_current_position(function(curbuf, _, cursor) - local _, node = find_smallest_match(cursor[1] - 1, cursor[2], query, curbuf) + local node = utls.index_by_name(query, match, 'inner') + local end_ = utls.index_by_name(query, match, 'inner.end') if node then - ts_utils.update_selection(curbuf, node) + utls.select(node, end_) end end), { buffer = true }) end + +--- Math textobjects +do + local query = vim.treesitter.query.parse("latex", [[ + (displayed_equation . (_) @inner (_)? @inner.end .) @outer + (inline_formula . (_) @inner (_)? @inner.end .) @outer + + (math_environment begin: (_) . (_) @inner (_)? @inner.end . end: (_)) @outer + ]]) + + query_textobject(query, 'm') +end + +--- Environment textobjects +do + local query = vim.treesitter.query.parse("latex", [[ + (generic_environment begin: (_) . (_) @inner (_)? @inner.end . end: (_)) @outer + (math_environment begin: (_) . (_) @inner (_)? @inner.end . end: (_)) @outer + ]]) + + query_textobject(query, 'e') +end + +--- Sections and subsections +do + local query = vim.treesitter.query.parse("latex", [[ + ;; No need to match the inner.end differently: the end of the section is its last node + (section text: _ . (_) @inner) @outer @inner.end + (subsection text: _ . (_) @inner) @outer @inner.end + ]]) + + query_textobject(query, 's') +end + +--- Enumerate items +do + local query = vim.treesitter.query.parse("latex", [[ + (enum_item . (_) @inner) @outer @inner.end + ]]) + + query_textobject(query, 'i') +end + local function buf_line_length(buf, row) return vim.api.nvim_buf_get_offset(buf, row + 1) - vim.api.nvim_buf_get_offset(buf, row) - 1 end @@ -271,9 +293,9 @@ end)) local function toggle_thing(query, left, right) return function(buf, win, cursor) - local match, node = find_smallest_match(cursor[1] - 1, cursor[2], query, buf) + local match, node = utls.find_smallest_match(cursor[1] - 1, cursor[2], query, buf) - local innode = index_by_name(query, match or {}, "in") + local innode = utls.index_by_name(query, match or {}, "in") if not innode then if node then local sline, scol, eline, ecol = vim.treesitter.get_node_range(node) @@ -324,7 +346,7 @@ do ]]) vim.keymap.set('n', '<LocalLeader>df', with_current_position(function(curbuf, curwin, cursor) - local match, node = find_smallest_match(cursor[1] - 1, cursor[2], function_query, curbuf) + local match, node = utls.find_smallest_match(cursor[1] - 1, cursor[2], function_query, curbuf) if not match then print("Not in an function") @@ -333,10 +355,10 @@ do local start_row, _, end_row, _ = node:range() - local innode = index_by_name(function_query, match, "in") + local innode = utls.index_by_name(function_query, match, "in") -- Correct the cursor position (this is at best a guesstimation) - local cname = index_by_name(function_query, match, "_name") + local cname = utls.index_by_name(function_query, match, "_name") local cstartline = cname:start() local srow, scol, _, ecol = innode:range() if cstartline == cursor[1] - 1 then diff --git a/lua/mytsutils.lua b/lua/mytsutils.lua @@ -0,0 +1,80 @@ +local M = {} + +local ts = vim.treesitter +local ts_utils = require 'nvim-treesitter.ts_utils' + +--- Indexes a match by the the name of it's capture +---@param query Query +---@param match TSMatch +---@param key string +---@return TSNode? +function M.index_by_name(query, match, key) + for id, node in pairs(match) do + if query.captures[id] == key then + return node + end + end +end + +--- Finds the smallest match of the given query, containing the given point, in the given buffer +---@param line number Line number to consider +---@param col number Column number to consider +---@param query Query Query to use for matching +---@param bufnr buffer Buffer to consider +---@param cname string? Name of the capture indicating the root of the match (for size computation), defaults to "_root" +---@return TSMatch? match The match containing the node indicated by cname +---@return TSNode? node The node indicated by cname +function M.find_smallest_match(line, col, query, bufnr, cname) + ---@type TSNode? + local root = ts_utils.get_root_for_position(line, col) + if not root then return end + + ---@type TSMatch + local smallest + + ---@type TSNode + local smallnode + + ---@type number + local smalllen + + cname = cname or "_root" + + for _, match, _ in query:iter_matches(root, bufnr, line, line + 1) do + local node = M.index_by_name(query, match, cname) + if node then + if vim.treesitter.node_contains(node, { line, col, line, col }) then + local thislen = ts_utils.node_length(node) + if not smallest or smalllen > thislen then + smallest = match + smalllen = thislen + smallnode = node + end + end + end + end + + return smallest, smallnode +end + + +--- Selects the provided nodes in character visual mode +---@param start TSNode Node to use the get the starting position +---@param stop TSNode? Node to use to get the end position (defaults to start) +function M.select(start, stop) + stop = stop or start + + local start_row, start_col = start:start() + local end_row, end_col = stop:end_() + + -- Force being into visual mode + if vim.api.nvim_get_mode().mode ~= 'v' then + vim.cmd "normal! v" + end + + vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) + vim.cmd "normal! o" + vim.api.nvim_win_set_cursor(0, { end_row + 1, end_col - 1 }) +end + +return M diff --git a/queries/latex/highlights.scm b/queries/latex/highlights.scm @@ -11,6 +11,7 @@ (new_command_definition) (environment_definition) (label_reference) + (label_definition) ] @nospell ;; Exclude some enviroments from spell-checking