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:
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