Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Move Fast

Use convenient, single-key presses to perform repetitive navigation - without remapping.

Enter movefast "mode", then use e.g. h and l to move - whether you are scrolling a buffer, navigating the buffer list, browsing a diff, stepping through quickfix entries or anything else that involves repetitive commands. The plugin contains some built-in movements but is entirely extensible - build the movefast movements you need!

Examples (using the built-in movements below):

Scrolling: Instead of Ctrl+dCtrl+dCtrl+uCtrl+d, use Spacejjkj

Tabbing: Instead of gtgtgTgt, use Spacetllhl

Buffering: To navigate buffer history for this window, use Spacebllhl

Each of these demonstrates a mapping to enter movefast-mode and perform the first movement (Spacej, Spacetl, Spacebl), then repeated movements using j/k or h/l.

Contents

Anatomy of a movement

Movements consist of 3 parts:

  1. Initialization of the movement mode
  2. Navigation within the mode
  3. Completion of the movement

A mapping is used to begin initialization. At its simplest, the mapping can call movefast#Init(), passing in an initial "direction" and a minimal options dictionary containing a 'next' callback reference:

nnoremap<silent>hl:call movefast#Init('l', { 'next': 'ChangeArg' })<CR>nnoremap<silent>lh:call movefast#Init('h', { 'next': 'ChangeArg' })<CR>

As there are no other options specified in the dictionary sent to movefast#Init(), the default h/l directions are used in this movement. The callback then performs the actual navigation, it could look like this:

function!ChangeArg(direction)
ifa:direction==# 'h'previouselseifa:direction==# 'l'nextendifredrawendfunction

With these mappings and the ChangeArg() callback function, hl or lh will enter a "change arg" mode and h and l will continue navigating forwards and backwards through the argument list.

Any cursor movement other than a "direction" (h/l) completes the the movement.

Movement options

All movefast movements are configured with an "options" dictionary. Available options are:

OptionDescriptionExamples
directionsAn array of direction keys['j', 'k']
["\<Left>","\<Right>"]
titleThe text to echo while in movefast mode'Scrolling...'
nextA funcref or function name to use to perform the movement, accepts a direction argument'ScrollNext'
function('ScrollNext')
oninitA funcref or expression to evaluate before initialising a movement'let save_cursor = getcurpos()'
onnextA funcref or expression to evaluate after each movement nextfunction('lightline#update')
oncompleteA funcref or expression to evaluate after leaving movefast mode'call setpos('.', save_cursor)'
cancelAn array of keys to use to complete the movement (advanced)['h', 'l']
bufferFlag indicating that this movement does not change buffer, allowing buffer-local mappings (advanced)1
getcharFlag forcing movefast to use getchar() to listen for movements, rather than mappings (advanced)1

Advanced options

There are 2 techniques used for reacting to movement directions: mappings and getchar(). They each have pros and cons.

By default, movements use mappings. To use getchar instead, use the getchar: 1 option.

getchar()

After performing each movement, a getchar() call is made to listen for the next input key. A direction key triggers another navigation, and any other key key completes the movement.

Pros:

  • Any non-direction key can be used to complete a movement.

Cons:

  • getchar() captures a keypress, meaning that it can't be used for anything else, e.g. a multi-key command or mapping. This means that a non-direction keypress is required to complete a movement before any multi-key commands can be input, which can be confusing. For example, after entering a movement, you may try to scroll to the top of the buffer with gg but the first g will be used to complete the motion, so Vim is left in operator-pending mode, waiting for the next key.
  • Vim remains in command-line mode, and the cursor is hidden, making it hard to keep track of where your last navigation has left you.

Mappings

Temporary mappings are created for the 2 directions, and removed again on movement completion. If conflicting mappings exist, we fall back to "getchar" mode.

Detection of movement completion is more difficult with mappings, as we can't simply "listen" for any non-direction key, as we can with getchar(). Instead, CursorMoved and InsertEnter autocmds are used to detect movement completion. Additionally, a movement may be configured with extra "cancellation" mapping keys, using the cancel: 1 option. A use-case may be to allow h to always complete vertical scrolling, even when the cursor is on the first column (meaning h does not move the cursor and trigger a CursorMoved event).

