1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 07:20:04 +08:00

chore(hop.nvim): update bundle hop.nvim

close https://github.com/SpaceVim/SpaceVim/issues/4837

update to: 90db1b2c61
This commit is contained in:
wsdjeg 2023-04-16 21:22:49 +08:00
parent 1d4406b5ca
commit 4d8d77fb5e
12 changed files with 454 additions and 310 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2021, Dimitri Sabadie <dimitri.sabadie@gmail.com> Copyright (c) 2021-2022, Dimitri Sabadie <dimitri.sabadie@gmail.com>
All rights reserved. All rights reserved.

View File

@ -6,7 +6,17 @@
/_/ /_/
· Neovim motions on speed! · · Neovim motions on speed! ·
[![](https://img.shields.io/badge/matrix-join%20the%20speed!-blueviolet)](https://matrix.to/#/#hop.nvim:matrix.org) <p align="center">
<img src="https://img.shields.io/github/issues/phaazon/hop.nvim?color=cyan&style=for-the-badge"/>
<img src="https://img.shields.io/github/issues-pr/phaazon/hop.nvim?color=green&style=for-the-badge"/>
<img src="https://img.shields.io/github/contributors-anon/phaazon/hop.nvim?color=blue&style=for-the-badge"/>
<img src="https://img.shields.io/github/last-commit/phaazon/hop.nvim?style=for-the-badge"/>
<img src="https://img.shields.io/github/v/tag/phaazon/hop.nvim?color=pink&label=release&style=for-the-badge"/>
</p>
<p align="center">
<a href="#using-vim-plug">Install</a> · <a href="https://github.com/phaazon/hop.nvim/wiki">Wiki</a> · <a href="https://github.com/phaazon/hop.nvim/wiki/Screenshots">Screenshots</a> · <a href="https://matrix.to/#/#hop.nvim:matrix.org">Discuss</a>
</p>
**Hop** is an [EasyMotion]-like plugin allowing you to jump anywhere in a **Hop** is an [EasyMotion]-like plugin allowing you to jump anywhere in a
document with as few keystrokes as possible. It does so by annotating text in document with as few keystrokes as possible. It does so by annotating text in
@ -15,19 +25,14 @@ represents a key to type to jump to the annotated text. Most of the time,
those sequences lengths will be between 1 to 3 characters, making every jump those sequences lengths will be between 1 to 3 characters, making every jump
target in your document reachable in a few keystrokes. target in your document reachable in a few keystrokes.
<p align="center">
<img src="https://user-images.githubusercontent.com/506592/176885253-5f618593-77c5-4843-9101-a9de30f0a022.png"/>
</p>
<!-- vim-markdown-toc GFM --> <!-- vim-markdown-toc GFM -->
* [Motivation](#motivation) * [Motivation](#motivation)
* [Features](#features) * [Features](#features)
* [Word mode (`:HopWord`)](#word-mode-hopword)
* [Line mode (`:HopLine`)](#line-mode-hopline)
* [1-char mode (`:HopChar1`)](#1-char-mode-hopchar1)
* [2-char mode (`:HopChar2`)](#2-char-mode-hopchar2)
* [Pattern mode (`:HopPattern`)](#pattern-mode-hoppattern)
* [Visual extend](#visual-extend)
* [Jump on sole occurrence](#jump-on-sole-occurrence)
* [Use as operator motion](#use-as-operator-motion)
* [Inclusive / exclusive motion](#inclusive--exclusive-motion)
* [Getting started](#getting-started) * [Getting started](#getting-started)
* [Installation](#installation) * [Installation](#installation)
* [Important note about versioning](#important-note-about-versioning) * [Important note about versioning](#important-note-about-versioning)
@ -36,8 +41,6 @@ target in your document reachable in a few keystrokes.
* [Nightly users](#nightly-users) * [Nightly users](#nightly-users)
* [Usage](#usage) * [Usage](#usage)
* [Keybindings](#keybindings) * [Keybindings](#keybindings)
* [Configuration](#configuration)
* [Extension](#extension)
* [Chat](#chat) * [Chat](#chat)
<!-- vim-markdown-toc --> <!-- vim-markdown-toc -->
@ -66,83 +69,18 @@ Neovim.
# Features # Features
- [x] Go to any word in the current buffer. - Go to any word in the current buffer (`:HopWord`).
- [x] Go to any character in the current buffer. - Go to any character in the current buffer (`:HopChar1`).
- [x] Go to any bigrams in the current buffer. - Go to any bigrams in the current buffer (`:HopChar2`).
- [x] Use Hop cross windows with multi-windows support. - Make an arbitrary search akin to <kbd>/</kbd> and go to any occurrences (`:HopPattern`).
- [x] Make an arbitrary search akin to <kbd>/</kbd> and go to any occurrences. - Go to any line and any line start (`:HopLine`, `:HopLineStart`).
- [x] Go to any line. - Go to anywhere (`:HopAnywhere`).
- [x] Visual extend mode, which allows you to extend a visual selection by hopping elsewhere in the document. - Use Hop cross windows with multi-windows support (`:Hop*MW`).
- [x] Use it with commands like `d`, `c`, `y` to delete/change/yank up to your new cursor position. - Use it with commands like `v`, `d`, `c`, `y` to visually select/delete/change/yank up to your new cursor position.
- [x] Support a wide variety of user configuration options, among the possibility to alter the behavior of commands - Support a wide variety of user configuration options, among the possibility to alter the behavior of commands
to hint only before or after the cursor, for the current line, change the dictionary keys to use for the labels, to hint only before or after the cursor (`:Hop*BC`, `:Hop*AC`), for the current line (`:Hop*CurrentLine`),
jump on sole occurrence, etc. change the dictionary keys to use for the labels, jump on sole occurrence, etc.
- [x] Extensible: provide your own jump targets and create Hop extensions! - Extensible: provide your own jump targets and create Hop extensions!
## Word mode (`:HopWord`)
This mode highlights all the recognized words in the visible part of the buffer and allows you to jump to any.
![](https://phaazon.net/media/uploads/hop_word_mode.gif)
## Line mode (`:HopLine`)
This mode highlights the beginnings of each line in the visible part of the buffer for quick line hopping.
![](https://phaazon.net/media/uploads/hop_line_mode.gif)
## 1-char mode (`:HopChar1`)
This mode expects the user to type a single character. That character will then be highlighted in the visible part of
the buffer, allowing to jump to any of its occurrence. This mode is especially useful to jump to operators, punctuations
or any symbols not recognized as parts of words.
![](https://phaazon.net/media/uploads/hop_char1_mode.gif)
## 2-char mode (`:HopChar2`)
A variant of the 1-char mode, this mode exacts the user to type two characters, representing a _bigram_ (they follow
each other, in order). The bigram occurrences in the visible part of the buffer will then be highlighted for you to jump
to any.
![](https://phaazon.net/media/uploads/hop_char2_mode.gif)
Note that its possible to _fallback to 1-char mode_ if you hit a special key as second key. This key can be controlled
via the user configuration. `:h hop-config-char2_fallback_key`.
## Pattern mode (`:HopPattern`)
Akin to `/`, this mode prompts you for a pattern (regex) to search. Occurrences will be highlighted, allowing you to
jump to any.
![](https://phaazon.net/media/uploads/hop_pattern_mode.gif)
## Visual extend
If you call any Hop commands / Lua functions from one of the visual modes, the visual selection will be extended.
![](https://phaazon.net/media/uploads/hop_visual_extend.gif)
## Jump on sole occurrence
If only a single occurrence is visible in the buffer, Hop will automatically jump to it without requiring pressing any
extra key.
![](https://phaazon.net/media/uploads/hop_sole_occurrence.gif)
## Use as operator motion
You can use Hop with any command that expects a motion, such as `d`, `y`, `c`, and it does what you would expect:
Delete/yank/change the document up to the new cursor position.
## Inclusive / exclusive motion
By default, Hop will operate in exclusive mode, which is similar to what you get with `t`: deleting from the cursor
position up to the next `)` (without deleting the `)`), which is normally done with `dt)`. However, if you want to be
inclusive (i.e. delete the `)`, which is `df)` in vanilla), you can set the `inclusive_jump` option to `true`.
Some limitations currently exist, requiring `virtualedit` special settings. `:h hop-config-inclusive_jump` for more
information.
# Getting started # Getting started
@ -184,7 +122,7 @@ Plug 'phaazon/hop.nvim'
```lua ```lua
use { use {
'phaazon/hop.nvim', 'phaazon/hop.nvim',
branch = 'v1', -- optional but strongly recommended branch = 'v2', -- optional but strongly recommended
config = function() config = function()
-- you can configure Hop the way you like here; see :h hop-config -- you can configure Hop the way you like here; see :h hop-config
require'hop'.setup { keys = 'etovxqpdygfblzhckisuran' } require'hop'.setup { keys = 'etovxqpdygfblzhckisuran' }
@ -199,29 +137,7 @@ the last one**. If you are not, then you are exposed to compatibility issues / b
# Usage # Usage
A bunch of vim commands are available to get your fingers wrapped around **Hop** quickly: See the [wiki](https://github.com/phaazon/hop.nvim/wiki).
- `:HopWord`: hop around by highlighting words.
- `:HopPattern`: hop around by matching against a pattern (as with `/`).
- `:HopChar1`: type a single key and hop to any occurrence of that key in the document.
- `:HopChar2`: type a bigram (two keys) and hop to any occurrence of that bigram in the document.
- `:HopLine`: jump to any visible line in your buffer.
- `:HopLineStart`: jump to any visible first non-whitespace character of each line in your buffer.
Most of these commands have variant to jump before / after the cursor, and on the current line. For instance,
`:HopChar1CurrentLineAC` is a form of `f` (Vim native motion) using Hop.
If you would rather use the Lua API, you can test it via the command prompt:
```vim
:lua require'hop'.hint_words()
```
For a more complete user guide and help pages:
```vim
:help hop
```
# Keybindings # Keybindings
@ -231,53 +147,22 @@ If you want to create a key binding from within Lua:
```lua ```lua
-- place this in one of your configuration file(s) -- place this in one of your configuration file(s)
vim.api.nvim_set_keymap('n', 'f', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR, current_line_only = true })<cr>", {}) local hop = require('hop')
vim.api.nvim_set_keymap('n', 'F', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR, current_line_only = true })<cr>", {}) local directions = require('hop.hint').HintDirection
vim.api.nvim_set_keymap('o', 'f', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR, current_line_only = true, inclusive_jump = true })<cr>", {}) vim.keymap.set('', 'f', function()
vim.api.nvim_set_keymap('o', 'F', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR, current_line_only = true, inclusive_jump = true })<cr>", {}) hop.hint_char1({ direction = directions.AFTER_CURSOR, current_line_only = true })
vim.api.nvim_set_keymap('', 't', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR, current_line_only = true })<cr>", {}) end, {remap=true})
vim.api.nvim_set_keymap('', 'T', "<cmd>lua require'hop'.hint_char1({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR, current_line_only = true })<cr>", {}) vim.keymap.set('', 'F', function()
vim.api.nvim_set_keymap('n', '<leader>e', "<cmd> lua require'hop'.hint_words({ hint_position = require'hop.hint'.HintPosition.END })<cr>", {}) hop.hint_char1({ direction = directions.BEFORE_CURSOR, current_line_only = true })
vim.api.nvim_set_keymap('v', '<leader>e', "<cmd> lua require'hop'.hint_words({ hint_position = require'hop.hint'.HintPosition.END })<cr>", {}) end, {remap=true})
vim.api.nvim_set_keymap('o', '<leader>e', "<cmd> lua require'hop'.hint_words({ hint_position = require'hop.hint'.HintPosition.END, inclusive_jump = true })<cr>", {}) vim.keymap.set('', 't', function()
hop.hint_char1({ direction = directions.AFTER_CURSOR, current_line_only = true, hint_offset = -1 })
end, {remap=true})
vim.keymap.set('', 'T', function()
hop.hint_char1({ direction = directions.BEFORE_CURSOR, current_line_only = true, hint_offset = 1 })
end, {remap=true})
``` ```
# Configuration
You can configure Hop via several different mechanisms:
- _Global configuration_ uses the Lua `setup` API (`:h hop.setup`). This allows you to setup global options that will be
used by all Hop Lua functions as well as the vim commands (e.g. `:HopWord`). This is the easiest way to configure Hop
on a global scale. You can do this in your `init.lua` or any `.vim` file by using the `lua` vim command.
Example:
```vim
" init.vim
"
" Use better keys for the bépo keyboard layout and set
" a balanced distribution of terminal / sequence keys
lua require'hop'.setup { keys = 'etovxqpdygfblzhckisuran', jump_on_sole_occurrence = false }
```
- _Local configuration overrides_ are available only on the Lua API and are `{opts}` Lua tables passed to the various
Lua functions. Those options have precedence over global options, so they allow to locally override options. Useful if
you want to test a special option for a single Lua function, such as `require'hop'.hint_lines()`. You can test them
inside the command line, such as:
```
:lua require'hop'.hint_words({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR })
```
- In the case of none of the above are provided, options are automatically read from the _default_ options. See `:h
hop-config` for a list of default values.
# Extension
It is possible to extend Hop by creating *Hop extension plugins*. For more info:
```vim
:h hop-extension
```
> Disclaimer: you may have written a nice Hop extension plugin. You can open an issue to merge it upstream but remember
> that its unlikely to be merged as Hop should remain small and straight-to-the point.
# Chat # Chat
Join the discussion on the official [Matrix room](https://matrix.to/#/#hop.nvim:matrix.org)! Join the discussion on the official [Matrix room](https://matrix.to/#/#hop.nvim:matrix.org)!

View File

@ -1,4 +1,4 @@
*hop.txt* For Neovim version 0.5 Last change: 2021 Nov 02 *hop.txt* For Neovim version 0.5 Last change: 2022 Oct 09
__ __
/ /_ ____ ____ / /_ ____ ____
@ -7,6 +7,7 @@
/_/ /_/\____/ .___/ /_/ /_/\____/ .___/
/_/ /_/
· Neovim motions on speed! · · Neovim motions on speed! ·
v2.0.2
============================================================================== ==============================================================================
CONTENTS *hop-contents* CONTENTS *hop-contents*
@ -177,6 +178,14 @@ creates a variant of the command that will only run for the current line.
This is akin to calling the |hop.hint_lines_skip_whitespace| Lua function. This is akin to calling the |hop.hint_lines_skip_whitespace| Lua function.
`:HopVertical` *:HopVertical*
`:HopVerticalBC` *:HopVerticalBC*
`:HopVerticalAC` *:HopVerticalAC*
`:HopVerticalMW` *:HopVerticalMW*
Like `HopLine` but keeps the column position of the cursor.
This is akin to calling the |hop.hint_vertical| Lua function.
`:HopAnywhere` *:HopAnywhere* `:HopAnywhere` *:HopAnywhere*
`:HopAnywhereBC` *:HopAnywhereBC* `:HopAnywhereBC` *:HopAnywhereBC*
`:HopAnywhereAC` *:HopAnywhereAC* `:HopAnywhereAC` *:HopAnywhereAC*
@ -310,6 +319,16 @@ Most of the functions and values you need to know about are in `hop`.
Arguments:~ Arguments:~
{opts} Hop options. {opts} Hop options.
`hop.hint_vertical(`{opts}`)` *hop.hint_vertical*
Hint the cursor position of each lines currently visible in the buffer
view and allow to jump to them. If the line is shorter than the current
cursor column position, it will default to the end of the line.
This works with empty lines as well.
Arguments:~
{opts} Hop options.
`hop.hint_anywhere(`{opts}`)` *hop.hint_anywhere* `hop.hint_anywhere(`{opts}`)` *hop.hint_anywhere*
Annotate anywhere in the current window with key sequences. Annotate anywhere in the current window with key sequences.
@ -499,7 +518,7 @@ doing that for you.
of all the words in the window and will make the cursor jump to the one of all the words in the window and will make the cursor jump to the one
fully reduced. fully reduced.
`hop.jump_target.regex_by_line_start()` *hop.jump_target.regex_by_line_start* `hop.jump_target.by_line_start()` *hop.jump_target.by_line_start*
Highlight the beginning of each line in the current window. Highlight the beginning of each line in the current window.
`hop.jump_target.regex_by_line_start_skip_whitespace()` *hop.jump_target.regex_by_line_start_skip_whitespace* `hop.jump_target.regex_by_line_start_skip_whitespace()` *hop.jump_target.regex_by_line_start_skip_whitespace*
@ -728,6 +747,18 @@ below.
Defaults:~ Defaults:~
`hint_position = require'hop.hint'.HintPosition.BEGIN` `hint_position = require'hop.hint'.HintPosition.BEGIN`
`hint_offset` *hop-config-hint_offset*
Offset to apply to a jump location.
If it is non-zero, the jump target will be offset horizontally from the
selected jump position by `hint_offset` character(s).
This option can be used for emulating the motion commands |t| and |T| where
the cursor is positioned on/before the target position.
Defaults:~
`hint_offset = 0`
`current_line_only` *hop-config-current_line_only* `current_line_only` *hop-config-current_line_only*
Apply Hop commands only to the current line. Apply Hop commands only to the current line.
@ -738,22 +769,6 @@ below.
Defaults:~ Defaults:~
`current_line_only = false` `current_line_only = false`
`inclusive_jump` *hop-config-inclusive_jump*
Make all motions inclusive; i.e. jumping to a jump target will actually
jump one display cell right to the jump target. Set this option to `true`
if you would like to have the same behavior as with the |f| motion. Set it
to `false` if you would like to have the same behavior as with the |t|
motion.
There is one important limitation if you use `inclusive_jump = true`: if
the jump target you would like to jump to is the last character on a
line, it will not do what you expect; for instance, deleting or yanking
with `d` / `y` will not include the last character by default, unless you
set |virtualedit| to `onemore`.
Defaults:~
`inclusive_jump = false`
`uppercase_labels` *hop-config-uppercase_labels* `uppercase_labels` *hop-config-uppercase_labels*
Display labels as uppercase. This option only affects the displayed Display labels as uppercase. This option only affects the displayed
labels; you still select them by typing the keys on your keyboard. labels; you still select them by typing the keys on your keyboard.
@ -845,6 +860,9 @@ highlight is used. For the rest:
Highlight used for the fake cursor visible when running a Hop command / Highlight used for the fake cursor visible when running a Hop command /
Lua functions. Lua functions.
`HopPreview` *hop-hl-HopPreview*
Highlight used for to preview the hint for HopPattern.
Highlights are inserted in an augroup, `HopInitHighlight`, and an autocommand Highlights are inserted in an augroup, `HopInitHighlight`, and an autocommand
is automatically set when initializing the plugin, unless you set is automatically set when initializing the plugin, unless you set
|hop-config-create_hl_autocmd| to `false`. |hop-config-create_hl_autocmd| to `false`.

View File

@ -9,9 +9,9 @@ M.jump_on_sole_occurrence = true
M.case_insensitive = true M.case_insensitive = true
M.create_hl_autocmd = true M.create_hl_autocmd = true
M.current_line_only = false M.current_line_only = false
M.inclusive_jump = false
M.uppercase_labels = false M.uppercase_labels = false
M.multi_windows = false M.multi_windows = false
M.hint_position = require'hop.hint'.HintPosition.BEGIN M.hint_position = require'hop.hint'.HintPosition.BEGIN
M.hint_offset = 0
return M return M

View File

@ -5,7 +5,7 @@ local hop = require'hop'
-- --
-- This function will perform checks at initialization to ensure everything will work as expected. -- This function will perform checks at initialization to ensure everything will work as expected.
function M.check() function M.check()
local health = require'health' local health = vim.health or require'health'
health.report_start('Ensuring keys are unique') health.report_start('Ensuring keys are unique')
local existing_keys = {} local existing_keys = {}

View File

@ -19,6 +19,9 @@ function M.insert_highlights()
-- Highlight used for the fake cursor visible when hopping. -- Highlight used for the fake cursor visible when hopping.
vim.api.nvim_command('highlight default link HopCursor Cursor') vim.api.nvim_command('highlight default link HopCursor Cursor')
-- Highlight used for preview pattern
vim.api.nvim_command('highlight link HopPreview IncSearch')
end end
function M.create_autocmd() function M.create_autocmd()

View File

@ -99,8 +99,10 @@ function M.set_hint_extmarks(hl_ns, hints, opts)
label = label:upper() label = label:upper()
end end
local col = hint.jump_target.column - 1
if vim.fn.strdisplaywidth(label) == 1 then if vim.fn.strdisplaywidth(label) == 1 then
vim.api.nvim_buf_set_extmark(hint.jump_target.buffer or 0, hl_ns, hint.jump_target.line, hint.jump_target.column - 1, { vim.api.nvim_buf_set_extmark(hint.jump_target.buffer or 0, hl_ns, hint.jump_target.line, col, {
virt_text = { { label, "HopNextKey" } }, virt_text = { { label, "HopNextKey" } },
virt_text_pos = 'overlay', virt_text_pos = 'overlay',
hl_mode = 'combine', hl_mode = 'combine',
@ -109,7 +111,7 @@ function M.set_hint_extmarks(hl_ns, hints, opts)
else else
-- get the byte index of the second hint so that we can slice it correctly -- get the byte index of the second hint so that we can slice it correctly
local snd_idx = vim.fn.byteidx(label, 1) local snd_idx = vim.fn.byteidx(label, 1)
vim.api.nvim_buf_set_extmark(hint.jump_target.buffer or 0, hl_ns, hint.jump_target.line, hint.jump_target.column - 1, { -- HERE vim.api.nvim_buf_set_extmark(hint.jump_target.buffer or 0, hl_ns, hint.jump_target.line, col, {
virt_text = { { label:sub(1, snd_idx), "HopNextKey1" }, { label:sub(snd_idx + 1), "HopNextKey2" } }, virt_text = { { label:sub(1, snd_idx), "HopNextKey1" }, { label:sub(snd_idx + 1), "HopNextKey2" } },
virt_text_pos = 'overlay', virt_text_pos = 'overlay',
hl_mode = 'combine', hl_mode = 'combine',
@ -119,4 +121,16 @@ function M.set_hint_extmarks(hl_ns, hints, opts)
end end
end end
function M.set_hint_preview(hl_ns, jump_targets)
for _, jt in ipairs(jump_targets) do
vim.api.nvim_buf_set_extmark(jt.buffer, hl_ns, jt.line, jt.column - 1, {
end_row = jt.line,
end_col = jt.column - 1 + jt.length,
hl_group = 'HopPreview',
hl_eol = true,
priority = prio.HINT_PRIO
})
end
end
return M return M

View File

@ -1,9 +1,3 @@
local defaults = require'hop.defaults'
local hint = require'hop.hint'
local jump_target = require'hop.jump_target'
local prio = require'hop.priority'
local window = require'hop.window'
local M = {} local M = {}
-- Ensure options are sound. -- Ensure options are sound.
@ -18,6 +12,9 @@ local function check_opts(opts)
if opts.multi_windows and opts.current_line_only then if opts.multi_windows and opts.current_line_only then
vim.notify('Cannot use current_line_only across multiple windows', 3) vim.notify('Cannot use current_line_only across multiple windows', 3)
end end
if vim.api.nvim_get_mode().mode ~= 'n' then
opts.multi_windows = false
end
end end
-- Allows to override global options with user local overrides. -- Allows to override global options with user local overrides.
@ -33,18 +30,62 @@ local function eprintln(msg, teasing)
end end
end end
-- A hack to prevent #57 by deleting twice the namespace (its super weird). -- Create hint state
local function clear_namespace(buf_handle, hl_ns) --
vim.api.nvim_buf_clear_namespace(buf_handle, hl_ns, 0, -1) -- {
vim.api.nvim_buf_clear_namespace(buf_handle, hl_ns, 0, -1) -- all_ctxs: All windows's context
-- buf_list: All buffers displayed in all windows
-- <xxx>_ns: Required namespaces
-- }
local function create_hint_state(opts)
local window = require'hop.window'
local hint_state = {}
-- get all window's context and buffer list
hint_state.all_ctxs = window.get_window_context(opts.multi_windows)
hint_state.buf_list = {}
for _, bctx in ipairs(hint_state.all_ctxs) do
hint_state.buf_list[#hint_state.buf_list + 1] = bctx.hbuf
for _, wctx in ipairs(bctx.contexts) do
window.clip_window_context(wctx, opts.direction)
end
end
-- create the highlight groups; the highlight groups will allow us to clean everything at once when Hop quits
hint_state.hl_ns = vim.api.nvim_create_namespace('hop_hl')
hint_state.dim_ns = vim.api.nvim_create_namespace('hop_dim')
-- backup namespaces of diagnostic
if vim.fn.has("nvim-0.6") == 1 then
hint_state.diag_ns = vim.diagnostic.get_namespaces()
end
-- Store users cursorline state
hint_state.cursorline = vim.api.nvim_win_get_option(vim.api.nvim_get_current_win(), 'cursorline')
return hint_state
end end
-- Dim everything out to prepare the Hop session. -- A hack to prevent #57 by deleting twice the namespace (its super weird).
local function clear_namespace(buf_list, hl_ns)
for _, buf in ipairs(buf_list) do
if vim.api.nvim_buf_is_valid(buf) then
vim.api.nvim_buf_clear_namespace(buf, hl_ns, 0, -1)
vim.api.nvim_buf_clear_namespace(buf, hl_ns, 0, -1)
end
end
end
-- Set the highlight of unmatched lines of the buffer.
-- --
-- - hl_ns is the highlight namespace. -- - hl_ns is the highlight namespace.
-- - top_line is the top line in the buffer to start highlighting at -- - top_line is the top line in the buffer to start highlighting at
-- - bottom_line is the bottom line in the buffer to stop highlighting at -- - bottom_line is the bottom line in the buffer to stop highlighting at
local function apply_dimming(buf_handle, hl_ns, top_line, bottom_line, cursor_pos, direction, current_line_only) local function set_unmatched_lines(buf_handle, hl_ns, top_line, bottom_line, cursor_pos, direction, current_line_only)
local hint = require'hop.hint'
local prio = require'hop.priority'
local start_line = top_line local start_line = top_line
local end_line = bottom_line local end_line = bottom_line
local start_col = 0 local start_col = 0
@ -53,9 +94,8 @@ local function apply_dimming(buf_handle, hl_ns, top_line, bottom_line, cursor_po
if direction == hint.HintDirection.AFTER_CURSOR then if direction == hint.HintDirection.AFTER_CURSOR then
start_col = cursor_pos[2] start_col = cursor_pos[2]
elseif direction == hint.HintDirection.BEFORE_CURSOR then elseif direction == hint.HintDirection.BEFORE_CURSOR then
if cursor_pos[2] ~= 0 then end_line = bottom_line - 1
end_col = cursor_pos[2] + 1 if cursor_pos[2] ~= 0 then end_col = cursor_pos[2] end
end
end end
if current_line_only then if current_line_only then
@ -68,13 +108,45 @@ local function apply_dimming(buf_handle, hl_ns, top_line, bottom_line, cursor_po
end end
end end
vim.api.nvim_buf_set_extmark(buf_handle, hl_ns, start_line, start_col, { local extmark_options = {
end_line = end_line, end_line = end_line,
end_col = end_col,
hl_group = 'HopUnmatched', hl_group = 'HopUnmatched',
hl_eol = true, hl_eol = true,
priority = prio.DIM_PRIO priority = prio.DIM_PRIO
}) }
if end_col then
local current_line = vim.api.nvim_buf_get_lines(buf_handle, cursor_pos[1] - 1, cursor_pos[1], true)[1]
local current_width = vim.fn.strdisplaywidth(current_line)
if end_col > current_width then
end_col = current_width - 1
end
extmark_options.end_col = end_col
end
vim.api.nvim_buf_set_extmark(buf_handle, hl_ns, start_line, start_col,
extmark_options)
end
-- Dim everything out to prepare the Hop session for all windows.
local function apply_dimming(hint_state, opts)
local window = require'hop.window'
for _, bctx in ipairs(hint_state.all_ctxs) do
for _, wctx in ipairs(bctx.contexts) do
window.clip_window_context(wctx, opts.direction)
-- dim everything out, add the virtual cursor and hide diagnostics
set_unmatched_lines(bctx.hbuf, hint_state.dim_ns, wctx.top_line, wctx.bot_line, wctx.cursor_pos, opts.direction, opts.current_line_only)
end
if vim.fn.has("nvim-0.6") == 1 then
for ns in pairs(hint_state.diag_ns) do
vim.diagnostic.show(ns, bctx.hbuf, nil, { virtual_text = false })
end
end
end
end end
-- Add the virtual cursor, taking care to handle the cases where: -- Add the virtual cursor, taking care to handle the cases where:
@ -83,6 +155,8 @@ end
-- - the current line is empty -- - the current line is empty
-- - there are multibyte characters on the line -- - there are multibyte characters on the line
local function add_virt_cur(ns) local function add_virt_cur(ns)
local prio = require'hop.priority'
local cur_info = vim.fn.getcurpos() local cur_info = vim.fn.getcurpos()
local cur_row = cur_info[2] - 1 local cur_row = cur_info[2] - 1
local cur_col = cur_info[3] - 1 -- this gives cursor column location, in bytes local cur_col = cur_info[3] - 1 -- this gives cursor column location, in bytes
@ -90,6 +164,12 @@ local function add_virt_cur(ns)
local virt_col = cur_info[5] - 1 local virt_col = cur_info[5] - 1
local cur_line = vim.api.nvim_get_current_line() local cur_line = vim.api.nvim_get_current_line()
-- toggle cursorline off if currently set
local cursorline_info = vim.api.nvim_win_get_option(vim.api.nvim_get_current_win(), 'cursorline')
if cursorline_info == true then
vim.api.nvim_win_set_option(vim.api.nvim_get_current_win(), 'cursorline', false)
end
-- first check to see if cursor is in a tab char or past end of line -- first check to see if cursor is in a tab char or past end of line
if cur_offset ~= 0 then if cur_offset ~= 0 then
vim.api.nvim_buf_set_extmark(0, ns, cur_row, cur_col, { vim.api.nvim_buf_set_extmark(0, ns, cur_row, cur_col, {
@ -114,27 +194,112 @@ local function add_virt_cur(ns)
end end
end end
-- Get pattern from input for hint and preview
function M.get_input_pattern(prompt, maxchar, opts)
local hint = require'hop.hint'
local jump_target = require'hop.jump_target'
local hs = {}
if opts then
hs = create_hint_state(opts)
hs.preview_ns = vim.api.nvim_create_namespace('hop_preview')
apply_dimming(hs, opts)
add_virt_cur(hs.hl_ns)
end
local K_Esc = vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
local K_BS = vim.api.nvim_replace_termcodes('<BS>', true, false, true)
local K_C_H = vim.api.nvim_replace_termcodes('<C-H>', true, false, true)
local K_CR = vim.api.nvim_replace_termcodes('<CR>', true, false, true)
local K_NL = vim.api.nvim_replace_termcodes('<NL>', true, false, true)
local pat_keys = {}
local pat = ''
while (true) do
pat = vim.fn.join(pat_keys, '')
if opts then
clear_namespace(hs.buf_list, hs.preview_ns)
if #pat > 0 then
local ok, re = pcall(jump_target.regex_by_case_searching, pat, false, opts)
if ok then
local jump_target_gtr = jump_target.jump_targets_by_scanning_lines(re)
local generated = jump_target_gtr(opts)
hint.set_hint_preview(hs.preview_ns, generated.jump_targets)
end
end
end
vim.api.nvim_echo({}, false, {})
vim.cmd('redraw')
vim.api.nvim_echo({{prompt, 'Question'}, {pat}}, false, {})
local ok, key = pcall(vim.fn.getchar)
if not ok then -- Interrupted by <C-c>
pat = nil
break
end
if type(key) == 'number' then
key = vim.fn.nr2char(key)
elseif key:byte() == 128 then
-- It's a special key in string
end
if key == K_Esc then
pat = nil
break
elseif key == K_CR or key == K_NL then
break
elseif key == K_BS or key == K_C_H then
pat_keys[#pat_keys] = nil
else
pat_keys[#pat_keys + 1] = key
end
if maxchar and #pat_keys >= maxchar then
pat = vim.fn.join(pat_keys, '')
break
end
end
if opts then
clear_namespace(hs.buf_list, hs.preview_ns)
-- quit only when got nothin for pattern to avoid blink of highlight
if not pat then M.quit(hs) end
end
vim.api.nvim_echo({}, false, {})
vim.cmd('redraw')
return pat
end
-- Move the cursor at a given location. -- Move the cursor at a given location.
-- --
-- If inclusive is `true`, the jump target will be incremented visually by 1, so that operator-pending motions can -- Add option to shift cursor by column offset
-- correctly take into account the right offset. This is the main difference between motions such as `f` (inclusive)
-- and `t` (exclusive).
-- --
-- This function will update the jump list. -- This function will update the jump list.
function M.move_cursor_to(w, line, column, inclusive) function M.move_cursor_to(w, line, column, hint_offset, direction)
-- If we do not ask for inclusive jump, we dont have to retreive any additional lines because we will jump to the -- If we do not ask for an offset jump, we dont have to retrieve any additional lines because we will jump to the
-- actual jump target. If we do want an inclusive jump, we need to retreive the line the jump target lies in so that -- actual jump target. If we do want a jump with an offset, we need to retrieve the line the jump target lies in so
-- we can compute the offset correctly. This is linked to the fact that currently, Neovim doesns have an API to « -- that we can compute the offset correctly. This is linked to the fact that currently, Neovim doesns have an API to
-- offset something by 1 visual column. » -- « offset something by N visual columns. »
if inclusive then
-- If it is pending for operator shift column to the right by 1
if vim.api.nvim_get_mode().mode == 'no' and direction ~= 1 then
column = column + 1
end
if hint_offset ~= nil and not (hint_offset == 0) then
-- Add `hint_offset` based on `charidx`.
local buf_line = vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(w), line - 1, line, false)[1] local buf_line = vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(w), line - 1, line, false)[1]
column = vim.fn.byteidx(buf_line, column + 1) -- Since `charidx` returns -1 when `column` is the tail, subtract 1 and add 1 to the return value to get
-- the correct value.
local char_idx = vim.fn.charidx(buf_line, column - 1) + 1 + hint_offset
column = vim.fn.byteidx(buf_line, char_idx)
end end
-- update the jump list -- update the jump list
vim.cmd("normal! m'") vim.cmd("normal! m'")
vim.api.nvim_set_current_win(w) vim.api.nvim_set_current_win(w)
vim.api.nvim_win_set_cursor(w, { line, column}) vim.api.nvim_win_set_cursor(w, { line, column })
end end
function M.hint_with(jump_target_gtr, opts) function M.hint_with(jump_target_gtr, opts)
@ -143,11 +308,13 @@ function M.hint_with(jump_target_gtr, opts)
end end
M.hint_with_callback(jump_target_gtr, opts, function(jt) M.hint_with_callback(jump_target_gtr, opts, function(jt)
M.move_cursor_to(jt.window, jt.line + 1, jt.column - 1, opts.inclusive_jump) M.move_cursor_to(jt.window, jt.line + 1, jt.column - 1, opts.hint_offset, opts.direction)
end) end)
end end
function M.hint_with_callback(jump_target_gtr, opts, callback) function M.hint_with_callback(jump_target_gtr, opts, callback)
local hint = require'hop.hint'
if opts == nil then if opts == nil then
opts = override_opts(opts) opts = override_opts(opts)
end end
@ -157,64 +324,49 @@ function M.hint_with_callback(jump_target_gtr, opts, callback)
return return
end end
local all_ctxs = window.get_window_context(opts.multi_windows) -- create hint state
local hs = create_hint_state(opts)
-- create the highlight groups; the highlight groups will allow us to clean everything at once when Hop quits
local hl_ns = vim.api.nvim_create_namespace('hop_hl')
local dim_ns = vim.api.nvim_create_namespace('')
-- create jump targets -- create jump targets
local generated = jump_target_gtr(opts) local generated = jump_target_gtr(opts)
local jump_target_count = #generated.jump_targets local jump_target_count = #generated.jump_targets
local h = nil local target_idx = nil
if jump_target_count == 0 then if jump_target_count == 0 then
eprintln(' -> theres no such thing we can see…', opts.teasing) target_idx = 0
clear_namespace(0, hl_ns) elseif vim.v.count > 0 then
clear_namespace(0, dim_ns) target_idx = vim.v.count
return
elseif jump_target_count == 1 and opts.jump_on_sole_occurrence then elseif jump_target_count == 1 and opts.jump_on_sole_occurrence then
local jt = generated.jump_targets[1] target_idx = 1
callback(jt) end
clear_namespace(0, hl_ns) if target_idx ~= nil then
clear_namespace(0, dim_ns) local jt = generated.jump_targets[target_idx]
if jt then
callback(jt)
else
eprintln(' -> theres no such thing we can see…', opts.teasing)
end
clear_namespace(hs.buf_list, hs.hl_ns)
clear_namespace(hs.buf_list, hs.dim_ns)
return return
end end
-- we have at least two targets, so generate hints to display -- we have at least two targets, so generate hints to display
local hints = hint.create_hints(generated.jump_targets, generated.indirect_jump_targets, opts) hs.hints = hint.create_hints(generated.jump_targets, generated.indirect_jump_targets, opts)
local hint_state = { -- dim everything out, add the virtual cursor and hide diagnostics
hints = hints, apply_dimming(hs, opts)
hl_ns = hl_ns, add_virt_cur(hs.hl_ns)
dim_ns = dim_ns, hint.set_hint_extmarks(hs.hl_ns, hs.hints, opts)
}
local buf_list = {}
for _, bctx in ipairs(all_ctxs) do
buf_list[#buf_list + 1] = bctx.hbuf
for _, wctx in ipairs(bctx.contexts) do
window.clip_window_context(wctx, opts.direction)
-- dim everything out, add the virtual cursor and hide diagnostics
apply_dimming(bctx.hbuf, dim_ns, wctx.top_line, wctx.bot_line, wctx.cursor_pos, opts.direction, opts.current_line_only)
end
end
add_virt_cur(hl_ns)
if vim.fn.has("nvim-0.6") == 1 then
hint_state.diag_ns = vim.diagnostic.get_namespaces()
for ns in pairs(hint_state.diag_ns) do vim.diagnostic.show(ns, 0, nil, { virtual_text = false }) end
end
hint.set_hint_extmarks(hl_ns, hints, opts)
vim.cmd('redraw') vim.cmd('redraw')
local h = nil
while h == nil do while h == nil do
local ok, key = pcall(vim.fn.getchar) local ok, key = pcall(vim.fn.getchar)
if not ok then if not ok then
for _, buf in ipairs(buf_list) do M.quit(hs)
M.quit(buf, hint_state)
end
break break
end end
local not_special_key = true local not_special_key = true
@ -232,13 +384,11 @@ function M.hint_with_callback(jump_target_gtr, opts, callback)
if not_special_key and opts.keys:find(key, 1, true) then if not_special_key and opts.keys:find(key, 1, true) then
-- If this is a key used in Hop (via opts.keys), deal with it in Hop -- If this is a key used in Hop (via opts.keys), deal with it in Hop
h = M.refine_hints(buf_list, key, hint_state, callback, opts) h = M.refine_hints(key, hs, callback, opts)
vim.cmd('redraw') vim.cmd('redraw')
else else
-- If it's not, quit Hop -- If it's not, quit Hop
for _, buf in ipairs(buf_list) do M.quit(hs)
M.quit(buf, hint_state)
end
-- If the key captured via getchar() is not the quit_key, pass it through -- If the key captured via getchar() is not the quit_key, pass it through
-- to nvim to be handled normally (including mappings) -- to nvim to be handled normally (including mappings)
if key ~= vim.api.nvim_replace_termcodes(opts.quit_key, true, false, true) then if key ~= vim.api.nvim_replace_termcodes(opts.quit_key, true, false, true) then
@ -253,7 +403,9 @@ end
-- --
-- Refining hints allows to advance the state machine by one step. If a terminal step is reached, this function jumps to -- Refining hints allows to advance the state machine by one step. If a terminal step is reached, this function jumps to
-- the location. Otherwise, it stores the new state machine. -- the location. Otherwise, it stores the new state machine.
function M.refine_hints(buf_list, key, hint_state, callback, opts) function M.refine_hints(key, hint_state, callback, opts)
local hint = require'hop.hint'
local h, hints = hint.reduce_hints(hint_state.hints, key) local h, hints = hint.reduce_hints(hint_state.hints, key)
if h == nil then if h == nil then
@ -264,15 +416,10 @@ function M.refine_hints(buf_list, key, hint_state, callback, opts)
hint_state.hints = hints hint_state.hints = hints
for _, buf in ipairs(buf_list) do clear_namespace(hint_state.buf_list, hint_state.hl_ns)
clear_namespace(buf, hint_state.hl_ns)
end
hint.set_hint_extmarks(hint_state.hl_ns, hints, opts) hint.set_hint_extmarks(hint_state.hl_ns, hints, opts)
vim.cmd('redraw')
else else
for _, buf in ipairs(buf_list) do M.quit(hint_state)
M.quit(buf, hint_state)
end
-- prior to jump, register the current position into the jump list -- prior to jump, register the current position into the jump list
vim.cmd("normal! m'") vim.cmd("normal! m'")
@ -283,16 +430,28 @@ function M.refine_hints(buf_list, key, hint_state, callback, opts)
end end
-- Quit Hop and delete its resources. -- Quit Hop and delete its resources.
function M.quit(buf_handle, hint_state) function M.quit(hint_state)
clear_namespace(buf_handle, hint_state.hl_ns) clear_namespace(hint_state.buf_list, hint_state.hl_ns)
clear_namespace(buf_handle, hint_state.dim_ns) clear_namespace(hint_state.buf_list, hint_state.dim_ns)
if vim.fn.has("nvim-0.6") == 1 then -- Restore users cursorline setting
for ns in pairs(hint_state.diag_ns) do vim.diagnostic.show(ns, buf_handle) end if hint_state.cursorline == true then
vim.api.nvim_win_set_option(vim.api.nvim_get_current_win(), 'cursorline', true)
end
for _, buf in ipairs(hint_state.buf_list) do
-- sometimes, buffers might be unloaded; thats the case with floats for instance (we can invoke Hop from them but
-- then they disappear); we need to check whether the buffer is still valid before trying to do anything else with
-- it
if vim.api.nvim_buf_is_valid(buf) and vim.fn.has("nvim-0.6") == 1 then
for ns in pairs(hint_state.diag_ns) do vim.diagnostic.show(ns, buf) end
end
end end
end end
function M.hint_words(opts) function M.hint_words(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local generator local generator
@ -309,20 +468,26 @@ function M.hint_words(opts)
end end
function M.hint_patterns(opts, pattern) function M.hint_patterns(opts, pattern)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
-- The pattern to search is either retrieved from the (optional) argument -- The pattern to search is either retrieved from the (optional) argument
-- or directly from user input. -- or directly from user input.
if pattern == nil then local pat
if pattern then
pat = pattern
else
vim.cmd('redraw')
vim.fn.inputsave() vim.fn.inputsave()
pat = M.get_input_pattern('Hop pattern: ', nil, opts)
local ok
ok, pattern = pcall(vim.fn.input, 'Search: ')
vim.fn.inputrestore() vim.fn.inputrestore()
if not pat then return end
end
if not ok then if #pat == 0 then
return eprintln('-> empty pattern', opts.teasing)
end return
end end
local generator local generator
@ -333,16 +498,18 @@ function M.hint_patterns(opts, pattern)
end end
M.hint_with( M.hint_with(
generator(jump_target.regex_by_case_searching(pattern, false, opts)), generator(jump_target.regex_by_case_searching(pat, false, opts)),
opts opts
) )
end end
function M.hint_char1(opts) function M.hint_char1(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local ok, c = pcall(vim.fn.getchar) local c = M.get_input_pattern('Hop 1 char: ', 1)
if not ok then if not c then
return return
end end
@ -354,32 +521,21 @@ function M.hint_char1(opts)
end end
M.hint_with( M.hint_with(
generator(jump_target.regex_by_case_searching(vim.fn.nr2char(c), true, opts)), generator(jump_target.regex_by_case_searching(c, true, opts)),
opts opts
) )
end end
function M.hint_char2(opts) function M.hint_char2(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local ok, a = pcall(vim.fn.getchar) local c = M.get_input_pattern('Hop 2 char: ', 2)
if not ok then if not c then
return return
end end
local ok2, b = pcall(vim.fn.getchar)
if not ok2 then
return
end
local pattern = vim.fn.nr2char(a)
-- if we have a fallback key defined in the opts, if the second character is that key, we then fallback to the same
-- behavior as hint_char1()
if opts.char2_fallback_key == nil or b ~= vim.fn.char2nr(vim.api.nvim_replace_termcodes(opts.char2_fallback_key, true, false, true)) then
pattern = pattern .. vim.fn.nr2char(b)
end
local generator local generator
if opts.current_line_only then if opts.current_line_only then
generator = jump_target.jump_targets_for_current_line generator = jump_target.jump_targets_for_current_line
@ -388,12 +544,14 @@ function M.hint_char2(opts)
end end
M.hint_with( M.hint_with(
generator(jump_target.regex_by_case_searching(pattern, true, opts)), generator(jump_target.regex_by_case_searching(c, true, opts)),
opts opts
) )
end end
function M.hint_lines(opts) function M.hint_lines(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local generator local generator
@ -404,12 +562,37 @@ function M.hint_lines(opts)
end end
M.hint_with( M.hint_with(
generator(jump_target.regex_by_line_start()), generator(jump_target.by_line_start()),
opts opts
) )
end end
function M.hint_vertical(opts)
local hint = require'hop.hint'
local jump_target = require'hop.jump_target'
opts = override_opts(opts)
-- only makes sense as end position given movement goal.
opts.hint_position = hint.HintPosition.END
local generator
if opts.current_line_only then
generator = jump_target.jump_targets_for_current_line
else
generator = jump_target.jump_targets_by_scanning_lines
end
-- FIXME: need to exclude current and include empty lines.
M.hint_with(
generator(jump_target.regex_by_vertical()),
opts
)
end
function M.hint_lines_skip_whitespace(opts) function M.hint_lines_skip_whitespace(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local generator local generator
@ -426,6 +609,8 @@ function M.hint_lines_skip_whitespace(opts)
end end
function M.hint_anywhere(opts) function M.hint_anywhere(opts)
local jump_target = require'hop.jump_target'
opts = override_opts(opts) opts = override_opts(opts)
local generator local generator
@ -444,7 +629,7 @@ end
-- Setup user settings. -- Setup user settings.
function M.setup(opts) function M.setup(opts)
-- Look up keys in user-defined table with fallback to defaults. -- Look up keys in user-defined table with fallback to defaults.
M.opts = setmetatable(opts or {}, {__index = defaults}) M.opts = setmetatable(opts or {}, {__index = require'hop.defaults'})
M.initialized = true M.initialized = true
-- Insert the highlights and register the autocommand if asked to. -- Insert the highlights and register the autocommand if asked to.

View File

@ -91,6 +91,13 @@ local function mark_jump_targets_line(buf_handle, win_handle, regex, line_contex
if b == nil or (b == 0 and e == 0) then if b == nil or (b == 0 and e == 0) then
break break
end end
-- Preview need a length to highlight the matched string. Zero means nothing to highlight.
local matched_length = e - b
-- As the make for jump target must be placed at a cell (but some pattern like '^' is
-- placed between cells), we should make sure e > b
if b == e then
e = e + 1
end
local colp = col + b local colp = col + b
if hint_position == hint.HintPosition.MIDDLE then if hint_position == hint.HintPosition.MIDDLE then
@ -99,10 +106,9 @@ local function mark_jump_targets_line(buf_handle, win_handle, regex, line_contex
colp = col + e - 1 colp = col + e - 1
end end
jump_targets[#jump_targets + 1] = { jump_targets[#jump_targets + 1] = {
line = line_nr,
column = math.max(1, colp + col_offset + col_bias),
line = line_context.line_nr, line = line_context.line_nr,
column = math.max(1, colp + col_offset + col_bias), column = math.max(1, colp + col_offset + col_bias),
length = math.max(0, matched_length),
buffer = buf_handle, buffer = buf_handle,
window = win_handle, window = win_handle,
} }
@ -342,10 +348,13 @@ function M.regex_by_searching(pat, plain_search)
if plain_search then if plain_search then
pat = vim.fn.escape(pat, '\\/.$^~[]') pat = vim.fn.escape(pat, '\\/.$^~[]')
end end
local regex = vim.regex(pat)
return { return {
oneshot = false, oneshot = false,
match = function(s) match = function(s)
return vim.regex(pat):match_str(s) return regex:match_str(s)
end end
} }
end end
@ -364,10 +373,12 @@ function M.regex_by_case_searching(pat, plain_search, opts)
pat = '\\c' .. pat pat = '\\c' .. pat
end end
local regex = vim.regex(pat)
return { return {
oneshot = false, oneshot = false,
match = function(s) match = function(s)
return vim.regex(pat):match_str(s) return regex:match_str(s)
end end
} }
end end
@ -378,22 +389,42 @@ function M.regex_by_word_start()
end end
-- Line regex. -- Line regex.
function M.regex_by_line_start() function M.by_line_start()
local c = vim.fn.winsaveview().leftcol
return { return {
oneshot = true, oneshot = true,
match = function(_) match = function(s)
return 0, 1, false local l = vim.fn.strdisplaywidth(s)
if c > 0 and l == 0 then
return nil
end
return 0, 1
end
}
end
-- Line regex at cursor position.
function M.regex_by_vertical()
local position = vim.api.nvim_win_get_cursor(0)[2]
local regex = vim.regex(string.format("^.\\{0,%d\\}\\(.\\|$\\)", position))
return {
oneshot = true,
match = function(s)
return regex:match_str(s)
end end
} }
end end
-- Line regex skipping finding the first non-whitespace character on each line. -- Line regex skipping finding the first non-whitespace character on each line.
function M.regex_by_line_start_skip_whitespace() function M.regex_by_line_start_skip_whitespace()
local pat = vim.regex("\\S") local regex = vim.regex("\\S")
return { return {
oneshot = true, oneshot = true,
match = function(s) match = function(s)
return pat:match_str(s) return regex:match_str(s)
end end
} }
end end

View File

@ -7,7 +7,7 @@ end
-- Get the next key of the input key in the input key set, if any, or return nil. -- Get the next key of the input key in the input key set, if any, or return nil.
local function next_key(keys, key) local function next_key(keys, key)
local _, e = keys:find(key) local _, e = keys:find(key, 1, true)
if e == #keys then if e == #keys then
return nil return nil

View File

@ -4,6 +4,7 @@ local M = {}
local function window_context(win_handle, cursor_pos) local function window_context(win_handle, cursor_pos)
-- get a bunch of information about the window and the cursor -- get a bunch of information about the window and the cursor
vim.api.nvim_set_current_win(win_handle)
local win_info = vim.fn.getwininfo(win_handle)[1] local win_info = vim.fn.getwininfo(win_handle)[1]
local win_view = vim.fn.winsaveview() local win_view = vim.fn.winsaveview()
local top_line = win_info.topline - 1 local top_line = win_info.topline - 1
@ -52,9 +53,10 @@ function M.get_window_context(multi_windows)
-- Generate contexts of windows -- Generate contexts of windows
local cur_hwin = vim.api.nvim_get_current_win() local cur_hwin = vim.api.nvim_get_current_win()
local cur_hbuf = vim.api.nvim_win_get_buf(cur_hwin) local cur_hbuf = vim.api.nvim_win_get_buf(cur_hwin)
all_ctxs[#all_ctxs + 1] = { all_ctxs[#all_ctxs + 1] = {
hbuf = cur_hbuf, hbuf = cur_hbuf,
contexts = { window_context(cur_hwin, vim.api.nvim_win_get_cursor(cur_hwin)) }, contexts = { window_context(cur_hwin, {vim.fn.line('.'), vim.fn.charcol('.')} ) },
} }
if not multi_windows then if not multi_windows then
@ -131,7 +133,7 @@ end
-- If the direction is HintDirection.AFTER_CURSOR, then everything before the cursor will be clipped. -- If the direction is HintDirection.AFTER_CURSOR, then everything before the cursor will be clipped.
function M.clip_window_context(context, direction) function M.clip_window_context(context, direction)
if direction == hint.HintDirection.BEFORE_CURSOR then if direction == hint.HintDirection.BEFORE_CURSOR then
context.bot_line = context.cursor_pos[1] - 1 context.bot_line = context.cursor_pos[1]
elseif direction == hint.HintDirection.AFTER_CURSOR then elseif direction == hint.HintDirection.AFTER_CURSOR then
context.top_line = context.cursor_pos[1] - 1 context.top_line = context.cursor_pos[1] - 1
end end

View File

@ -47,12 +47,18 @@ command! HopLineBC lua require'hop'.hint_lines({ direction = require'hop.hint'.H
command! HopLineAC lua require'hop'.hint_lines({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR }) command! HopLineAC lua require'hop'.hint_lines({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR })
command! HopLineMW lua require'hop'.hint_lines({ multi_windows = true }) command! HopLineMW lua require'hop'.hint_lines({ multi_windows = true })
" The jump-to-line command. " The jump-to-line command (non-whitespace).
command! HopLineStart lua require'hop'.hint_lines_skip_whitespace() command! HopLineStart lua require'hop'.hint_lines_skip_whitespace()
command! HopLineStartBC lua require'hop'.hint_lines_skip_whitespace({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR }) command! HopLineStartBC lua require'hop'.hint_lines_skip_whitespace({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR })
command! HopLineStartAC lua require'hop'.hint_lines_skip_whitespace({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR }) command! HopLineStartAC lua require'hop'.hint_lines_skip_whitespace({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR })
command! HopLineStartMW lua require'hop'.hint_lines_skip_whitespace({ multi_windows = true }) command! HopLineStartMW lua require'hop'.hint_lines_skip_whitespace({ multi_windows = true })
" The vertical command (line jump preserving the column cursor position).
command! HopVertical lua require'hop'.hint_vertical()
command! HopVerticalBC lua require'hop'.hint_vertical({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR })
command! HopVerticalAC lua require'hop'.hint_vertical({ direction = require'hop.hint'.HintDirection.AFTER_CURSOR })
command! HopVerticalMW lua require'hop'.hint_vertical({ multi_windows = true })
" The jump-to-anywhere command. " The jump-to-anywhere command.
command! HopAnywhere lua require'hop'.hint_anywhere() command! HopAnywhere lua require'hop'.hint_anywhere()
command! HopAnywhereBC lua require'hop'.hint_anywhere({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR }) command! HopAnywhereBC lua require'hop'.hint_anywhere({ direction = require'hop.hint'.HintDirection.BEFORE_CURSOR })