GithubHelp home page GithubHelp logo

ggandor / leap.nvim Goto Github PK

View Code? Open in Web Editor NEW
4.3K 14.0 45.0 1.4 MB

Neovim's answer to the mouse 🦘

License: MIT License

Fennel 96.43% Lua 2.05% Python 1.53%
neovim neovim-plugin nvim nvim-plugin fennel vim motion

leap.nvim's Introduction

leap.nvim

Leap is a general-purpose motion plugin for Neovim, building and improving primarily on vim-sneak, with the ultimate goal of establishing a new standard interface for moving around in the visible area in Vim-like modal editors. It allows you to reach any target in a very fast, uniform way, and minimizes the required focus level while executing a jump.

showcase

How to use it (TL;DR)

Leap's default motions allow you to jump to any position in the visible editor area by entering a 2-character search pattern, and then potentially a label character to pick your target from multiple matches, similar to Sneak. The main novel idea in Leap is that you get a preview of the target labels - you can see which key you will need to press before you actually need to do that.

  • Initiate the search in the forward (s) or backward (S) direction, or in the other windows (gs). (Note: you can use a single key for the current window or even the whole tab page, if you are okay with the trade-offs.)
  • Start typing a 2-character pattern ({char1}{char2}).
  • After typing the first character, you see "labels" appearing next to some of the {char1}{?} pairs. You cannot use the labels yet - they only get active after finishing the pattern.
  • Enter {char2}. If the pair was not labeled, then voilà, you're already there. You can safely ignore the remaining labels, and continue editing - those are guaranteed non-conflicting letters, disappearing on the next keypress.
  • Else: type the label character, that is now active. If there are more matches than available labels, you can switch between groups, using <space> and <backspace>.

Character pairs give you full coverage of the screen:

  • s{char}<space> jumps to the last character on a line.
  • s<space><space> jumps to actual end-of-line characters, including empty lines.

At any stage, <enter> consistently jumps to the next available target (<backspace> steps back):

  • s<enter>... repeats the previous search.
  • s{char}<enter>... can be used as a multiline substitute for fFtT motions.

Why is this method cool?

It is ridiculously fast: not counting the trigger key, leaping to literally anywhere on the screen rarely takes more than 3 keystrokes in total, that can be typed in one go. Often 2 is enough.

At the same time, it reduces mental effort to almost zero:

  • You don't have to weigh alternatives: a single universal motion type can be used in all non-trivial situations.

  • You don't have to compose in your head: one command achieves one logical movement.

  • You don't have to be aware of the context: the eyes can keep focusing on the target the whole time.

  • You don't have to make decisions on the fly: the sequence you should enter is determined from the very beginning.

  • You don't have to pause in the middle: if typing at a moderate speed, at each step you already know what the immediate next keypress should be, and your mind can process the rest in the background.

Getting started

Status

The plugin is not 100% stable yet, but don't let that stop you - the usage basics are extremely unlikely to change. To follow breaking changes, subscribe to the corresponding issue.

Requirements

  • Neovim >= 0.7.0 stable, or latest nightly

Dependencies

Installation

Use your preferred method or plugin manager. No extra steps needed besides defining keybindings - to use the default ones, put the following into your config (overrides s, S and gs in all modes):

require('leap').create_default_mappings() (init.lua)

lua require('leap').create_default_mappings() (init.vim)

Alternative key mappings

Calling require('leap').create_default_mappings() is equivalent to:

vim.keymap.set({'n', 'x', 'o'}, 's',  '<Plug>(leap-forward)')
vim.keymap.set({'n', 'x', 'o'}, 'S',  '<Plug>(leap-backward)')
vim.keymap.set({'n', 'x', 'o'}, 'gs', '<Plug>(leap-from-window)')

A suggested alternative arrangement (bidirectional s for Normal mode):

vim.keymap.set('n',        's', '<Plug>(leap)')
vim.keymap.set('n',        'S', '<Plug>(leap-from-window)')
vim.keymap.set({'x', 'o'}, 's', '<Plug>(leap-forward)')
vim.keymap.set({'x', 'o'}, 'S', '<Plug>(leap-backward)')

Mapping to <Plug>(leap) is not recommended for Visual mode, as autojumping in a random direction might be too disorienting with the selection highlight on, and neither for Operator-pending mode, as dot-repeat cannot be used if the search is non-directional.

Note that compared to using separate keys for the two directions, you will get twice as many targets and thus half as many autojumps on average, but not needing to press the Shift key for backward motions might compensate for that. Another caveat is that you cannot traverse through the matches (:h leap-traversal), although invoking repeat right away (:h leap-repeat) can substitute for that.

<Plug>(leap) sorts matches by euclidean distance from the cursor, with the exception that the current line, and on the current line, forward direction is prioritized. That is, you can always be sure that the targets right in front of you will be the first ones.

See :h leap-custom-mappings for more.

Suggested additional tweaks
-- Define equivalence classes for brackets and quotes, in addition to
-- the default whitespace group.
require('leap').opts.equivalence_classes = { ' \t\r\n', '([{', ')]}', '\'"`' }