Pros:

  • A multi-key command or mapping can be used at any time.
  • Vim remains in normal mode.

Cons:

  • Complete existing mappings (e.g. nnoremap l ll) prevent a movement mapping from being made (fall back to "getchar mode").
  • Partial existing mappings (e.g. nnoremap ll 5l) result in slow movements, as vim waits for 'timeoutlen' before performing the navigation, in case another key of the existing mapping is entered.
  • Cannot always determine when a movement is complete.

The first 2 cons may be resolved by using buffer-local mappings, as buffer mappings will override global mappings and can use the modifiers. However, buffer-local mappings will of course only work for movements which do not change buffer! Buffer/tab/arg navigation movements cannot use buffer-local mappings, so of the 3 built-in movements, only scrolling uses buffer-local mappings. Use the buffer: 1 option to make a movement use buffer-local mappings.

Built-in movements

Some movements are included in the plugin:

  • Scroll: vertical (<C-d>/<C-u>) and horizontal (zH/zL) scrolling
  • Tab: move through tabs with gt and gT
  • Buffer: move through window-local or global buffer history

Buffer history

The "scroll" and "tab" movements are simple movefast movements, but the buffer movement is more complicated. The plugin maintains window-local buffer stacks, allowing navigation back and forth through the window history. On movement completion, the final buffer is moved to the top of the history stack, and the buffer which initialised the movement becomes the "alternate" buffer (allowing :h CTRL-^ navigation).

Additionally, a global buffer history stack is maintained, allowing navigation through all normal buffers, in order of last access. Only "normal" buffers and help buffers are included in the history - not special buffers such as quickfix buffers.

Note that the buffer movement always starts from the top of the history stack, so there are no <Plug>(movefast_buffer_next)/<Plug>(movefast_buffer_global_next) maps. It only makes sense to begin navigating back through the history.

Should these history stacks be useful outside of vim-movefast, they are stored in variables g:movefast_buffer_history (global) and w:movefast_buffer_history (window).

Built-in movement configuration

No mappings are provided automatically. Add mappings in your .vimrc to add the movefast movements you are interested in:

" Default directions: 'h'/'l'nmap<Space>bh<Plug>(movefast_buffer_prev)nmap<Space>Bh<Plug>(movefast_buffer_global_prev)" Default directions: 'j'/'k'nmap<Space>j<Plug>(movefast_scroll_down)nmap<Space>k<Plug>(movefast_scroll_up)" Default directions: 'h'/'l'nmap<Space>h<Plug>(movefast_scroll_left)nmap<Space>l<Plug>(movefast_scroll_right)" Default directions: 'h'/'l'nmap<Space>th<Plug>(movefast_tab_prev)nmap<Space>tl<Plug>(movefast_tab_next)

These mappings work well with the default movefast directions for the built-in movements. The options (see Movement options above) for these movements can be overridden using the following variables:

MovementOptions variableDefault directions
Buffer history (window)g:movefast_buffer['h', 'l']
Buffer history (global)g:movefast_buffer_global['h', 'l']
Vertical scrollg:movefast_scroll['j', 'k']
Horizontal scrollg:movefast_scroll_horizontal['h', 'l']
Tab navigationg:movefast_tab['h', 'l']

Here are some customization examples:

" Use 'j' and 'k' for window-local buffer history navigationletg:movefast_buffer= { 'directions': ['j', 'k'] }
" Use 'J' and 'K' for global buffer history navigationletg:movefast_buffer_global= { 'directions': ['J', 'K'] }
" Use arrow keys and custom titles for scrolling, and update vim-lightline" statusline after each movementletg:movefast_scroll= {
\ 'directions': ["\<Down>", "\<Up>"],
\ 'title': 'Scrolling...',
\ 'onnext': function('lightline#update')
\}letg:movefast_scroll_horizontal= {
\ 'directions': ["\<Left>", "\<Right>"],
\ 'title': 'Scrolling side-to-side...',
\ 'onnext': function('lightline#update')
\}" Initialize buffer history navigation with `oi`, and use `i`/`o` to" continuenmapoi<Plug>(movefast_buffer_prev)letg:movefast_buffer= { 'directions': ['i', 'o'] }

About

An extensible, modal navigation plugin for Vim

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages