Code Monkey home page Code Monkey logo

lightspeed.nvim's Introduction

Announcement

Lightspeed has been deprecated in favor of Leap, a streamlined but in many respects enhanced version of its ancestor. Compared to Lightspeed, Leap:

  • gets rid of some gimmicks with a low benefit/cost ratio (like "shortcut" labels), but works the same way in the common case; all the really important features are there
  • has a smaller and simpler visual footprint; it feels like using Sneak
  • is much more flexible and extensible; it can be used as an engine for selecting arbitrary targets, and performing arbitrary actions on them

🌌 lightspeed.nvim

Lightspeed is a motion plugin for Neovim, with a relatively small interface and lots of innovative ideas, that allow for making on-screen movements with yet unprecedented ease and efficiency. The aim is to maximize speed while minimizing mental effort and breaks in the flow, providing a distractionless experience, that should feel as "native" as possible.

welcome

The gist in 30 secs

With Lightspeed you can jump to any positions in the visible window area by entering a 2-character search pattern, and then optionally a "label" character for choosing among multiple matches. The plugin aims to substitute all native commands for in-window navigation (/, ?, gg, f, etc.) with a uniform, minimal set of atomic (repeatable), multi-axis motions.

So far we have more or less described what vim-sneak does. The game-changing idea in Lightspeed is its "clairvoyant" ability: it maps possible futures, and shows you which keys you will need to press before you actually need to do that, so despite the use of target labels, you can keep typing in a continuous manner. You can almost always reach the destination by at most - and very often less than - four keystrokes in total, that can be typed in one go.

Video tutorial

If this sounds cool enough, read on, or watch the 6-minute introductory video by DevOnDuty - a very good entry point, showing the basic usage with straightforward, easy to understand explanations.

Sky chart

Quick links (FAQ)

🧬 Evolution and design

Composite motions do not compose

Everyone has been taught that the "Vim way" of reaching distant points in the window is using combinations of primitive motions: 8jfx;;. The pipelining instinct is so deeply ingrained in our Vim-infected mindsets, that many of us tend to forget that this approach has evolved merely as a consequence of the limitations of the interface, and is not some divinely decreed, superior way of doing things; while the "controls as language" paradigm is an ingenious aspect of Vim in general, "compound sentences" make no sense for doing arbitrary jumps between A and B, that should ideally be atomic.

Railways versus jetpacks

EasyMotion attempted to improve the situation by introducing many new "atoms" - direct routes to a lot of specific targets. That plugin and its derivatives (Hop, or Avy for Emacs) are a bit like convoluted railway networks, with pre-built stations: each time you have to think about which train to take, which exit point is the closest to your goal, etc. In short, they buy speed for cognitive load - a questionable bargain.

Sneak's approach, however, with its sole focus on using 2-character search patterns for targeting, and later combining that with the labeling method inspired by EasyMotion, felt close to perfect at its time. A user of Sneak embraces a philosophy that is just the opposite of above: you barely need to think about motions anymore - "sneaking" gets you everywhere you need to be, with maximal precision. It is like having a jetpack on you all the time.

Always a step ahead of you

Lightspeed takes the next logical step, and eliminates yet more cognitive overhead, unnecessary keystrokes or interruptions, by blurring the boundary between one- and two-character search. The idea is to process the input incrementally - analyzing the available information after each keystroke, to assist the user and offer shortcuts:

  • jump based on partial input: if the character is unique in the search direction, you will automatically jump after the first input (these characters are highlighted beforehand, so this is never too surprising)
  • shortcut-labels: for some matches, it is possible to use the target label right after the first input, as if doing 1-character search
  • ahead-of-time displayed target labels: in any case, you will see the label right after the first input, so once you need to type it, your brain will already have processed it

The last one is probably the biggest game-changer, beating the major problem of all other general-purpose motion plugins - the frustrating momentary pause between entering your search pattern and selecting the target. Once you try it, you will never look back.

To see these features in action, check the screen recordings in the in-depth introduction below.

Universal motions