-- Use the traversal keys to repeat the previous motion without explicitly
-- invoking Leap.
require('leap.user').set_repeat_keys('<enter>', '<backspace>')
Workaround for the duplicate cursor bug when autojumping

For Neovim versions < 0.10 (neovim/neovim#20793):

-- Hide the (real) cursor when leaping, and restore it afterwards.
vim.api.nvim_create_autocmd('User', { pattern = 'LeapEnter',
    callback = function()
      vim.cmd.hi('Cursor', 'blend=100')
      vim.opt.guicursor:append { 'a:Cursor/lCursor' }
    end,
  }
)
vim.api.nvim_create_autocmd('User', { pattern = 'LeapLeave',
    callback = function()
      vim.cmd.hi('Cursor', 'blend=0')
      vim.opt.guicursor:remove { 'a:Cursor/lCursor' }
    end,
  }
)

Caveat: If you experience any problems after using the above snippet, check #70 and #143 to tweak it.

Lazy loading

...is all the rage now, but doing it via your plugin manager is unnecessary, as Leap lazy loads itself. Using the keys feature of lazy.nvim might even cause problems.

Extras

Experimental features, APIs might be subject to change.

Incremental treesitter node selection
vim.keymap.set({'n', 'x', 'o'}, 'ga',  function ()
  require('leap.treesitter').select()
end)

-- Linewise.
vim.keymap.set({'n', 'x', 'o'}, 'gA',
  'V<cmd>lua require("leap.treesitter").select()<cr>'
)

Besides choosing a label (ga{label}), in Normal/Visual mode you can also use the traversal keys for incremental selection (; and , are automatically added to the default keys). The labels are forced to be safe, so you can operate on the current selection right away (ga;;y).

Tips

  • The traversal can "wrap around" backwards, so you can select the root node right away (ga,), instead of going forward (ga;;;...).

  • Linewise mode skips the current line, and redundant nodes are also filtered out (only the outermost are kept among the ones that span the same line ranges).

  • You can set the trigger key (or the suffix of it) and its inverted case to increase/decrease the selection (see clever-f.vim):

    -- "clever-a"
    vim.keymap.set({'n', 'x', 'o'}, 'ga',  function ()
      local sk = vim.deepcopy(require('leap').opts.special_keys)
      -- The items in `special_keys` can be both strings or tables - the
      -- shortest workaround might be the below one:
      sk.next_target = vim.fn.flatten(vim.list_extend({'a'}, {sk.next_target}))
      sk.prev_target = vim.fn.flatten(vim.list_extend({'A'}, {sk.prev_target}))
    
      require('leap.treesitter').select { opts = { special_keys = sk } }
    end)
Remote operations ("spooky actions at a distance")

Inspired by leap-spooky.nvim, and flash.nvim's similar feature.

This function allows you to perform an action in a remote location: it forgets the current mode or pending operator, lets you leap with the cursor (to anywhere on the tab page), then continues where it left off. Once an operation or insertion is finished, it moves the cursor back to the original position, as if you had operated from the distance.

-- If using the default mappings (`gs` for multi-window mode), you can
-- map e.g. `gS` here.
vim.keymap.set({'n', 'o'}, 'gs', function ()
  require('leap.remote').action()
end)

Example: gs{leap}yap yanks the paragraph at the position specified by {leap}. Getting used to the Normal-mode command is recommended over Operator-pending mode (ygs{leap}ap), since the former requires the same number of keystrokes, but it is much more flexible, as it allows you to move around freely, or to visually select a region before operating on it (that is, mistakes can be corrected, and more complex selections are possible). It might be more intuitive too, since the jump does not tear the operator and the selection command apart.

Tips

  • Swapping regions becomes moderately simple, without needing a custom plugin: d{region1}gs{leap}v{region2}pP. Example (swapping two words): diwgs{leap}viwpP.

  • As the remote mode is active until returning to Normal mode again (by any means), <ctrl-o> becomes your friend in Insert mode, or when doing change operations.

Icing on the cake, no. 1 - automatic paste after yanking

With this, you can clone text objects or regions in the blink of an eye, even from another window.

vim.api.nvim_create_augroup('LeapRemote', {})
vim.api.nvim_create_autocmd('User', {
  pattern = 'RemoteOperationDone',
  group = 'LeapRemote',
  callback = function (event)
    -- Do not paste if some special register was in use.
    if vim.v.operator == 'y' and event.data.register == '"' then
      vim.cmd('normal! p')
    end
  end,
})

Icing on the cake, no. 2 - giving input ahead of time

The input parameter lets you feed keystrokes ahead of time, e.g. to automatically trigger visual selection (v) (so you can gs{leap}apy), or create a forced linewise version of the command (V):

vim.keymap.set({'n', 'o'}, 'gS', function ()
  require('leap.remote').action { input = 'V' }
end)

This also lets you create remote text objects, for an even more intuitive workflow (yarp{leap}):

local default_text_objects = {
  'iw', 'iW', 'is', 'ip', 'i[', 'i]', 'i(', 'i)', 'ib',
  'i>', 'i<', 'it', 'i{', 'i}', 'iB', 'i"', 'i\'', 'i`',
  'aw', 'aW', 'as', 'ap', 'a[', 'a]', 'a(', 'a)', 'ab',
  'a>', 'a<', 'at', 'a{', 'a}', 'aB', 'a"', 'a\'', 'a`',
}
-- Create remote versions of all native text objects by inserting `r`
-- into the middle (`iw` becomes `irw`, etc.):
for _, tobj in ipairs(default_text_objects) do
  vim.keymap.set({'x', 'o'}, tobj:sub(1,1)..'r'..tobj:sub(2), function ()
    require('leap.remote').action { input = tobj }
  end)
end

Next steps

Help files are not exactly page-turners, but I suggest at least skimming :help leap, even if you don't have a specific question yet (if nothing else: :h leap-usage, :h leap-config, :h leap-events). While Leap has deeply thought-through, opinionated defaults, its small(ish) but comprehensive API makes it pretty flexible.

Design considerations in detail

The ideal

Premise: jumping from point A to B on the screen should not be some exciting puzzle, for which you should train yourself; it should be a non-issue. An ideal keyboard-driven interface would impose almost no more cognitive burden than using a mouse, without the constant context-switching required by the latter.

That is, you do not want to think about

  • the command: we need one fundamental targeting method that can bring you anywhere: a jetpack on the back, instead of airline routes (↔ EasyMotion and its derivatives)
  • the context: it should be enough to look at the target, and nothing else (↔ vanilla Vim motion combinations using relative line numbers and/or repeats)
  • the steps: the motion should be atomic (↔ Vim motion combos), and ideally you should be able to type the whole sequence in one go, on more or less autopilot (↔ any kind of "just-in-time" labeling method; note that the "search command on steroids" approach by Pounce and Flash, where the labels appear at an unknown time by design, makes this last goal impossible)

All the while using as few keystrokes as possible, and getting distracted by as little incidental visual noise as possible.

How do we measure up?

It is obviously impossible to achieve all of the above at the same time, without some trade-offs at least; but in our opinion Leap comes pretty close, occupying a sweet spot in the design space. (The worst remaining offender might be visual noise, but clever filtering in the preview phase can help - see :h leap.opts.preview_filter.)

The one-step shift between perception and action is the big idea that cuts the Gordian knot: a fixed pattern length combined with previewing labels can eliminate the surprise factor from the search-based method (which is the only viable approach - see "jetpack" above). Fortunately, a 2-character pattern - the shortest one with which we can play this trick - is also long enough to sufficiently narrow down the matches in the vast majority of cases.

Fixed pattern length also makes (safe) automatic jump to the first target possible. You cannot improve on jumping directly, just like how f and t works, not having to read a label at all, and not having to accept the match with <enter> either. However, we can do this in a smart way: if there are many targets (more than 15-20), we stay put, so we can use a bigger, "unsafe" label set - getting the best of both worlds. The non-determinism we're introducing is less of an issue here, since the outcome is known in advance.

In sum, compared to other methods based on labeling targets, Leap's approach is unique in that it

  • offers a smoother experience, by (somewhat) eliminating the pause before typing the label

  • feels natural to use for both distant and close targets

FAQ

Bugs

Workaround for the duplicate cursor bug when autojumping

For Neovim versions < 0.10 (neovim/neovim#20793):

-- Hide the (real) cursor when leaping, and restore it afterwards.
vim.api.nvim_create_autocmd('User', { pattern = 'LeapEnter',
    callback = function()
      vim.cmd.hi('Cursor', 'blend=100')
      vim.opt.guicursor:append { 'a:Cursor/lCursor' }
    end,
  }
)
vim.api.nvim_create_autocmd('User', { pattern = 'LeapLeave',
    callback = function()
      vim.cmd.hi('Cursor', 'blend=0')
      vim.opt.guicursor:remove { 'a:Cursor/lCursor' }
    end,
  }
)

Caveat: If you experience any problems after using the above snippet, check #70 and #143 to tweak it.

Defaults

Why remap `s`/`S`?

Common operations should use the fewest keystrokes and the most comfortable keys, so it makes sense to take those over by Leap, especially given that both native commands have synonyms:

Normal mode

  • s = cl (or xi)
  • S = cc

Visual mode

  • s = c
  • S = Vc, or c if already in linewise mode

If you are not convinced, just head to :h leap-custom-mappings.

Features

Search in all windows
vim.keymap.set('n', 's', function ()
  require('leap').leap {
    target_windows = require('leap.user').get_focusable_windows()
  }
end)

The additional trade-off here is that if you have multiple windows open on the tab page, then you will almost never get an autojump, except if all targets are in the same window (it would be too disorienting if the cursor could suddenly jump in/to a different window than your goal, right before selecting the target, not to mention that we don't want to change the state of a window we're not targeting).

Smart case sensitivity, wildcard characters (one-way aliases)

The preview phase, unfortunately, makes them impossible, by design: for a potential match, we might need to show two different labels (corresponding to two different futures) at the same time. (1, 2, 3)

Arbitrary remote actions instead of jumping

Basic template:

local function remote_action ()
  require('leap').leap {
    target_windows = require('leap.user').get_focusable_windows(),
    action = function (target)
      local winid = target.wininfo.winid
      local lnum, col = unpack(target.pos)  -- 1/1-based indexing!
      -- ... do something at the given position ...
    end,
  }
end

See Extending Leap for more.

Configuration

Disable auto-jumping to the first match
require('leap').opts.safe_labels = {}
Disable previewing labels
require('leap').opts.preview_filter = function () return false end
Greying out the search area
-- Or just set to grey directly, e.g. { fg = '#777777' },
-- if Comment is saturated.
vim.api.nvim_set_hl(0, 'LeapBackdrop', { link = 'Comment' })
Lightspeed-style highlighting
-- The below settings make Leap's highlighting closer to what you've been
-- used to in Lightspeed.

vim.api.nvim_set_hl(0, 'LeapBackdrop', { link = 'Comment' }) -- or some grey
vim.api.nvim_set_hl(0, 'LeapMatch', {
  -- For light themes, set to 'black' or similar.
  fg = 'white', bold = true, nocombine = true,
})
-- Deprecated option. Try it without this setting first, you might find
-- you don't even miss it.
require('leap').opts.highlight_unlabeled_phase_one_targets = true
Working with non-English text

If a language-mapping ('keymap') is active, Leap waits for keymapped sequences as needed and searches for the keymapped result as expected.

Also check out opts.equivalence_classes, that lets you group certain characters together as mutual aliases, e.g.:

{
  ' \t\r\n', 'aäàáâãā', 'dḍ', 'eëéèêē', 'gǧğ', 'hḥḫ',
  'iïīíìîı', '', '', 'sṣšß', 'tṭ', 'uúûüűū', 'zẓ'
}

Miscellaneous

Was the name inspired by Jef Raskin's Leap?

To paraphrase Steve Jobs about their logo and Turing's poison apple, I wish it were, but it is a coincidence. "Leap" is just another synonym for "jump", that happens to rhyme with Sneak. That said, in some respects you can indeed think of leap.nvim as a spiritual successor to Raskin's work, and thus the name as a little tribute to the great pioneer of interface design, even though embracing the modal paradigm is a fundamental difference in our approach.

Extending Leap

There are lots of ways you can extend the plugin and bend it to your will - see :h leap.leap() and :h leap-events. Besides tweaking the basic parameters of the function (search scope, jump offset, etc.), you can:

  • give it a custom action to perform, instead of jumping
  • feed it with custom targets, and only use it as labeler/selector
  • customize its behavior on a per-call basis via autocommands

Some practical examples:

Linewise motions
local function get_line_starts(winid, skip_range)
  local wininfo =  vim.fn.getwininfo(winid)[1]
  local cur_line = vim.fn.line('.')
  -- Skip lines close to the cursor.
  local skip_range = skip_range or 2

  -- Get targets.
  local targets = {}
  local lnum = wininfo.topline
  while lnum <= wininfo.botline do
    local fold_end = vim.fn.foldclosedend(lnum)
    -- Skip folded ranges.
    if fold_end ~= -1 then
      lnum = fold_end + 1
    else
      if (lnum < cur_line - skip_range) or (lnum > cur_line + skip_range) then
        table.insert(targets, { pos = { lnum, 1 } })
      end
      lnum = lnum + 1
    end
  end

  -- Sort them by vertical screen distance from cursor.
  local cur_screen_row = vim.fn.screenpos(winid, cur_line, 1)['row']
  local function screen_rows_from_cur(t)
    local t_screen_row = vim.fn.screenpos(winid, t.pos[1], t.pos[2])['row']
    return math.abs(cur_screen_row - t_screen_row)
  end
  table.sort(targets, function (t1, t2)
    return screen_rows_from_cur(t1) < screen_rows_from_cur(t2)
  end)

  if #targets >= 1 then
    return targets
  end
end

-- You can pass an argument to specify a range to be skipped
-- before/after the cursor (default is +/-2).
function leap_line_start(skip_range)
  local winid = vim.api.nvim_get_current_win()
  require('leap').leap {
    target_windows = { winid },
    targets = get_line_starts(winid, skip_range),
  }
end

-- For maximum comfort, force linewise selection in the mappings:
vim.keymap.set('x', '|', function ()
  -- Only force V if not already in it (otherwise it would exit Visual mode).
  if vim.fn.mode(1) ~= 'V' then vim.cmd('normal! V') end
  leap_line_start()
end)
vim.keymap.set('o', '|', "V<cmd>lua leap_line_start()<cr>")
Shortcuts to Telescope results
-- NOTE: If you try to use this before entering any input, an error is thrown.
-- (Help would be appreciated, if someone knows a fix.)
local function get_targets (buf)
  local pick = require('telescope.actions.state').get_current_picker(buf)
  local scroller = require('telescope.pickers.scroller')
  local wininfo = vim.fn.getwininfo(pick.results_win)[1]
  local top = math.max(
    scroller.top(pick.sorting_strategy, pick.max_results, pick.manager:num_results()),
    wininfo.topline - 1
  )
  local bottom = wininfo.botline - 2  -- skip the current row
  local targets = {}
  for lnum = bottom, top, -1 do  -- start labeling from the closest (bottom) row
    table.insert(targets, { wininfo = wininfo, pos = { lnum + 1, 1 }, pick = pick, })
  end
  return targets
end

local function pick_with_leap (buf)
  require('leap').leap {
    targets = function () return get_targets(buf) end,
    action = function (target)
      target.pick:set_selection(target.pos[1] - 1)
      require('telescope.actions').select_default(buf)
    end,
  }
end

require('telescope').setup {
  defaults = {
    mappings = {
      i = { ['<a-p>'] = pick_with_leap },
    }
  }
}
Enhanced f/t motions

See flit.nvim. Note that this is not a proper extension plugin, as it uses undocumented API too.

leap.nvim's People

Contributors

danilshvalov avatar escwxyz avatar ggandor avatar indianboy42 avatar kohane27 avatar krmbzds avatar kuntau avatar mivort avatar mohamed-abdelnour avatar nunosempere avatar osamuaoki avatar paulossouza avatar vivelev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

leap.nvim's Issues

packer error

    use {
      "ggandor/leap.nvim",
      config = function()
        require("leap").set_default_keymaps()
      end,
    }

screen-1649474699

[Feature Request]need a cross-all-window option

The existing cross-window motions can only search in other windows on the tab page, I want a cross-all-window motion which include current window. I'm used to getting anywhere with one key, I don't want to check whether it is forward or backward, nor whether it is the other window or just the current window.

vscode-neovim support

I tried to use leap.nvim in VSCode by using neovim extension). However, for some reason, the third character is only displayed as the same character of the word, not a different character to jump (the image below is for key sequence sco). I think this is only a display issue because when I type s or f after that sequence, the cursor jumps to the second and the third word which is the same behavior when I tried it with neovim.

image

OS: Arch Linux 5.17.4
VSCode: 1.66.2
neovim: 0.7.0

Take in 3+ characters instead of the default 2

Is there a way to configure this plugin to take in 3 or more characters after s? I often find that my files have many matches for two characters, and it's smoother for me to directly read the text at the place I want to go, as opposed to the arbitrary hint letters placed at each match by leap. The leap-added letters require me to (1) read the place I want to go, (2) type s{c1}{c2}, (3) read the place again to react to the leap-added character, (4) type the character. It seems smoother to instead (1) read the place I want to go, (2) type the characters there until my cursor gets there. The hint letters are still useful as an escape hatch in case the string i'm searching for is long and occurs many times in the file.

I realize I'm basically just describing search, but that involves extra keystrokes to exit the search, and doesn't have hint letters.

I'm still newish to this plugin, so maybe there is another option that reduces this friction (or i just need to get used to it).

Disable concealing when using leap.nvim

When I have used lightspeed.nvim, it was disabling concealing of current buffer. Meanwhile with leap.nvim concealing is still present. It would be much better to have an option to disable concealing while using leap.

Support smartcase

It would be nice if the plugin implemented smartcase: if there is an uppercase letter in the input, use case-sensitive search; otherwise, use case-insensitive search.

Copying the look from hop?

Hi, I've been using hop for a while, but I like a lot of the ideas here in leap so have been trying it out for a bit. The main thing I miss from hop though is how when it is hinting what to type, it removes all of the syntax colors from everything but the hinting letters so they're really easy to spot without needing to highlight the whole background of the letter.

That feels like it's something I could accomplish with some of the autocommands leap provides, but I wanted ask you if that made sense as the way to do it? If not can you please give a few pointers of how else to go about it?

Then also is this something at all that you'd put into the project itself, or is it best left as a do-it-yourself-if-you-want-it sort of feature?

How does this plugin relate to Lightspeed?

Hi Ggandor!

I have a question: Is this a replacement to lightspeed, or do you use them together?
I have gotten used to lightspeed now and couldn't live without it. I am still not a pro at handling all of the quirks of it but it is just amazing to use.

Add flag for auto-jump to first occurrence

I use a large scroll offset to keep my cursor vertically centered. As a side effect of this, and because leap moves the cursor to the first match automatically, my visual lock on my jump target gets broken on the second character I enter after s, which re-introduces that Hop/Easymotion mental pause that leap would otherwise solve for me.

Is there any way that a new config parameter could be added to turn that initial jump off?

Edge case: jumping to character at the end of a line (before a <cr>)

Edit: I just installe the plugin a couple of minutes ago and I was playing with the motions and options while reading the README. Right now I have just read the section Jumping to the last character on a line. Should I close this?

Using the default keymaps and given the following file:

image

Pressing sa works more or less as expected. The UI is bit misleading, thought, since it is showing the labels next to the a's (because <cr> denotes the end of a line I guess).

image

Then, pressing <cr> messes up all of the labels and it's imposible to any of the other results by using the labels displayed before pressing <cr>.

image

`<Enter>` and `<backspace>` won't work on `multiselect` when `special_keys` is set on `setup()`

You can revert the most recent pick with <backspace>, and accept the selection with <enter>.

As the title suggests, when special_keys is set on setup(), multiselect mode's both keybindings <Enter> and <backspace> won't work.

The same applies if <enter> and <backspace> are not used for special_keys.
I have also confirmed that these work when special_keys is not set.

Anyway, thank you for your great plugin!

Pressing s enters substitute instead of leap

Hey, relatively new to vim, Love the idea of leap and wanted to try it out. Unfortunately after adding it to my init.lua, saving, sourcing, and running packer install pressing s causes me to substitute instead of triggering leap. I checked that leap is successfully installed by running :help leap. Is there anything else I need to do?

nvim 0.7.2

I can see that this was supposed to work with 0.7+ but it also states that nightly should be used. I tried with 0.7.2 but it does highlight the works but dosn't jump .. you just get an error with

not found: w

if I press sw for finding a word starting with w .. is it supposed to work with 0.7 or only nightly ?

Bug: `LeapBackdrop` highlight is not fully cleared if `scrolloff` moves window

Hard to explain this in words, so I'll add some pictures. When I use leap and start typing, it adds the highlight from LeapBackdrop to the entire search area. However, I have vim.opt.scrolloff = 999 in my init.lua which causes the window the scroll down when I select the right entry.

Starting search: squ

Screen Shot 2022-06-26 at 7 18 53 PM

Pick entry: d

Screen Shot 2022-06-26 at 7 21 30 PM

Scroll back to top: gg

Screen Shot 2022-06-26 at 7 22 43 PM

Hope that makes the issue clear! Seemingly, there is an issue with highlight removal when it scrolls during the jump. I'm not quite sure how neovim handles highlights, but hop.nvim doesn't seem to have that problem when using a similar feature, so perhaps their mechanism may shed some light?

Thanks for the help!

Thank you <3

Being an OSS maintainer can be hard. As a recent adopter of your plugin, I just want to say thank you for the hard work. Your writeup is clear and concise. It's also obvious that you've put a lot of time and effort into thinking about and building this plugin. It's not money, but sending some <3 over the internet.

Unlabeled pattern not highlighting do not work.

Neovim version (nvim -v)

NVIM v0.7.2
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by [email protected]

Configured with Lunarvim

Operating system/version

macOS Montery 12.6

Terminal name/version

xterm-256color

Installation

Packer: { "ggandor/leap.nvim" }
Config: require("leap").set_default_keymaps(true)

Expected behavior

Open example file in Neovim and hit s and o in normal mode. Expect to see green colored text with underlines for matches.
image

Actual behavior

Open example file in Neovim and hit s and o in normal mode. Only see match groups.
Screen Shot 2022-10-03 at 8 06 07 AM

Also
Screen Shot 2022-10-03 at 8 26 28 AM

continue pattern with soh and get error.
Screen Shot 2022-10-03 at 8 19 35 AM

What I tried

  • Resetting to default colorscheme
  • Overriding LeapMatch highlights group to be an obvious style (no change)
  • Setting LeapBackdrop to dark grey (this sets it to grey successfully but does not fix the main issue)
  • Reinstalling Leap.nvim

I think this plugin is really, really cool and I'm so excited to try it! It feels like the last important thing I need to finally cut the cord from my IDE. Thanks so much for developing it, and I appreciate any help you can offer for this issue!

highlight groups get overriden when `:colorscheme` is used

Just noticed that setting the leap highlight groups (LeapMatch, …) in a colorscheme has no effect. Well it has when the file is :sourced, but not when set correctly with :colorscheme theme.

It happens because the ColorScheme-event, for which an autocmd is set up, gets triggered.

leap.nvim/fnl/leap.fnl

Lines 1000 to 1010 in b0921b7

; Colorscheme plugins might clear out our highlight definitions, without
; defining their own, so we re-init the highlight on every change.
(api.nvim_create_autocmd "ColorScheme"
{:callback init-highlight
:group "LeapDefault"})
(api.nvim_create_autocmd "User"
{:pattern "LeapEnter"
:callback #(do (save-editor-opts)
(set-temporary-editor-opts))
:group "LeapDefault"})

