Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mawkler/demicolon.nvim

Overloaded ; and , keys in Neovim
https://github.com/mawkler/demicolon.nvim

neovim neovim-plugin plugin treesitter

Last synced: about 4 hours ago
JSON representation

Overloaded ; and , keys in Neovim

Awesome Lists containing this project

README

        

# demicolon.nvim

In addition to repeating `t`/`T`/`f`/`F` motions, this plugin lets you repeat diagnostic jumps (e.g. `]d`/`[d`) and [nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects?tab=readme-ov-file#text-objects-move) jumps (e.g. `]f`/`[f`) with the `;`/`,` keys.

This plugin also integrates with [gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim).

https://github.com/user-attachments/assets/e847cf39-40bd-49cb-9989-34e921b3393a

## Installation

With [lazy.nvim](https://github.com/folke/lazy.nvim):

```lua
{
'mawkler/demicolon.nvim',
-- keys = { ';', ',', 't', 'f', 'T', 'F', ']', '[', ']d', '[d' }, -- Uncomment this to lazy load
dependencies = {
'nvim-treesitter/nvim-treesitter',
'nvim-treesitter/nvim-treesitter-textobjects',
},
opts = {}
}
```

## Usage

After pressing any of the keymaps below, demicolon.nvim lets you repeat them with `;` and `,`.

### Vertical motions (`t`/`T`/`f`/`F`)

See [`:help t`](https://neovim.io/doc/user/motion.html#t), [`:help T`](https://neovim.io/doc/user/motion.html#T), [`:help f`](https://neovim.io/doc/user/motion.html#f), and [`:help F`](https://neovim.io/doc/user/motion.html#F) respectively.

### Diagnostic motions

By default, demicolon.nvim will create the diagnostic motion keymaps below. See [Configuration](#Configuration) for info on how to disable default keymaps.

| Motion | Description |
| --------- | --------------------------------------- |
| `]d`/`[d` | Next/previous diagnostic (any severity) |
| `]e`/`[e` | Next/previous error |
| `]w`/`[w` | Next/previous warning |
| `]i`/`[i` | Next/previous information |
| `]h`/`[h` | Next/previous hint |

### Treesitter text-object motions

demicolon.nvim lets you repeat any [nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects?tab=readme-ov-file#text-objects-move) motion. For example: `]f` to "jump to next function", `]c` to "jump to next class", etc.

> [!NOTE]
> To use treesitter text-objects you need to [configure `textobjects.move` in nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects?tab=readme-ov-file#text-objects-move).

### Native Neovim motions

| Motion | Jumps to next/pevious... | Help page with more information |
| ----------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `]q`/`[q` | item in quickfix list | [`:help ]q`](https://neovim.io/doc/user/quickfix.html#%5Dq)/[`:help [q`](https://neovim.io/doc/user/quickfix.html#%5Bq) |
| `]l`/`[l` | item in location list | [`:help ]l`](https://neovim.io/doc/user/quickfix.html#%5Dl)/[`:help [l`](https://neovim.io/doc/user/quickfix.html#%5Bl) |
| `]`/`[` | file in quickfix list | [`:help ]CTRL-Q`](https://neovim.io/doc/user/quickfix.html#%5DCTRL-Q)/[`:help [CTRL-Q`](https://neovim.io/doc/user/quickfix.html#%5BCTRL-Q) |
| `]`/`[` | file in location list | [`:help ]CTRL-L`](https://neovim.io/doc/user/quickfix.html#%5DCTRL-L)/[`:help [CTRL-L`](https://neovim.io/doc/user/quickfix.html#%5BCTRL-L) |
| `]z`/`[z` | fold | [`:help zj`](https://neovim.io/doc/user/fold.html#zj)/[`:help zk`](https://neovim.io/doc/user/fold.html#zk) |
| `]s`/`[z` | spelling mistake | [`:help ]s`](https://neovim.io/doc/user/spell.html#%5Ds)/[`:help [s`](https://neovim.io/doc/user/spell.html#%5Bs) |

## Configuration

Default options:

```lua
opts = {
diagnostic = {
-- See `:help vim.diagnostic.Opts.Float`
float = {}
},
-- Create default keymaps
keymaps = {
-- Create t/T/f/F key mappings
horizontal_motions = true,
-- Create ]d/[d, etc. key mappings to jump to diganostics. See demicolon.keymaps.create_default_diagnostic_keymaps
diagnostic_motions = true,
-- Create ; and , key mappings
repeat_motions = true,
-- Create ]q/[q/]/[ and ]l/[l/]/[ quickfix and location list mappings
list_motions = true,
-- Create `]s`/`[s` key mappings for jumping to spelling mistakes
spell_motions = true,
-- Create `]z`/`[z` key mappings for jumping to folds
fold_motions = true,
},
integrations = {
-- Integration with https://github.com/lewis6991/gitsigns.nvim
gitsigns = {
enabled = true,
keymaps = {
next = ']c',
prev = '[c',
},
},
},
}
```

### Custom jumps

You can create your own custom repeatable jumps using `repeatably_do()` in [`demicolon.jump`](./lua/demicolon/jump.lua). `repeatably_do()` takes a funcion as its first argument and options to be passed to that function as its second argument. Make sure that the options include a boolean `forward` field to determine whether the action should be forward or backward. Take a look at how I've implemented the [gitsigns.nvim integration](./lua/demicolon/integrations/gitsigns.lua) for inspiration.

### eyeliner.nvim integration

[eyeliner.nvim](https://github.com/jinh0/eyeliner.nvim) can highlight unique letters in words when you press `t`/`T`/`f`/`F`. Below is my recommended configuration for using eyeliner.nvim together with demicolon.nvim.

**NOTE:** make sure to set `keymaps.horizontal_motions = false` in your demicolon setup if you want to use this config.

```lua
return {
'jinh0/eyeliner.nvim',
-- keys = { 't', 'f', 'T', 'F' }, -- Uncomment this to lazy load eyeliner.nvim
config = function()
require('eyeliner').setup({
highlight_on_key = true,
default_keymaps = false,
dim = true, -- Optional
})

local function eyeliner_jump(key)
local forward = vim.list_contains({ 't', 'f' }, key)
return function()
require('eyeliner').highlight({ forward = forward })
return require('demicolon.jump').horizontal_jump(key)()
end
end

local nxo = { 'n', 'x', 'o' }
local opts = { expr = true }

vim.keymap.set(nxo, 'f', eyeliner_jump('f'), opts)
vim.keymap.set(nxo, 'F', eyeliner_jump('F'), opts)
vim.keymap.set(nxo, 't', eyeliner_jump('t'), opts)
vim.keymap.set(nxo, 'T', eyeliner_jump('T'), opts)
end
}
```

### Full recommended config

Here is a full configuration, including [nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects) and [eyeliner.nvim](https://github.com/jinh0/eyeliner.nvim):

Click here to see the code

```lua
require('lazy').setup({
{
'nvim-treesitter/nvim-treesitter-textobjects',
dependencies = 'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
config = function()
require('nvim-treesitter.configs').setup({
ensure_installed = 'all',
textobjects = {
move = {
enable = true,
goto_next_start = {
["]f"] = "@function.outer",
["]a"] = "@argument.outer",
["]m"] = "@method.outer",
-- ...
},
goto_previous_start = {
["[f"] = "@function.outer",
["[a"] = "@argument.outer",
["[m"] = "@method.outer",
-- ...
},
},
},
})
end,
},
{
'jinh0/eyeliner.nvim',
keys = { 't', 'f', 'T', 'F' },
opts = {
highlight_on_key = true,
dim = true,
default_keymaps = false,
}
},
{
'mawkler/demicolon.nvim',
dependencies = {
'jinh0/eyeliner.nvim',
'nvim-treesitter/nvim-treesitter',
'nvim-treesitter/nvim-treesitter-textobjects'
},
keys = { ';', ',', 't', 'f', 'T', 'F', ']', '[', ']d', '[d' },
config = function()
require('demicolon').setup({
keymaps = {
horizontal_motions = false,
},
})

local function eyeliner_jump(key)
local forward = vim.list_contains({ 't', 'f' }, key)
return function()
require('eyeliner').highlight({ forward = forward })
return require('demicolon.jump').horizontal_jump(key)()
end
end

local nxo = { 'n', 'x', 'o' }
local opts = { expr = true }

vim.keymap.set(nxo, 'f', eyeliner_jump('f'), opts)
vim.keymap.set(nxo, 'F', eyeliner_jump('F'), opts)
vim.keymap.set(nxo, 't', eyeliner_jump('t'), opts)
vim.keymap.set(nxo, 'T', eyeliner_jump('T'), opts)
end
}
})
```

[Here's](https://github.com/nvim-treesitter/nvim-treesitter-textobjects?tab=readme-ov-file#built-in-textobjects) the full list of available treesitter textobjects.

### "I want `;`/`,` to remember the direction of the original jump"

Neovim's default behaviour is for `;`/`,` to remember the direction of the jump that they repeat. That means that if you for instance repeat a `T` motion (as opposed to `t`) with `;` it will move you to the _left_. A lot of people prefer if `;` always moves you to the right and `,` always to the left, which is how demicolon works by default.

If you prefer Neovim's default behaviour you can disable demicolon's default `;`/`,` keymaps and remap them manually like this:

Click here to see the code

```lua
require('demicolon').setup({
keymaps = {
repeat_motions = false,
},
})

local ts_repeatable_move = require('nvim-treesitter.textobjects.repeatable_move')
local nxo = { 'n', 'x', 'o' }

vim.keymap.set(nxo, ';', ts_repeatable_move.repeat_last_move)
vim.keymap.set(nxo, ',', ts_repeatable_move.repeat_last_move_opposite)
```

## Credit

The treesitter portion of this plugin is just a wrapper over [nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects). Credit to them for making an awesome plugin!