To make the suite complete, Lightspeed implements enhanced f/t-like motions working over multiple lines, with same-key repeat available, and a so-called x-mode, providing exclusive/inclusive variations for 2-character search. Together the four bi-directional motions (s/x/f/t) make it possible to reach and operate on the whole window area with high efficiency in all situations when there is no obvious atomic alternative - like w, {, or % - available.

Other improvements and quality-of-life features

  • smart shifting between Sneak/EasyMotion mode - the plugin automatically jumps to the first match if the remaining matches can be covered by a limited set of "safe" target labels, but stays in place, and switches to an extended, more comfortable label set otherwise
  • linewise operations are possible via the same interface, by targeting (potentially off-screen) EOL characters
  • uniform repeat interface, and flawless dot-repeat support for operators (with repeat.vim installed)
  • bidirectional search (opt-in)
  • cross-window motions

High-level guiding principles

"Some people . . . like tons of features, but experienced users really care about cohesion, conceptual integrity, and reliability. I think of [the latter] as the @tpope school." (justinmk)

  • 80/20: focus on features that are applicable in all contexts - micro-improvements to the most frequent tasks accumulate more savings than vanity features that turn out to be rarely needed in practice

  • Design is making decisions: mitigate choice paralysis for the user, regarding both usage (the kinds of targeting methods provided) and configuration options

  • Sharpen the saw: the plugin should feel a natural extension to the core, with an interplay as seamless and intuitive as possible

📚 An in-depth introduction of the key features

Jump on partial input

If you enter a character that is the only match in the search direction, Lightspeed jumps to it directly, without waiting for a second input. These unique characters are highlighted beforehand; quick-scope is based on a similar idea, but the intent here is not a "choose me!"-kind of preliminary orientation (the assumption is that you know where you want to go), more like giving feedback for your brain while you type.

jumping to unique characters

To further mitigate accidents, a short timeout is set by default, until the second character in the pair (and only that) is "swallowed". In operator-pending mode, the operated area gets a temporary highlight until the next character is entered.

Ahead-of-time labeling

Target labels are shown ahead of time, right after typing the first input character. This means you can often type without any serious break in the flow, almost as if using 3-character search. It is a micro-optimisation, but can mean the world - Lightspeed simply feels different because of this.

incremental labeling

Shortcuts

Made possible by the above, Lightspeed has the concept of "shortcutable" positions, where the assigned label itself is enough to determine the target: those you can reach via typing the label character right after the first input, bypassing the second one. This case is surprisingly frequent in practice, and in case of harder-to-type sequences, when you're not rushing with 200+ CPM, can work really well.

You can see that "shortcuts" are highlighted differently (with a background color):

shortcuts

Note that this is just an alternative: you do not have to watch out for these, and nothing bad happens if you type the second input as normal, and then type the label to reach the target. But in my experience, you can often guess whether the targeted position will be shortcutable, e.g. if there is a character that seems to be consistently followed by the same other character in the window (simple examples: a comment leader, e.g. - in Lua, or an < if there are lots of <Plug> forms in a section of a Vim config file).

Grouping matches by distance

When there is a large number of matches, we cycle through groups instead of trying to label everything at once (just like Sneak does it). However, the immediate next group is always shown ahead of time too, with a different color, so your brain has a bit of time to process the label, even in case of a distant group. If the target is right in the second group, you don't even have to think in terms of "switching groups" - a blue label should rather be thought of as a <space>-prefixed, 2-character label. That means we have 2 * number-of-labels targets right away that are in the efficiently-reachable/low-cognitive-load range.

groups

Note that Lightspeed keeps the invariant that a label consists of exactly one character, that should always stay in the same position, once appeared. (No rolling/flashing sequence of labels, like in case of Hop/EasyMotion.)

🚀 Getting started

Requirements

  • Neovim >= 0.7.0

Dependencies

  • repeat.vim is required for the dot-repeat functionality to work as intended.

Installation

use 'ggandor/lightspeed.nvim'
Plug 'ggandor/lightspeed.nvim'

🏹 Usage

"Just relax and let your mind go blank" - Lightspeed thinks for you. It always presents information before it is actually needed.

2-character search (s/x)

Without further ado, let's cut to the chase, and learn by doing. (Permalink to the file, if you want to follow along.)

The search is invoked with s in the forward direction, and S in the backward direction. Let's press s:

quick example 1

You can see that the search area is greyed out, and you can also see some characters highlighted. Those are characters with only one occurrence, and you can jump to them by simply typing the given character.

Let's target some word containing me. After entering the letter m, the plugin processes all bigrams starting with it, and from here on, you have all the visual information you need to reach your specific target:

quick example 2

Now type e. If you aimed for the first match (in frame_minheight), you are good to go, just continue the work! (The labels for the subsequent matches of me remain visible until the next keypress, but they are carefully chosen "safe" letters, guaranteed to not interfere with your following editing command.) If you aimed for some other match, then type the label, for example u, and move on to that.

quick example 3

An alternative could have been using a shortcut - skipping the second pattern character (e in our case), and just typing the label, if it has an inverse highlight. This is only practical if the first pattern character is hard to type - it is not worth it to deliberately pause and wait for a potential shortcut, instead of going with the flow. Shortcuts can always be used as normal labels - skipping is optional.

To show the last important feature, let's zoom out a bit, and target the struct member on the line available = oldwin->w_frame->fr_height; near the bottom, using the pattern fr, by first pressing s, and then f:

quick example 4

The blue labels indicate the "secondary" group of matches, where we start to reuse the available labels for a given pair (s, f, n... again). You can reach those by prefixing the label with <space>, that switches to the subsequent match group. For example, to jump to the "blue" j target, you should now press r<space>j. In very rare cases, if the large number of matches cannot be covered even by two label groups, you might need to press <space> multiple times, until you see the target labeled, first with blue, and then, after one more <space>, red.

To summarize, here is the general flow again (in Normal and Visual mode, with the default settings):

s|S char1 (char2|shortcut)? (<space>|<tab>)* label?

That is,

  • invoke in the forward (s) or backward (S) direction
  • enter the first character of the search pattern (might short-circuit after this, if the character is unique in the search direction)
    • the "beacons" are lit at this point; all potential matches are labeled (char1 + ?)
  • finish the motion by selecting a shortcut, or enter the second character of the search pattern (might short-circuit after this, if there is only one match)
    • certain beacons are extinguished; only char1 + char2 matches remain
    • the cursor automatically jumps to the first match if there are enough "safe" labels; pressing any other key than a group-switch or a target label exits the plugin now
  • optionally cycle through the groups of matches that can be labeled at once
  • choose a labeled target to jump to (in the current group)

When matches are too close to each other

If a match is too close to the next one, the beacon should be "squeezed" into the original 2-column box of the match; that is, on top of an A B match, a B label pair will appear, where the first field shows the character masked by the label (it is shifted left by a column) - those are the brownish characters you can see on some of the screenshots. In the most extreme case, the B field can even be overlapped by the label of another match, but only until the second input has not been entered - after that, all overlapped matches are guaranteed to become uncovered.

Operator-pending mode

In Operator-pending mode, there are two different (pairs of) motions available, providing the necessary additional comfort and precision, since in that case we are targeting exact positions, and can only aim once, without the means of easy correction.

z/Z are the equivalents of Normal/Visual s/S, and they follow the semantics of / and ? in terms of cursor placement and inclusive/exclusive operational behaviour, including forced motion types (:h forced-motion):

ab···|                    |···ab
█████·  ←  Zab    zab  →  ████ab
██████  ← vZab    vzab →  █████b

The mnemonic for X-mode could be extend/exclude (corresponding to x/X). It provides missing variants for the two directions:

ab···|                    |···ab
ab███·  ←  Xab    xab  →  ██████
ab████  ← vXab    vxab →  █████b

As you can see from the figure, x goes to the end of the match, including it in the operation, while X stops just before - in an absolute sense, after - the end of the match (the equivalent of T for two-character search). In simpler terms: in X-mode, the relevant edge of the operated area gets an offset of +2.

The assignment of z and x seems a sensible default, considering that those keys are free in O-P mode, and the handy visual mnemonic that x is physically to the right of z on a QWERTY keyboard (think about "pulling" the cursor forward). We are also acknowledging that "surround" plugins in Operator-pending mode may benefit more from being able to use the s/S keypair than general-purpose motion plugins like Lightspeed.

Cross-window motions

gs and gS are like s/S, but they search in the successor/predecessor windows in the window tree of the current tab page. In practical terms: gs scans downwards/rightwards, while gS upwards/leftwards. In exceptional cases, the direction can be switched on the fly with tab after invocation.

Bidirectional search

By mapping to the special keys <Plug>Lightspeed_omni_s and <Plug>Lightspeed_omni_gs, you can search in the whole window or tab page, instead of just a given direction. In this case, the matches are sorted by their screen distance from the cursor, advancing in concentric circles. This is a very different mental model, but has its own merits too.

1-character search (f/t)

Lightspeed also overrides the native f/F/t/T motions with enhanced versions that work over multiple lines. In all other respects they behave the same way as the native ones.

Matching line breaks (linewise motions)

The newline character is represented by <enter> in search patterns. For example, f<enter> is equivalent to $, and will move the cursor to the end of the line. s<enter> will label all EOL positions, including off-screen ones (labeled as <{label} or {label}>), providing an easy way to move to blank lines. Likewise, a character before EOL can be targeted by s{char}<enter> (\n in the match is highlighted as ¬ by default).

Repeating motions

Repeating in Lightspeed works in a uniform way across all motions - all of the following methods (and even combinations of them) are valid options.

Note that for s/x motions the labels will remain available during the whole time, even after entering instant-repeat mode, if the "safe" label set is in use.

"Instant" repeat (after jumping)

  • In Normal and Visual mode, the motions can be repeated by pressing the corresponding trigger key - s, f, t - again. (They continue in the original direction, whether it was forward or backward.) S, F and T, on the other hand, always revert the previous repeat. Note that in the case of T (or X, if mapped), this results in a different, and presumably more useful behaviour than what you are used to in clever-f and Sneak: it does not repeat the search in the reverse direction, but puts the cursor back to its previous position - before the previous match -, allowing for an easy correction when you accidentally overshoot your target.

  • For f/t-search, there is a special, opt-in repeat mode: pressing the target character again can also repeat the motion (opts.repeat_ft_with_target_char).

"Cold" repeat

  • Pressing <backspace> after invoking any of Lightspeed's motions searches with the previous input (1- and 2-character searches are saved separately). Subsequent keystrokes of <backspace> move on to the next match (that is, it invokes "instant-repeat" mode), while <tab> reverts (just like S/F/T).

  • There are also dedicated <Plug> keys available for repeating the two search modes. ; and , are mapped to f/t repeat by default (following the native behaviour), but it might be a good idea to remap them to repeat s/x. If you would like to set them to repeat the last Lightspeed motion (whether it was s/x or f/t), see :h lightspeed-custom-mappings. Just like above, subsequent keystrokes move on to the next match, while the opposite key reverts the previous motion.

Dot-repeat

Dot-repeat aims to behave in the most intuitive way in different situations - on special cases, see :h lightspeed-dot-repeat.

See also

For more details, see the docs (:h lightspeed-usage, :h lightspeed-default-mappings), and the in-depth introduction.

🔧 Configuration

Lightspeed exposes a configuration table (opts), that can be set directly, or via a setup function that updates the current settings with the values given in its argument table.

-- NOTE: This is just illustration - there is no need to copy/paste the
-- defaults, or call `setup` at all, if you do not want to change anything.

require'lightspeed'.setup {
  ignore_case = false,
  exit_after_idle_msecs = { unlabeled = nil, labeled = nil },
  --- s/x ---
  jump_to_unique_chars = { safety_timeout = 400 },
  match_only_the_start_of_same_char_seqs = true,
  force_beacons_into_match_width = false,
  -- Display characters in a custom way in the highlighted matches.
  substitute_chars = { ['\r'] = '¬', },
  -- Leaving the appropriate list empty effectively disables "smart" mode,
  -- and forces auto-jump to be on or off.
  safe_labels = { . . . },
  labels = { . . . },
  -- These keys are captured directly by the plugin at runtime.
  special_keys = {
    next_match_group = '<space>',
    prev_match_group = '<tab>',
  },
  --- f/t ---
  limit_ft_matches = 4,
  repeat_ft_with_target_char = false,
}

For a detailed description of the available options, see the docs: :h lightspeed-config.

You can also set options individually from the command line:

lua require'lightspeed'.opts.jump_to_unique_chars = false

EasyMotion/Hop-style config

By default, Lightspeed is tuned for maximum speed, especially for close and midrange movements, but the cost of this is increased visual noise and a bit more hectic experience. For a "calmer" style of navigation, similar to using Hop or EasyMotion, add the following two lines to your config:

jump_to_unique_chars = false,
safe_labels = {}

These disable the two most obtrusive automagic features (jumping to unique characters, and to the first 2-character match), while you can still enjoy Lightspeed's unique advantage of making the labels visible right as you type.

You might also want to use bidirectional search instead of the default s/S - for that, see :h lightspeed-custom-mappings.

Keymaps

Lightspeed aims to be part of an "extended native" layer, similar to such canonized Vim plugins like surround.vim or targets.vim. Therefore it provides carefully thought-out defaults, mapping to the following keys: s, S (Normal and Visual mode), gs, gS (Normal mode), z, Z, x, X (Operator-pending mode), and - obviously, enhancing the built-in motions - f, F, t, T, ;, , (all modes). See :h lightspeed-default-mappings for details.

That said, Lightspeed will check for conflicts with any custom mappings created by you or other plugins, and will not overwrite them, unless explicitly told so. To set alternative keymaps, you can use the <Plug> keys listed in :h lightspeed-custom-mappings.

Overridden native keymaps (s/S/gs)

Basic motions, like Lightspeed jumps, should have the absolute least friction among all commands, since they are the most frequent.

  • s: for replacing one character, r is the adequate choice; for the rare case when one wants to continue inserting after that, using cl is more than fine
  • S: cc is comfortable enough, and it is consistent with yy and dd
  • gs: probably no one misses this shortcut for the :sleep command

Setting keys to repeat the last lightspeed motion (s/x/f/t)

That can be achieved easily with autocommands and expression mappings. See :h lightspeed-custom-mappings.

Using the repeat keys for instant repeat only

Likewise, see :h lightspeed-custom-mappings for an example snippet.

Disabling the default keymaps

See :h lightspeed-disable-default-mappings.

User events

Lightspeed triggers User events on entering/exiting, so that you can set up autocommands, e.g. to change the values of some editor options while the plugin is active. For details, check :h lightspeed-events.

Highlight groups

For customizing the highlight colors, see :h lightspeed-highlight. If you are a colorscheme author/maintainer, please also check out the appropriate guide.

In case you - as a user - are not happy with a certain colorscheme's integration, you could force reloading the default settings by calling lightspeed.init_highlight(true). The call can even be wrapped in an autocommand to automatically re-init on every colorscheme change:

autocmd ColorScheme * lua require'lightspeed'.init_highlight(true)

This can be tweaked further, you could e.g. check the actual colorscheme, and only execute for certain ones, etc.

Notes

  • While the plugin is active, the actual cursor is down on the command line, but its position in the window is kept highlighted, using the attributes of the built-in Cursor highlight group - should you experience any issues, you should check the state of that first. Alternatively, you can tweak the LightspeedCursor group, to highlight the cursor in a custom way.

  • If you are using VSCode with NeoVim extension, you need to set hi LightspeedCursor gui=reverse in your nvim config to support the fake cursor and make Lightspeed work.

  • The otherwise useful multiline scoping of f/F/t/T can be undesirable when recording macros or executing :normal. This is being worked on, but as an API change, it should be thought through carefully. In the meantime, here is a rather elegant workaround for macros by rktjmp (caveat: this causes a problem for same-key repeat):

    nmap <expr> f reg_recording() . reg_executing() == "" ? "<Plug>Lightspeed_f" : "f"
    nmap <expr> F reg_recording() . reg_executing() == "" ? "<Plug>Lightspeed_F" : "F"
    nmap <expr> t reg_recording() . reg_executing() == "" ? "<Plug>Lightspeed_t" : "t"
    nmap <expr> T reg_recording() . reg_executing() == "" ? "<Plug>Lightspeed_T" : "T"

    For :normal, you could use the bang-version :normal!, although that disables all custom mappings, so that is only a half-measure.

❔ Why is there no feature X or Y?

Smart case-sensitivity?

See #64. It is unfortunately impossible for this plugin, by design. (Because of ahead-of-time labeling, it would require showing two different labels - corresponding to two different futures - at the same time.)

Arbitrary-length search pattern?

That is practically labeling /? matches, right? It is overkill for our purposes, IMO. Again, we are optimizing for the common case. A 2-character pattern, with the secondary group of matches displayed ahead of time, should be enough for making an on-screen jump efficiently 99% of the time; in that remaining 1%, just live with having to press Space multiple times. (What the heck are you editing, on what size of display, by the way?)

Labeled matches for 1-character search?

That would be pretty pointless, for two reasons. First, the pause is inevitable then, since it is physically impossible to show labels ahead of time. And usually there are too many matches, so we should use multi-character labels. (The closer ones you could probably reach with sab directly, instead of fa + l.) Now, ask yourself the question: isn't it much better to type two on-screen characters and then a "little bit surprising" label almost in one go (sabl), than to type one on-screen character, and wait for (most probably) two surprising characters to appear (fa + lm)?

Second, labeling matches makes it impossible to directly jump to the first target when doing operations - we're making our lives harder in the most frequent case (e.g. couldn't do a simple dfa).

In general, if you need to start thinking about whether to use f or s, scanning the context, then the whole thing is screwed already. Minimal mental effort. That is the mantra of Lightspeed. You should think of f and t as shortcuts for very specific situations, when you can count the number of occurrences, and thus reach for them in a totally automatic way, and not as equals of the s/x motions.

I miss Sneak's "vertical scope" feature...

That might indeed be useful, but I considered it would needlessly complicate the plugin. Sometime in the future we might add that though.

If you work with tabular data frequently, you can make a mapping instead that pre-populates the normal search prompt with horizontal bounds based on the count, something like the following (:h /\%v):

" note: g? in the example overwrites the superfun native rot13 command
nnoremap <expr> g/ '/<C-u>\%>'.(col(".")-v:count1).'v\%<'.(col(".")+v:count1).'v'
nnoremap <expr> g? '?<C-u>\%>'.(col(".")-v:count1).'v\%<'.(col(".")+v:count1).'v'

🌜 Contributing

Every contribution is very welcome, be it a bug report, fix, or just a discussion-initiating question - please do not feel intimidated. If you have any problems with the documentation especially, do not hesitate to reach out.

Tip: besides the issue tracker, be sure to also check/use Discussions for announcements, simple Q&A, and open-ended brainstorming.

Regarding feature requests and enhancements, consider the guiding principles first. If you have a different vision, feel free to fork the plugin and improve upon it in ways you think are best - I am glad to help -, but I'd like to keep this version streamlined, and save it from feature creep. Of course, that doesn't mean that I am not open for discussions.

Lightspeed is written in Fennel, and compiled to Lua ahead of time. I am aware that using Fennel might limit the number of available contributors, but compile-time macros, pattern matching, and a bunch of other features are simply too much of a convenience. (Learning a Lisp can be an eye-opening experience anyway, even though Fennel is something of a half-blood.)

As for "building", the plugin is really just one .fnl file at the moment, that you can compile into the lua folder with the Fennel executable manually, or using the provided Makefile.

💡 Inspired by

As always, we are standing on the shoulders of giants:

  • Sneak: a big fan of this - absolute respect for justinmk, besides his work on Neovim, for making a motion plugin that I have considered to be close to perfect for a long time
  • clever-f
  • Hop: a promising take on EasyMotion in the Neovim-era
  • EasyMotion: the venerable one, of course

lightspeed.nvim's People

Contributors

aleksey-rowan avatar alxhnr avatar ejmastnak avatar ggandor avatar jackm245 avatar murarisabavath avatar p00f avatar rktjmp avatar soares avatar theblob42 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

lightspeed.nvim's Issues

Question regarding match highlighting

Hey 👋🏼,

I'm a bit confused if the highlighting is working correctly or not. In the 👇🏼 attached mp4 I want to move to the last instance of match.

I input sm and this gives me 5 hits, for parameters and match. I then input a and it narrows it down even further to only match. This is the state that is confusing to me as lightspeed is still highlighting a over the matches. I would assume it to only view valid input options and a is not valid in this case. The correct input would be d.