Repeat after search does not work

Maybe I misunderstand the documentation:

Pressing <enter> (opts.special_keys.repeat) after invoking any of Leap's motions searches with the previous input.

So I would expect I can do something like this:

  • sab to highlight all ab
  • x to jump to the ab labelled with x
  • <enter> to repeat the search for ab

But this does not work. Enter simply behaves as normal.

Here's my config:

require'leap'.set_default_keymaps()
require'leap'.setup {

  case_insensitive = true,
  safe_labels = {},
  labels = {
    "f", "j", "d", "k", "a", "l", "ö", "r", "t", "u", "e", "i", "v", "h", "c", ",", "n",
    "w", "o", "x", "y", ",", "q", "p", "t", "b", "z", ".", "-", "+", "ä"
  },

  special_keys = {
    repeat_search = '<enter>',
    next_match    = '<enter>',
    prev_match    = '<tab>',
    next_group    = '<space>',
    prev_group    = '<tab>',
    eol           = '<space>',
  },
}

leap-forward-x and selection=exclusive

Right now leap-forward-x lands 1 char short in visual mode when selection=exclusive. I'm assuming this isn't intended behavior as leap-forward does the right thing.

And thanks for this plugin :)

what's the correct way to remap z/Z to s/S

Hi there,

I've just switched from vim to nvim because of lightspeed & leap!

