Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add vimtex integration #9

Merged
merged 12 commits into from
Jan 31, 2025
64 changes: 64 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ With [lazy.nvim](https://github.com/folke/lazy.nvim):
{
'mawkler/demicolon.nvim',
-- keys = { ';', ',', 't', 'f', 'T', 'F', ']', '[', ']d', '[d' }, -- Uncomment this to lazy load
-- ft = 'tex', -- ...and this if you use LaTeX
dependencies = {
'nvim-treesitter/nvim-treesitter',
'nvim-treesitter/nvim-treesitter-textobjects',
Expand Down Expand Up @@ -79,6 +80,23 @@ demicolon.nvim lets you repeat any [nvim-treesitter-textobjects](https://github.
| `]t`/`[t` | Test | `:help neotest.jump` |
| `]T`/`[T` | Failed test | `:help neotest.jump` |

### [VimTeX](https://github.com/lervag/vimtex) motions

Note that these mappings are only created in normal mode and visual mode. For some reason they don't work when created for operator-pending mode.

| Motion | Jumps to next/pevious... | Help page with more information |
| --------- | ------------------------ | ------------------------------- |
| `][`/`[[` | Section start | `:help vimtex-motions` |
| `]]`/`[]` | Section end | `:help vimtex-motions` |
| `]r`/`[r` | Frame start | `:help vimtex-motions` |
| `]R`/`[R` | Frame end | `:help vimtex-motions` |
| `]n`/`[n` | Math start | `:help vimtex-motions` |
| `]N`/`[N` | Math end | `:help vimtex-motions` |
| `]/`/`[/` | Comment start | `:help vimtex-motions` |
| `]*`/`[*` | Comment end | `:help vimtex-motions` |
| `]m`/`[m` | Environment start | `:help vimtex-motions` |
| `]M`/`[M` | Environment end | `:help vimtex-motions` |

## Configuration

Default options:
Expand Down Expand Up @@ -127,6 +145,52 @@ opts = {
},
},
},
-- Integration with https://github.com/lervag/vimtex
vimtex = {
enabled = true,
keymaps = {
section_start = {
next = '][',
prev = '[[',
},
section_end = {
next = ']]',
prev = '[]',
},
frame_start = {
next = ']r',
prev = '[r',
},
frame_end = {
next = ']R',
prev = '[R',
},
math_start = {
next = ']n',
prev = '[n',
},
math_end = {
next = ']N',
prev = '[N',
},
comment_start = {
next = ']/',
prev = '[/',
},
comment_end = {
next = ']*',
prev = '[*',
},
environment_start = {
next = ']m',
prev = '[m',
},
environment_end = {
next = ']M',
prev = '[M',
},
}
},
},
}
```
Expand Down
2 changes: 1 addition & 1 deletion doc/demicolon.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*demicolon.txt* For NVIM v0.8.0 Last change: 2025 January 24
*demicolon.txt* For NVIM v0.8.0 Last change: 2025 January 29

==============================================================================
Table of Contents *demicolon-table-of-contents*
Expand Down
66 changes: 66 additions & 0 deletions lua/demicolon/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,26 @@ local M = {}
---@field enabled? boolean
---@field keymaps? DemicolonNeotestKeymapOptions

---@class DemicolonVimtexKeymapOptions
---@field section_start? DemicolonIntegrationKeymaps
---@field section_end? DemicolonIntegrationKeymaps
---@field frame_start? DemicolonIntegrationKeymaps
---@field frame_end? DemicolonIntegrationKeymaps
---@field math_start? DemicolonIntegrationKeymaps
---@field math_end? DemicolonIntegrationKeymaps
---@field comment_start? DemicolonIntegrationKeymaps
---@field comment_end? DemicolonIntegrationKeymaps
---@field environment_start? DemicolonIntegrationKeymaps
---@field environment_end? DemicolonIntegrationKeymaps

---@class DemicolonVimtexOptions
---@field enabled? boolean
---@field keymaps? DemicolonVimtexKeymapOptions

---@class DemicolonIntegrationOptions
---@field gitsigns? DemicolonGitsignsOptions Integration with https://github.com/lewis6991/gitsigns.nvim
---@field neotest? DemicolonNeotestOptions Integration with https://github.com/nvim-neotest/neotest
---@field vimtex? DemicolonVimtexOptions Integration with https://github.com/lervag/vimtex

---@class DemicolonOptions
---@field diagnostic? DemicolonDiagnosticOptions Diagnostic options
Expand Down Expand Up @@ -70,6 +87,51 @@ local options = {
},
},
},
vimtex = {
enabled = true,
keymaps = {
section_start = {
next = '][',
prev = '[[',
},
section_end = {
next = ']]',
prev = '[]',
},
frame_start = {
next = ']r',
prev = '[r',
},
frame_end = {
next = ']R',
prev = '[R',
},
math_start = {
next = ']n',
prev = '[n',
},
math_end = {
next = ']N',
prev = '[N',
},
comment_start = {
next = ']/',
prev = '[/',
},
comment_end = {
next = ']*',
prev = '[*',
},
environment_start = {
next = ']m',
prev = '[m',
},
environment_end = {
next = ']M',
prev = '[M',
},
}
}
},
}

Expand Down Expand Up @@ -113,6 +175,10 @@ function M.setup(opts)
if options.integrations.neotest.enabled then
require('demicolon.integrations.neotest').create_keymaps()
end

if options.integrations.vimtex.enabled then
require('demicolon.integrations.vimtex').create_keymaps()
end
end

return M
2 changes: 1 addition & 1 deletion lua/demicolon/integrations/gitsigns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function M.jump(options)
else
local exists, gitsigns = pcall(require, 'gitsigns')
if not exists then
vim.notify('diagnostic.nvim: gitsigns.nvim is not installed', vim.log.levels.WARN)
vim.notify('demicolon.nvim: gitsigns.nvim is not installed', vim.log.levels.WARN)
return
end

Expand Down
2 changes: 1 addition & 1 deletion lua/demicolon/integrations/neotest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function M.jump(options)
require('demicolon.jump').repeatably_do(function(opts)
local exists, neotest = pcall(require, 'neotest')
if not exists then
vim.notify('diagnostic.nvim: neotest is not installed', vim.log.levels.WARN)
vim.notify('demicolon.nvim: neotest is not installed', vim.log.levels.WARN)
return
end

Expand Down
95 changes: 95 additions & 0 deletions lua/demicolon/integrations/vimtex.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
local M = {}

---@param options { forward?: boolean, vimtex_key: string }
function M.jump(options)
return function()
require('demicolon.jump').repeatably_do(function(opts)
local direction = (opts.forward == nil or opts.forward) and ']' or '['
local vimtex_mapping = direction .. opts.vimtex_key

-- Vimtex does not use lua to map to their functions, but special <Plug>
-- mappings, which we have to map to.
-- All mappings of the desired type are listed here:
-- https://github.com/lervag/vimtex/blob/83e331dcad5ce28012e656eea3906b5b897db2ba/doc/vimtex.txt#L3899

-- Manually store the count and prepend it to the mapping to preserve it
local count = vim.v.count
local count_str = count > 1 and tostring(count) or ''
local mapping = count_str .. '<Plug>(vimtex-' .. vimtex_mapping .. ')'
local plug_mapping = vim.api.nvim_replace_termcodes(mapping, true, false, true)
vim.api.nvim_feedkeys(plug_mapping, 'm', false)
end, options)
end
end

---@param key string the key to map this to
---@param vimtex_mapping string the original vimtex mapping
---@param desc string the description of the mapping
function M.vimtex_map(key, vimtex_mapping, desc)
-- Override only if it's a vimtex mapping or not set.
-- That's roughly the behavior of vimtex as well:
-- https://github.com/lervag/vimtex/blob/83e331dcad5ce28012e656eea3906b5b897db2ba/autoload/vimtex.vim#L415
local existing_lhs = vim.fn.maparg(vimtex_mapping, 'nx')
if (existing_lhs ~= '' and existing_lhs:sub(1, 13) ~= '<Plug>(vimtex') then
return
end

local vimtex_key = vimtex_mapping:sub(2, 2)
local forward = vimtex_mapping:sub(1, 1) == ']'

local nx = { 'n', 'x' }

vim.keymap.set(nx, key, M.jump({ forward = forward, vimtex_key = vimtex_key }),
{ desc = desc, buffer = true, noremap = true, silent = true })
end

function M.create_keymaps()
local options = require('demicolon').get_options().integrations.vimtex
local keymaps = options and options.keymaps

if not options or not options.enabled or not keymaps then
return
end

vim.api.nvim_create_autocmd('FileType', {
group = vim.api.nvim_create_augroup('demicolon_vimtex_keymap', {}),
pattern = 'tex',
callback = function()
if vim.fn.exists(':VimtexCompile') ~= 2 then
return
end

M.vimtex_map(keymaps.section_start.next, '][', 'Next section start')
M.vimtex_map(keymaps.section_start.prev, '[[', 'Previous section start')

M.vimtex_map(keymaps.section_end.next, ']]', 'Next section end')
M.vimtex_map(keymaps.section_end.prev, '[]', 'Previous section end')

M.vimtex_map(keymaps.frame_start.next, ']r', 'Next frame start')
M.vimtex_map(keymaps.frame_start.prev, '[r', 'Previous frame start')

M.vimtex_map(keymaps.frame_end.next, ']R', 'Next frame end')
M.vimtex_map(keymaps.frame_end.prev, '[R', 'Previous frame end')

M.vimtex_map(keymaps.math_start.next, ']n', 'Next math start')
M.vimtex_map(keymaps.math_start.prev, '[n', 'Previous math start')

M.vimtex_map(keymaps.math_end.next, ']N', 'Next math end')
M.vimtex_map(keymaps.math_end.prev, '[N', 'Previous math end')

M.vimtex_map(keymaps.comment_start.next, ']/', 'Next comment start')
M.vimtex_map(keymaps.comment_start.prev, '[/', 'Previous comment start')

M.vimtex_map(keymaps.comment_end.next, ']*', 'Next comment end')
M.vimtex_map(keymaps.comment_end.prev, '[*', 'Previous comment end')

M.vimtex_map(keymaps.environment_start.next, ']m', 'Next environment start')
M.vimtex_map(keymaps.environment_start.prev, '[m', 'Previous environment start')

M.vimtex_map(keymaps.environment_end.next, ']M', 'Next environment end')
M.vimtex_map(keymaps.environment_end.prev, '[M', 'Previous environment end')
end,
})
end

return M