simplescreenrecorder-2021-06-28_09.36.34.mp4

Turn off scrolloff while lightspeeding?

After pressing the second character, lightspeed immediately jumps to the first match. Unfortunately, if one has 'scrolloff' activated, this may cause the screen to scroll, making it hard to keep focus on the remaining labels. Would it be possible to temporarily disable the setting?

Tui support for highlighting

The Highlighting groups don't provide any cterm definitions for the TUI.
I found in lua/lightspeed.lua around line 175 the definition of the highlight groups

Is a target of this project to support TUI too?, then I could open a PR with my changes to discuss a possible implementation.

Disable conceallevel while lightspeed is active

the problem is that when conceallevel > 0 you can't really see where you are trying to jump to. So it would make to disable conceal while jumping.

Is there anyway easy way to do this? or could this be feature of this plugin?

Limit f/F/t/T scope to current line for macros and `:normal`

This should definitely be an option at least, even for 2-character search maybe. I wonder what would be the best solution interface-wise. Should we add an opts flag, something like limit_scope_to_current_line_for_macros? Or a prefix key that switches off multiline search on demand? Any ideas/preferences?

Also, is there any hack out there that makes it possible to detect whether we are currently executing a normal command, and not actually typing it live (like we can check macro playback with reg_executing)?

Omni direction

Hello. The main reason why I use hop is Omni-direction. I can press just shortcut for :Hop2char and press two characters and move both top or bottom direction. I don't care about two different shortcuts.
May I do it with lightspeed?

Can't extend visual selection if (vim) windows show different buffers

If you have multiple (VIM) windows open which show different buffers lightspeed commands (s, S, f, t, F, T) will exit visual mode.

Example

  • open neovim
  • split the current buffer :sp
  • switch to visual mode and try e.g. f i, f, f etc.
    • The selection should be extended as one would expect
  • switch one of the windows to another buffer
  • switch to visual mode and try e.g. f i, f, f etc.
    • The cursor jumps but we're back in normal mode

lightspeed