As I'm new to lua way of setting up things, any chance you could give sample config to set z/Z (oprator pending) to s/S (as I'm not using vim-surround)

Many thanks in advance!

Error attempt to index field 'wininfo' (a nil value)

Steps to reproduce

  1. Execute command :lua require('leap').leap({multiselect = true})
  2. Make a few matches.
  3. <CR>.

Expected behavior

I able to execute commands on cursors.

Actual behavior

E5108: Error executing lua ...nfig/nvim/pack/plugins/start/leap.nvim/lua/leap/main.lua:594: attempt to index field 'wininfo' (a nil value)
stack traceback:
	...nfig/nvim/pack/plugins/start/leap.nvim/lua/leap/main.lua:594: in function 'do_action'
 	...nfig/nvim/pack/plugins/start/leap.nvim/lua/leap/main.lua:984: in function 'leap'
 	[string ":lua"]:1: in main chunk

Problems with cursor highlight

👋🏼,

Before mentioning anything I just want to give you a huge thank you @ggandor. I've been using this plugin and lightspeed on a daily basis for a long time and they are essential to my tool kit.


I noted today that I sometimes lost track on here my cursor was when searching with leap.nvim. After some exploration I found that it was due to poor highlighting when leap.nvim automatically jumps to the closes match, and before inputting a label.

