mawkler/demicolon.nvim

github github
keybinding
stars 79
issues 0
subscribers 2
forks 2
CREATED

2024-08-06

UPDATED

2 months ago


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 jumps (e.g. ]f/[f) with the ;/, keys.

This plugin also integrates with gitsigns.nvim.

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

Installation

With lazy.nvim:

{
  '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, :help T, :help f, and :help F respectively.

Diagnostic motions

By default, demicolon.nvim will create the diagnostic motion keymaps below. See 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 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.

Native Neovim motions

Motion Jumps to next/pevious... Help page with more information
]q/[q item in quickfix list :help ]q/:help [q
]l/[l item in location list :help ]l/:help [l
]<C-q>/[<C-q> file in quickfix list :help ]CTRL-Q/:help [CTRL-Q
]<C-l>/[<C-l> file in location list :help ]CTRL-L/:help [CTRL-L
]z/[z fold :help zj/:help zk
]s/[z spelling mistake :help ]s/:help [s

Configuration

Default options:

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/]<C-q>/[<C-q> and ]l/[l/]<C-l>/[<C-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. 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 for inspiration.

eyeliner.nvim integration

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.

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 and eyeliner.nvim:

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

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. Credit to them for making an awesome plugin!