Minimal NVIM configuration used in the example
local install_path = vim.fn.stdpath('data') .. '/site/pack/packer/start/packer.nvim'
local is_packer_installed = vim.fn.isdirectory(install_path) == 1
if not is_packer_installed then
    vim.api.nvim_command('!git clone https://github.com/wbthomason/packer.nvim ' .. install_path)
end

require('packer').startup(function(use)
    use 'wbthomason/packer.nvim'
    use 'ggandor/lightspeed.nvim'
end)

I have had a look in the code, but could not see what could cause this behavior 🙁

sneak#is_sneaking() equivalent

I have a mapping which looks like this:

nmap <expr> ; sneak#is_sneaking() ? '<Plug>Sneak_;' : ':'
vmap <expr> ; sneak#is_sneaking() ? '<Plug>Sneak_;' : ':'

Basically means map ; to : when not in "label mode" otherwise let the plugin handle the mapping.

Is it currently possible to have the same behavior?

Quick-Scope like functionality.

Over at LunarVim we are wanting to integrate lightspeed, some users also want to have quick-scope installed, but this is vim-script only.

Would this functionality be something you'd consider adding to lightspeed (or accepting a patch for)..

It would be great to have the one lightspeed plugin to rule them all.

Add Media to readme.md to Preview Functionality

Title. I think it would improve user adoption rate based. A picture is worth a thousand words so for some of the more meatier plugins it really helped me understand their purpose when I was new to vim.

Willing/able to do this myself but I adopted this plugin today and need to familiarize myself.

If the decision is that this is not the right move then I'm still interested if anyone has something they want to show off to someone new.

Visual mode, 'S' for backward selection not working

Just installed and using standard config:

require("lightspeed").setup({})

In visual mode, using 's' to select forwards works as expected. However, 'S' from visual mode does not. It stays in Visual mode but does not enter 'lightpeed'.

Restore greyed out area after jump to 1st match after a timeout

First: thanks for this awesome plugin! I really start to get the hang of it.

But after some weeks of use I found one thing that could maybe be tweaked. It's about this feature (from the README):

the cursor automatically jumps to the first match by default; pressing any other key than a group-switch or a target label exits the plugin now

After typing 2 chars I often find, that I'm already where I wanted to go. But now the area is still greyed out. All I wanted to do is go to that new location, e.g. to reason about some piece of code. But my brain feels disturbed a lot by the still greyed out area. I now feel a disruption because I have think about "How can I get rid of that greyed out area?". I could press a key without any binding (ESC is to far away. On my german keyboard ä may be a good candidate). But it still requires me to press yet another key - for no real action.

I wonder if we couldn't just get a (configurable) timeout: After the cursor jumped to the first match, we could wait x seconds and then exit the plugin and restore the greyed out area.

Does this make sense? Or is there an even better alternative that I'm missing?

Interested in porting to Emacs?

Hi, first of all I want to say this is an amazing plugin! I consider this to be an essential plugin in my workflow now. Not really an issue but since you have implemented this in Lisp, I was wondering if you are interested in implementing this plugin in Emacs? Evil-snipe and Avy are substitutes for vim-sneak and EasyMotion, but I don't think there is anything like lightspeed in Emacs.

Allow custom characters as substitutes for highlighting whitespace characters (tab/space)

It might be nice to be able to set stand-ins for characters in labels.

See in this attached screenshot, where I have run vs0 from the start of the line (taken with Nord colorscheme so probably reasonably representative of many users):

image

Technically the first match is 0<space>, which makes sense, but it can be easy to overlook, I naturally want to type 00 because my brain reads it as a shortcut jump. I know this is wrong but still it happens.

If I could specify space_label = "˽", my brain might read it correctly.

image

I don't really know what the complexity would be, I think it's totally serviceable as is, but maybe a nice to have?

Allow for targeting the visible part of wrapped last lines

NVIM v0.6.0-dev+266-g5fd21b8d3
Lightspeed: 27625ea

If I have some very long lines which are wrapped, lightspeed doesn't seem to let me search everything visible. This may be a deliberate limitation for performance?

It's easier to see with a video. I have 3 lines. From line 1, I can only "query" (with s) into lines 1 and 2. From line 2, I can only query into line 2. I think it's to do with line 3 ending off screen?

lightspeed-2021-09-27_03.08.01.mp4