I think the following two screenshots explains it better than any words:

image
image

As you can see in ☝🏼 there's a huge difference in how visible the cursor is. I would like the cursor to have the same highlight.

Just ping me if I need to provide any more information 🙏🏼

Better highlight docs

After messing around for a while i still have no idea how to use the highlight groups for customizing leap nor do I know what group exactly highlights what. Maybe there can be some better clarification on how to customize the highlights

Dot-repeat: custom implementation, vendor repeat.vim?

Nvim version: NVIM v0.8.0-dev-1132-g37a71d1f2

init.lua

require("leap").set_default_keymaps()

Use nvim -u init.lua to load minimal config

Do a movement like dz12 or cz12 and try to dot-repeat it. Looks like the editor is locked into a sort of operator-pending mode to my eyes.

Having the same problem with flit.nvim and lightspeed.nvim. First noticed it using f/t motions with lightspeed.

Export <Plug> functions

The plugin provides Plug mappings. But it would be nice to export the functions too to be able to map them using vim.keymap.

To map leap.leap() function with vim.keymap I have to do something like this:

local function leap_forward()
  leap.leap({})
end

local function leap_forward_x()
  leap.leap({ offset = 1, inclusive_op = true })
end

vim.keymap.set({ 'n', 'x' }, 'f', leap_forward, { noremap = true })
vim.keymap.set({ 'n', 'x' }, 'F', leap_backward, { noremap = true })

or use <Plug> mappings. But it would be nice to have functions that used in <Plug> mappings exported directly:

local leap = require('leap')
vim.keymap.set({ 'n', 'x' }, leap.leap_forward, { noremap = true })
vim.keymap.set({ 'n', 'x' }, 'F', leap.leap_backward, { noremap = true })

And re-use this functions for Plug mappings.

Leap doesnt seem to be working properly

after I installed the plugin and tried to search for something, I typed sli expecting to be able to jump to symbols_outline, but all I get is this strange highlight.
image

Config for leap:

require("leap").setup({})
``

This plugin is awesome

Hi,

I just wanted to say that my motions across the screen have never felt as fast and natural as they have with this plugin. Thank you so much for building it, you have my eternal gratitude.

This issue can be closed.

Error when trying to set the default keymaps

With

require('leap').set_default_keymaps()

in my init.lua, I get the following error:

E5113: Error while calling lua chunk: ...share/nvim/site/pack/packer/start/leap.nvim/lua/leap.lua:1519: attempt to call field 'nvim_create_augroup' (a nil v
alue)
stack traceback:
        ...share/nvim/site/pack/packer/start/leap.nvim/lua/leap.lua:1519: in main chunk
        [C]: in function 'require'
        /Users/tbrown/.config/nvim/init.lua:238: in main chunk