(if embed is broken https://streamable.com/rvs7as)

Content used, but any long, wrapped content will work.

Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia. Quae animi veritatis at ex unde! Natus doloribus ducimus mollitia provident facere Pariatur hic omnis adipisci eos consequuntur Ipsum accusamus minus totam eum aliquid. Nisi animi accusantium nam suscipit mollitia.
Consectetur autem cupiditate nemo harum pariatur! Vero unde adipisci velit aperiam in. Nostrum aspernatur eligendi hic praesentium optio Tenetur officiis aperiam non necessitatibus architecto Beatae sit ex dolor doloremque doloribus. Consectetur autem cupiditate nemo harum pariatur! Vero unde adipisci velit aperiam in. Nostrum aspernatur eligendi hic praesentium optio Tenetur officiis aperiam non necessitatibus architecto Beatae sit ex dolor doloremque doloribus. Consectetur autem cupiditate nemo harum pariatur! Vero unde adipisci velit aperiam in. Nostrum aspernatur eligendi hic praesentium optio Tenetur officiis aperiam non necessitatibus architecto Beatae sit ex dolor doloremque doloribus. Consectetur autem cupiditate nemo harum pariatur! Vero unde adipisci velit aperiam in. Nostrum aspernatur eligendi hic praesentium optio Tenetur officiis aperiam non necessitatibus architecto Beatae sit ex dolor doloremque doloribus.
Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque voluptate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a? Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque voluptate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a? Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque voluptate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a? Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque voluptate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a? Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque volupccctate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a? Adipisicing consequuntur obcaecati dolorem quae reprehenderit Perspiciatis reiciendis architecto molestiae odio corporis Architecto soluta optio quaerat expedita expedita. Neque voluptate tempora magni consectetur suscipit ea. Architecto nihil vitae delectus a?

nvim_echo is nil value

I'm getting:

E5108: Error executing lua /home/mc/.vim/plugged/lightspeed.nvim/lua/lightspeed.lua:35: attempt to call field 'nvim_echo' (a nil value) when I try hitting s after installing via Plug.

I've pulled the very latest Neovim nightly (Ubuntu) on two different machines:

> apt policy neovim
neovim:
  Installed: 0.5.0+ubuntu2+git202106150022-b28d458f8-d569569c9-e70091b15~ubuntu18.04.1
  Candidate: 0.5.0+ubuntu2+git202106150022-b28d458f8-d569569c9-e70091b15~ubuntu18.04.1
  Version table:
 *** 0.5.0+ubuntu2+git202106150022-b28d458f8-d569569c9-e70091b15~ubuntu18.04.1 500
        500 http://ppa.launchpad.net/neovim-ppa/unstable/ubuntu bionic/main amd64 Packages
        100 /var/lib/dpkg/status
     0.4.4-1~ubuntu18.04.1~ppa1 500
        500 http://ppa.launchpad.net/neovim-ppa/stable/ubuntu bionic/main amd64 Packages
     0.2.2-3 500
        500 http://au.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages

I've also tried a minimal vimrc file with only this Plug install and nothing else - same error.

This looks like a Neovim / Lua API problem - It was my understanding that Lua was now bundled with Neovim. Is there some way to check I have a compatible version of Lua compiled into Neovim correctly?

Space character in labels

And useless search character prefix, which confuses user.

Screenshot 1

Screenshot

Screenshot 2

Screenshot 2

Screenshot 3 - Hop

Hop short jumps

Screenshot 4

Screenshot 4

  1. In screenshot above, I searched for e, The green color parts don't need first e to jump to, so they are confusing.

  2. f & j in purple color need space key to be pressed first, which is not obvious, and space is neither in the labels config.

  3. And why do I have very few 1 character jumps in Screenshot 2? Hop & Easymotion make jump labels as short as possible. See Line 1-6 in Hop screenshot.

  4. In Screenshot 4, there is a capital H, which I didn't include in labels config, why is it there?

How to remove space form labels & e from those green color areas.

Config

require'lightspeed'.setup({
	jump_to_first_match = false,
	highlight_unique_chars = false,
	grey_out_search_area = true,
    -- Target keys
	labels = {'d', 'k', 'f', 'j', 's', 'l', 'a', ';', ',', 'c', 'm', 'e', 'i', 'w', 'o', 'g', 'h', 'v', 'n', 'r', 'u', 't', 'x', ',', 'q', 'p', '[', ']', 'z', '.', '/', 'y', 'b'}
})

Consider removing F/f/T/t search from lightspeed and splitting it into a different plugin

It is a good functionality that I've come to like. There were some design decisions I wasn't sure of but after thinking about it I've come to like it. But I think it would be more elegant/UNIX-philosophy compliant if this was in a separate plugin. I know that someone who doesn't want these features can just unmap them but I think it is more elegant and maintainable to only add what you want to your config, as opposed to adding and removing.

An alternative would be a boolean option of whether to include this functionality rather than making it the responsibility of the user to write unmaps.

Include Boolean Option for native , and ; character search

This suggestion naturally follows from the second part of #46

Include boolean option for including native ; and , functionality, rather than suggesting that the user copy one of the implementations that are included in the documentation; this will help avoid further clutter in users' config files, and also simplify the documentation.

Remapping s key?

I'd like to remap s to another key. I see in the docs you have:

`<Plug>Lightspeed_s`

But for those of us not using vim-plug how does one accomplish a remap?

Issue with f/t's same-key repeat

Thanks for creating this plugin. I'm liking it so far but I encountered one issue where the native f/t was nicer (or I'm doing it wrong 😉)

Imagine this situation where | is the cursor.

 |   hello abcade

When I would want to delete abca.

With native f I would type fad;.

With lightspeed I haven't found a way except fadfa. I would have expected fadf<CR> to work but this deleted until the end of line.

Changing highlighting colors

I was wondering how one can modify the highlight colors and particularly change the background color of the Shortcut labels. I read the documentation and I think that it is related to lightspeed-highlighting and the LightspeedShortcut part in the :help section but it is not clear how to use it. Thank you!

If the third character make the match unique, use that third character to jump and why the reminder character should stay on screen

If the third character make the match unique, use that third character to jump, this way we can reduce the "surprise" factor.

In this case if I search for pr there are only 2 possibilities: project and previousTctTask_
fullScreen
The ideal it would be I could jump to project with o and previousTctTask_ with e

The jump to the first match could be automatically, but you get the idea.

Also, it is necessary that r when I have already pressed pr?
Captura de pantalla de 2021-06-15 20-52-37

Cursor color changing when lightspeed is active

👋🏼,