since Leap doesn't finish initializing, I can't make anything work. I am running:

NVIM v0.7.0-dev+1129-g30c9c8815
Build type: Release
LuaJIT 2.1.0-beta3

Which is the latest HEAD from homebrew.

macOS 12.3 on a Mac Studio with M1Max.

I have found that this error occurs on non-nightly builds of vim 0.7. Is that my problem? What are my options? Which version to I need?

Typo in nvim API call

Lines 191, 194, and 281 call api.nvim_exec_autocmds, but the correct api call is api.nvim_exec_autocmd

Screenshot 2022-04-04 at 15 48 21

Leap is not triggered with s & S

After installing Leap it is not triggered if I press s or S.

Instead s removes a space and S the whole line.

It worked once (no clue why) but after I restarted my terminal I was stuck again with the behaviour I described above.

Lightspeed is working as expected.

Leap (probably) OK with vscode-neovim / vscode-neovim?

The subject repo is for a Visual Studio Code extension that embeds a NeoVim instance. Its Wiki says, "The problem with [easymotion] is that it replaces your text with markers and then restores [it]. ... [F]or VS Code it leads to broken text and many errors reported while you're jumping. [So] @asvetliakov created the special vim-easymotion fork. This version of the plugin doesn't touch your text ... ."

In this respect should Leap be OK with respect to VS Code?

(I haven't tried Leap yet. The README is daunting -- so many details -- but I think it's likely to be made clear by use. I'd like it to work with VSCode, though.) Thanks for tolerating a newbie question!

Some magic causes vim to be blocked

Hi, great plugin, thank you.

Before 054456a plugin worked well. Now when certain conditions are met (about them below) and you call :lua require('leap').leap { inclusive_op = true } it blocks vim. This is very similar to recursion because vim starts to consume too much CPU.

Specs:

  • Nixos Unstable
  • alacritty 0.10.1
  • sway 1.7
  • neovim v0.8.0-dev. I can't tell you which commit it is, sorry. If it's important, I can build neovim with any commit.

To reproduce:

  1. put this line in file дальнейшего сохранения, так же как и с гитом - к ним можно откатываться
  2. nvim --clean path/to/file
  3. shrink the terminal window to 82 or less characters
  4. :set nowrap
  5. :set rtp+=path/to/plugin
  6. lua require('leap').leap { inclusive_op = true }
  7. press -
github.mp4

error:

E5108: Error executing lua Keyboard interrupt
stack traceback:
        [C]: in function 'searchpos'
        ...im/site/pack/packer/start/leap.nvim//lua/leap/search.lua:165: in function '(for generator)'
        ...im/site/pack/packer/start/leap.nvim//lua/leap/search.lua:229: in function '_197_'
        ...nvim/site/pack/packer/start/leap.nvim//lua/leap/main.lua:1125: in function 'leap'
        [string ":lua"]:1: in main chunk

It is pure magic to me, but maybe you can fix it.

Leap doesn't register commands nor keymaps

Hi,
Really liked this plugin so I tried installing it with use("ggandor/leap.nvim"), then did a require("leap").set_default_keymaps().
But then I restarted neovim and tried typing s, which did the default action and not leap.

Then I tried executing :<Plug>(leap-forward), and got an error.

Could this be fixed?

Edge cases (for sake of completeness)

Hi! I am looking into leap.nvim for inspiration for internals and interface for one of my own plugins! (if I complete it, attribution will be given where it's due :])

I am asking how do you resolve these two cases, and prevent odd/unexpected behavior at the least:

Case 1

abab
| <- cursor

Type S and then ab and try to access first ab. It doesn't seem like it has a label, despite the README saying:

If a directly reachable match covers a label, the match will get a highlight (like in traversal mode), and the label will only be displayed after the second input, that resolves the ambiguity. If a label gets positioned over another label (this might occur before EOL or the window edge, when the labels need to be shifted left), an "empty" label will be displayed until entering the second input.

Maybe this above one is me not configuring correctly.

Case 2

+-------(window)-------+
|   | <- cursor        |
|                  hihi|
|                      |
+----------------------+

Type 's' and then hi and try to access second hi. For me, the label seems to exist, but it is offscreen—I must zoom out in my terminal emulator (i.e., press Cmd-minus) to see it.

Unable to leap to certain single characters

Hey 👋,

I noted today that I'm no longer able to leap to certain single characters. This have previously worked by pressing next match.

This is the sequence I can replace it on:

  • Press s
  • Press i (the character you want to leap to)
  • Press Enter (or the key you have set to next match)

It then outputs not found: i instead of allowing me to move to the next match. Do note that I only see this issue for certain characters, leaping to e works perfectly for examples.

I'd love some assistance debugging this if it's only an issue on my side... Also, thank you for an fantastic plugin. ❤️

Dimming the text

I'm moving from lightspeed to leap and so far it feels great. The only thing I miss from lightspeed was the dimming of the text that was being searched. For me personally was very helpful to see only the matches from lightspeed with the colorscheme "disabled".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.