I explicitly set my cursor color in alacritty which help me track it. The cursor color changes when lightspeed is activated though - which is very confusing to me and I have a really hard time seeing where my cursor is.

Not active:
2021-06-28_12-31

Active:
2021-06-28_12-31_1

This is highly noticable when using t or f repeatedly. The cursor color changes depending on the syntax highlighting and it's just confusing...

Is there an option to disable this or is it possible to add one? 🙏🏼

A way to map repeat `s` motions? (Hopefully with `;`/`,` as well)

Thanks for this plugin!

Just like there is a way to map ; and , (or custom keys) to repeat f/t motions, is there a way to map keys to repeat s (2-key) motions? If not, is there a way to repeat them at all? perhaps, by pressing s again?

It isn't currently documented and I tried to do it myself adhoc by adjusting the example for ft but from looking at the code, the ft and the s tables aren't exactly identical so I couldn't simply adjust the code in the documentation to apply to s.

I wonder if it's just a niche use-case? I'm using 2-key motions for everything; same line, next line or across half a screen. I find that the cost of pressing the second character is almost zero and the benefits it brings in disambiguation to reduce the number of matches is big. I also have jump-to-first-match enabled.

Thanks again!

What theme are you using?

I'm wondering what theme your using in the screenshots (and what theme(s) you're personally using currently if different)

Failing to set keybind overrides

Hey 👋🏼,

I'm trying to run the following config:

--  ╻  ╻┏━╸╻ ╻╺┳╸┏━┓┏━┓┏━╸┏━╸╺┳┓
--  ┃  ┃┃╺┓┣━┫ ┃ ┗━┓┣━┛┣╸ ┣╸  ┃┃
--  ┗━╸╹┗━┛╹ ╹ ╹ ┗━┛╹  ┗━╸┗━╸╺┻┛
return function()
    require('lightspeed').setup {
        jump_to_first_match = false,
        cycle_group_fwd_key = '<tab>',
        cycle_group_bwd_key = '<s-tab>'
    }
end

but the cycle keybind overrides are not working. Strangely the default Space is no longer working either. This must be a bug, no? 🐛 🔨

A minor thought is that I find it somewhat strange that a option alters the default keymap. What's the reasoning behind this?

Semicolon (;) and Comma (,) repeats not working when mapping with : instead of <CMD>

Hi, this is a relatively small issue but when changing the command triggers found in the help for ; and , repeating, it stops working in visual mode. (Changing the <cmd> to :). When trying to use ; or , in visual mode, it doesn't expand the selection after the first search with f or t. However, it works fine when I do what the help says. I'm not sure if this is a Neovim or Lightspeed issue so if it doesn't seem like a Lightspeed issue (or if it's a non-problem) feel free to just resolve this.

Thank you

Is it possible to customize the keys that are used for the hints?

Right now it seems to use s,f,n,/,t,g and the shifted forms of those, maybe more. I suppose because those keys are associated with searching before. but they are also all over the place, I was thinking that using home row keys (starting from index finger and going out) should be better. At least for me, thats why I wondered if there is a config option yet?

Error when using F when 'limit_ft_matches' is zero

When I set the line

lua require'lightspeed'.opts['limit_ft_matches'] = 0

And search with F or f (Fa or fa) for example and then try to reverse the search with F I get the warning not found: [character typed] and I have to press F twice for each reverse jump. The problem goes away when I set limit_ft_matches to a value of one or greater. The problem does not occur using T.

Option to have uniform highlighting?

One type of highlights changes the label's font color only (e.g. red), whereas another type changes the background and the font color (e.g. red bg, white font):

2021-06-29-02:00:27-screenshot

Is there any way to make it the former only? The latter is too low contrast so it's not easy on the eyes.

Also, why do the highlights differ anyway? Functionality, they seem to achieve the same purpose since they're of the same group of labels.

Turn into module and lazy-load

I appreciate that the current design is much easier to maintain, but it would be nice to have lightspeed do proper on-demand loading (similarly to how old vimscript plugins did the heavy lifting in autoload functions that are not sourced unconditionally at startup).

(Basically, you want to move as much as possible into modules that are required only on executing the mappings.)

Missing target

Running command Se and I noticed that I don't get a prompt for one of the e's above

16a43856ef7e10d8fd29265e9a976619

Resizing the window during an action moves the view to the actual cursor position

Hello!
I understand that this the cursor being moved to this "blank portion"(for lack of a better term) is intended behavior, but how can the resizing of the editor itself be handled?

Here's a video showing the said behavior using 2 character search and f:

lightspeedcursorresize.mp4

I have a few of ideas on how it could work, simplest of them being:

VimResize autocommand on start -> winrestview(winsaveview-saved-somewhere) -> disable autocommand on end

But then how best would it be best to implement the saving of window view?
Since there is no VimResizePre auto command, Would it be a CursorMoved autocommand that enables when lightspeed is active? Or would it be better to use a completely different approach?

Sorry for the questions, curious about what approach others would take :D

fFtT repeats do not work with vim-cutlass

When svermeulen/vim-cutlass is registered after lightspeed the repeating single character searches does not work. The initial single character search still works, but pressing, as an example, f again just starts a new f single character search.

I'm able to work around this issue by

nnoremap s <nop>
Plug 'svermeulen/vim-cutlass'
unmap s
Plug 'ggandor/lightspeed.nvim'

but thought you might want to be aware of the potential conflict. In particular cutlass does not, that I can tell, ever mess with any of fFtT.

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.