1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-04-14 07:09:11 +08:00

Add smooth scrolling key bindings (#4386)

This commit is contained in:
Wang Shidong 2021-08-12 10:45:36 +08:00 committed by GitHub
parent bb7a54808b
commit 061de45b7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 844 additions and 253 deletions

View File

@ -227,12 +227,6 @@ function! SpaceVim#default#keyBindings() abort
" Improve scroll, credits: https://github.com/Shougo
nnoremap <expr> zz (winline() == (winheight(0)+1) / 2) ?
\ 'zt' : (winline() == &scrolloff + 1) ? 'zb' : 'zz'
noremap <expr> <C-f> max([winheight(0) - 2, 1])
\ ."\<C-d>".(line('w$') >= line('$') ? "L" : "H")
noremap <expr> <C-b> max([winheight(0) - 2, 1])
\ ."\<C-u>".(line('w0') <= 1 ? "H" : "L")
noremap <expr> <C-e> (line("w$") >= line('$') ? "j" : "3\<C-e>")
noremap <expr> <C-y> (line("w0") <= 1 ? "k" : "3\<C-y>")
" Select blocks after indenting
xnoremap < <gv

View File

@ -23,6 +23,7 @@ function! SpaceVim#layers#core#plugins() abort
call add(plugins, [g:_spacevim_root_dir . 'bundle/nvim-yarp', {'merged': 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-hug-neovim-rpc', {'merged': 0}])
endif
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-smoothie', {'merged': 0}])
if g:spacevim_filemanager ==# 'nerdtree'
call add(plugins, [g:_spacevim_root_dir . 'bundle/nerdtree', { 'merged' : 0,
\ 'loadconf' : 1}])
@ -171,12 +172,12 @@ function! SpaceVim#layers#core#config() abort
\ . string(s:_function('s:buffer_transient_state')) . ', [])',
\ ['buffer-transient-state',
\ [
\ '[SPC b .] is to open the buffer transient state',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
\ '[SPC b .] is to open the buffer transient state',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
call SpaceVim#mapping#space#def('nnoremap', ['b', 'd'], 'call SpaceVim#mapping#close_current_buffer()', 'delete-this-buffer', 1)
call SpaceVim#mapping#space#def('nnoremap', ['b', 'D'],
\ 'call SpaceVim#mapping#kill_visible_buffer_choosewin()',
@ -245,36 +246,36 @@ function! SpaceVim#layers#core#config() abort
call SpaceVim#mapping#space#def('nnoremap', ['f', 'v', 'd'], 'SPConfig',
\ ['open-custom-configuration',
\ [
\ '[SPC f v d] is to open the custom configuration file for SpaceVim',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
\ '[SPC f v d] is to open the custom configuration file for SpaceVim',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
let lnum = expand('<slnum>') + s:lnum - 1
call SpaceVim#mapping#space#def('nnoremap', ['n', '-'], 'call call('
\ . string(s:_function('s:number_transient_state')) . ', ["-"])',
\ ['decrease-number-under-cursor',
\ [
\ '[SPC n -] is to decrease the number under the cursor, and open',
\ 'the number translate state buffer',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
\ '[SPC n -] is to decrease the number under the cursor, and open',
\ 'the number translate state buffer',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
let lnum = expand('<slnum>') + s:lnum - 1
call SpaceVim#mapping#space#def('nnoremap', ['n', '+'], 'call call('
\ . string(s:_function('s:number_transient_state')) . ', ["+"])',
\ ['increase-number-under-cursor',
\ [
\ '[SPC n +] is to increase the number under the cursor, and open',
\ 'the number translate state buffer',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
\ '[SPC n +] is to increase the number under the cursor, and open',
\ 'the number translate state buffer',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
let g:vimproc#download_windows_dll = 1
" call SpaceVim#mapping#space#def('nnoremap', ['p', 't'], 'call SpaceVim#plugins#projectmanager#current_root()', 'find-project-root', 1)
let g:_spacevim_mappings_space.p.t = {'name' : '+Tasks'}
@ -301,12 +302,12 @@ function! SpaceVim#layers#core#config() abort
\ . string(s:_function('s:close_current_tab')) . ', [])',
\ ['close-current-tab',
\ [
\ '[SPC q t] is to close the current tab, if it is the last tab, do nothing.',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
\ '[SPC q t] is to close the current tab, if it is the last tab, do nothing.',
\ '',
\ 'Definition: ' . s:filename . ':' . lnum,
\ ]
\ ]
\ , 1)
call SpaceVim#mapping#gd#add('HelpDescribe', function('s:gotodef'))
let g:_spacevim_mappings_space.c = {'name' : '+Comments'}
@ -359,27 +360,27 @@ function! s:number_transient_state(n) abort
call state.set_title('Number Transient State')
call state.defind_keys(
\ {
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : ['+','='],
\ 'desc' : 'increase number',
\ 'func' : '',
\ 'cmd' : "normal! \<c-a>",
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : '-',
\ 'desc' : 'decrease number',
\ 'func' : '',
\ 'cmd' : "normal! \<c-x>",
\ 'exit' : 0,
\ },
\ ],
\ }
\ )
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : ['+','='],
\ 'desc' : 'increase number',
\ 'func' : '',
\ 'cmd' : "normal! \<c-a>",
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : '-',
\ 'desc' : 'decrease number',
\ 'func' : '',
\ 'cmd' : "normal! \<c-x>",
\ 'exit' : 0,
\ },
\ ],
\ }
\ )
call state.open()
endfunction
@ -439,23 +440,23 @@ endfunction
let g:string_info = {
\ 'vim' : {
\ 'connect' : '.',
\ 'line_prefix' : '\',
\ },
\ 'java' : {
\ 'connect' : '+',
\ 'line_prefix' : '',
\ },
\ 'perl' : {
\ 'connect' : '.',
\ 'line_prefix' : '\',
\ },
\ 'python' : {
\ 'connect' : '+',
\ 'line_prefix' : '\',
\ 'quotes_hi' : ['pythonQuotes']
\ },
\ }
\ 'connect' : '.',
\ 'line_prefix' : '\',
\ },
\ 'java' : {
\ 'connect' : '+',
\ 'line_prefix' : '',
\ },
\ 'perl' : {
\ 'connect' : '.',
\ 'line_prefix' : '\',
\ },
\ 'python' : {
\ 'connect' : '+',
\ 'line_prefix' : '\',
\ 'quotes_hi' : ['pythonQuotes']
\ },
\ }
function! s:split_string(newline) abort
if s:is_string(line('.'), col('.'))
@ -611,104 +612,104 @@ function! s:buffer_transient_state() abort
call state.set_title('Buffer Selection Transient State')
call state.defind_keys(
\ {
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : {
\ 'name' : 'C-1..C-9',
\ 'pos' : [[1,4], [6,9]],
\ 'handles' : [
\ ["\<C-1>" , ''],
\ ["\<C-2>" , ''],
\ ["\<C-3>" , ''],
\ ["\<C-4>" , ''],
\ ["\<C-5>" , ''],
\ ["\<C-6>" , ''],
\ ["\<C-7>" , ''],
\ ["\<C-8>" , ''],
\ ["\<C-9>" , ''],
\ ],
\ },
\ 'desc' : 'goto nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ {
\ 'key' : {
\ 'name' : '1..9',
\ 'pos' : [[1,2], [4,5]],
\ 'handles' : [
\ ['1' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [1])'],
\ ['2' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [2])'],
\ ['3' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [3])'],
\ ['4' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [4])'],
\ ['5' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [5])'],
\ ['6' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [6])'],
\ ['7' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [7])'],
\ ['8' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [8])'],
\ ['9' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [9])'],
\ ],
\ },
\ 'desc' : 'move buffer to nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ {
\ 'key' : {
\ 'name' : 'M-1..M-9',
\ 'pos' : [[1,4], [6,9]],
\ 'handles' : [
\ ["\<M-1>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [1])'],
\ ["\<M-2>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [2])'],
\ ["\<M-3>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [3])'],
\ ["\<M-4>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [4])'],
\ ["\<M-5>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [5])'],
\ ["\<M-6>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [6])'],
\ ["\<M-7>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [7])'],
\ ["\<M-8>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [8])'],
\ ["\<M-9>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [9])'],
\ ],
\ },
\ 'desc' : 'swap buffer with nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : 'n',
\ 'desc' : 'next buffer',
\ 'func' : '',
\ 'cmd' : 'bnext',
\ 'exit' : 0,
\ },
\ {
\ 'key' : ['N', 'p'],
\ 'desc' : 'previous buffer',
\ 'func' : '',
\ 'cmd' : 'bp',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'd',
\ 'desc' : 'kill buffer',
\ 'func' : '',
\ 'cmd' : 'call SpaceVim#mapping#close_current_buffer()',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'q',
\ 'desc' : 'quit',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 1,
\ },
\ ],
\ }
\ )
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : {
\ 'name' : 'C-1..C-9',
\ 'pos' : [[1,4], [6,9]],
\ 'handles' : [
\ ["\<C-1>" , ''],
\ ["\<C-2>" , ''],
\ ["\<C-3>" , ''],
\ ["\<C-4>" , ''],
\ ["\<C-5>" , ''],
\ ["\<C-6>" , ''],
\ ["\<C-7>" , ''],
\ ["\<C-8>" , ''],
\ ["\<C-9>" , ''],
\ ],
\ },
\ 'desc' : 'goto nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ {
\ 'key' : {
\ 'name' : '1..9',
\ 'pos' : [[1,2], [4,5]],
\ 'handles' : [
\ ['1' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [1])'],
\ ['2' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [2])'],
\ ['3' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [3])'],
\ ['4' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [4])'],
\ ['5' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [5])'],
\ ['6' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [6])'],
\ ['7' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [7])'],
\ ['8' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [8])'],
\ ['9' , 'call call(' . string(s:_function('s:move_buffer_to_nth_win')) . ', [9])'],
\ ],
\ },
\ 'desc' : 'move buffer to nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ {
\ 'key' : {
\ 'name' : 'M-1..M-9',
\ 'pos' : [[1,4], [6,9]],
\ 'handles' : [
\ ["\<M-1>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [1])'],
\ ["\<M-2>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [2])'],
\ ["\<M-3>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [3])'],
\ ["\<M-4>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [4])'],
\ ["\<M-5>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [5])'],
\ ["\<M-6>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [6])'],
\ ["\<M-7>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [7])'],
\ ["\<M-8>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [8])'],
\ ["\<M-9>" , 'call call(' . string(s:_function('s:swap_buffer_with_nth_win')) . ', [9])'],
\ ],
\ },
\ 'desc' : 'swap buffer with nth window',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : 'n',
\ 'desc' : 'next buffer',
\ 'func' : '',
\ 'cmd' : 'bnext',
\ 'exit' : 0,
\ },
\ {
\ 'key' : ['N', 'p'],
\ 'desc' : 'previous buffer',
\ 'func' : '',
\ 'cmd' : 'bp',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'd',
\ 'desc' : 'kill buffer',
\ 'func' : '',
\ 'cmd' : 'call SpaceVim#mapping#close_current_buffer()',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'q',
\ 'desc' : 'quit',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 1,
\ },
\ ],
\ }
\ )
call state.open()
endfunction
@ -826,48 +827,48 @@ function! s:jump_transient_state() abort
call state.set_title('Jump Transient State')
call state.defind_keys(
\ {
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : 'j',
\ 'desc' : 'next jump',
\ 'func' : '',
\ 'cmd' : 'try | exe "norm! \<C-i>"| catch | endtry ',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'J',
\ 'desc' : 'previous jump',
\ 'func' : '',
\ 'cmd' : 'try | exe "norm! \<c-o>" | catch | endtry',
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : 'c',
\ 'desc' : 'next change',
\ 'func' : '',
\ 'cmd' : "try | exe 'norm! g,' | catch | endtry",
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'C',
\ 'desc' : 'previous change',
\ 'func' : '',
\ 'cmd' : "try | exe 'norm! g;' | catch | endtry",
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'q',
\ 'desc' : 'quit',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 1,
\ },
\ ],
\ }
\ )
\ 'layout' : 'vertical split',
\ 'left' : [
\ {
\ 'key' : 'j',
\ 'desc' : 'next jump',
\ 'func' : '',
\ 'cmd' : 'try | exe "norm! \<C-i>"| catch | endtry ',
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'J',
\ 'desc' : 'previous jump',
\ 'func' : '',
\ 'cmd' : 'try | exe "norm! \<c-o>" | catch | endtry',
\ 'exit' : 0,
\ },
\ ],
\ 'right' : [
\ {
\ 'key' : 'c',
\ 'desc' : 'next change',
\ 'func' : '',
\ 'cmd' : "try | exe 'norm! g,' | catch | endtry",
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'C',
\ 'desc' : 'previous change',
\ 'func' : '',
\ 'cmd' : "try | exe 'norm! g;' | catch | endtry",
\ 'exit' : 0,
\ },
\ {
\ 'key' : 'q',
\ 'desc' : 'quit',
\ 'func' : '',
\ 'cmd' : '',
\ 'exit' : 1,
\ },
\ ],
\ }
\ )
call state.open()
endfunction

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 20192020 vim-smoothie authors (see README.md for a complete list)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,100 @@
vim-smoothie: Smooth scrolling for Vim done right🥤
===================================================
This (neo)vim plugin makes scrolling nice and _smooth_. Find yourself
completely lost every time you press `Ctrl-D` or `Ctrl-F`? You might want to
give _vim-smoothie_ a try!
![scrolling demo](demo.gif)
Installation
------------
You will need reasonably new Vim or Neovim with timers support. Vim 8+ or
Neovim 0.3+ should do the trick.
Install the plugin using your favorite plugin manager, for example [vim-plug]:
```
Plug 'psliwka/vim-smoothie'
```
Customization
-------------
_vim-smoothie_ aims for sane defaults, and should work out-of-the-box for most
users. In some cases, however, you might want to customize its behavior, by
adjusting one or more of the following variables in your `vimrc`:
* `g:smoothie_enabled`: Set it to `0` (or `v:false`) to disable vim-smoothie.
Useful for temporary disabling the plugin, f.ex. when running Vim over
extremely slow connections.
* `g:smoothie_no_default_mappings`: If true, will prevent the plugin from
overriding default scrolling keys (`Ctrl-D` and friends). You are then
supposed to bind keys you like by yourself. See `plugin/smoothie.vim` to
discover available mappings.
* `g:smoothie_experimental_mappings`: Set this to true to enable additional,
experimental mappings (currently `gg` and `G`). It is not recommended to
enable them unless you're ready to fight potential bugs.
Alternatives, a.k.a. why create yet another plugin
--------------------------------------------------
There are many other Vim plugins attempting to resolve the same problem. The
most interesting one is [sexy_scroller.vim], which covers way more movement
commands than vim-smoothie will ever do. Unfortunately, it also suffers from
frequent visual artifacts, such as erratic screen jumps and animation
jittering, impairing visual orientation and breaking the user experience. Many
of these bugs are nearly impossible to fix due to the plugin's internal design.
Hence, vim-smoothie was born, focusing on stable, bug-free, _smooth_
experience, at a cost of smaller feature set.
The table below summarizes key differences between vim-smoothie and three other
popular smooth scrolling plugins I've used in the past: [sexy_scroller.vim],
[comfortable-motion.vim], and [vim-smooth-scroll].
| | vim-smoothie | [sexy_scroller.vim] | [comfortable-motion.vim] | [vim-smooth-scroll] |
|---|:---:|:---:|:---:|:---:|
| Supported commands | `^D` `^U` `^F` `^B` `gg`<sup>1</sup> `G`<sup>1</sup> | A lot❤ | `^D` `^U` `^F` `^B` | `^D` `^U` `^F` `^B` |
| Erratic screen jumps and jittering now and then | Nope | A lot💔 | Nope | Nope |
| Scrolling distance is proportional to window height | ✅ | ✅ | ❌ | ✅ |
| Easing out (soft-stop) | ✅ | ✅ | ✅ | ❌ |
| Supports setting `[count]` before movement (f.ex. `3^F` to scroll down 3 pages) | ✅ | ✅ | ❌ | ❌ |
| Respects `scroll` and `startofline` options | ✅ | ✅ | ❌ | ❌ |
| `^D` and `^U` behave correctly near buffer ends, just moving the cursor instead of scrolling the window | ✅ | ✅ | ❌ | ❌ |
| Terminal bell support | ✅ | ✅ | ❌ | ❌ |
| Pun in name | ✅ | ✅ | ❌ | ❌ |
Notes:
1. these commands are experimental and disabled by default
Known issues/incompatibilities
------------------------------
vim-smoothie strives to remain fully compatible with native commands it
replaces. That is, every command should still behave exactly as described in
`:help scroll.txt`. There are still some deviations from the original behavior,
which hopefully will be addressed in the future:
* `^F` and `^B` should respect the `window` option.
* Native commands may move in a smarter way over wrapped/folded lines.
Credits
-------
Created by [Piotr Śliwka](https://github.com/psliwka). Improved by
[Klas Segeljakt](https://github.com/segeljakt) and
[Subhaditya Nath](https://github.com/subnut).
Many thanks to authors of [vim-smooth-scroll], [comfortable-motion.vim], and
[sexy_scroller.vim] for inspiration!
License
-------
[MIT](LICENSE)
[vim-plug]: https://github.com/junegunn/vim-plug
[vim-smooth-scroll]: https://github.com/terryma/vim-smooth-scroll
[comfortable-motion.vim]: https://github.com/yuttie/comfortable-motion.vim
[sexy_scroller.vim]: https://github.com/joeytwiddle/sexy_scroller.vim

View File

@ -0,0 +1,416 @@
""
" This variable is used to inform the s:step_*() functions about whether the
" current movement is a cursor movement or a scroll movement. Used for
" motions like gg and G
let s:cursor_movement = v:false
""
" This variable is needed to let the s:step_down() function know whether to
" continue scrolling after reaching EOL (as in ^F) or not (^B, ^D, ^U, etc.)
"
" NOTE: This variable "MUST" be set to v:false in "every" function that
" invokes motion (except smoothie#forwards, where it must be set to v:true)
let s:ctrl_f_invoked = v:false
if !exists('g:smoothie_enabled')
""
" Set it to 0 to disable vim-smoothie. Useful for very slow connections.
let g:smoothie_enabled = 1
endif
if !exists('g:smoothie_update_interval')
""
" Time (in milliseconds) between subsequent screen/cursor position updates.
" Lower value produces smoother animation. Might be useful to increase it
" when running Vim over low-bandwidth/high-latency connections.
let g:smoothie_update_interval = 20
endif
if !exists('g:smoothie_speed_constant_factor')
""
" This value controls constant term of the velocity curve. Increasing this
" boosts primarily cursor speed at the end of animation.
let g:smoothie_speed_constant_factor = 10
endif
if !exists('g:smoothie_speed_linear_factor')
""
" This value controls linear term of the velocity curve. Increasing this
" boosts primarily cursor speed at the beginning of animation.
let g:smoothie_speed_linear_factor = 10
endif
if !exists('g:smoothie_speed_exponentiation_factor')
""
" This value controls exponent of the power function in the velocity curve.
" Generally should be less or equal to 1.0. Lower values produce longer but
" perceivably smoother animation.
let g:smoothie_speed_exponentiation_factor = 0.9
endif
if !exists('g:smoothie_break_on_reverse')
""
" Stop immediately if we're moving and the user requested moving in opposite
" direction. It's mostly useful at very low scrolling speeds, hence
" disabled by default.
let g:smoothie_break_on_reverse = 0
endif
""
" Execute {command}, but saving 'scroll' value before, and restoring it
" afterwards. Useful for some commands (such as ^D or ^U), which overwrite
" 'scroll' permanently if used with a [count].
"
" Additionally, this function temporarily clears 'scrolloff' and resets it
" after command execution. This is workaround for a bug described in
" https://github.com/psliwka/vim-smoothie/issues/18
function s:execute_preserving_scroll(command)
let l:saved_scroll = &scroll
let l:saved_scrolloff = 0
if &scrolloff
let l:saved_scrolloff = &scrolloff
let &scrolloff = 0
endif
execute a:command
let &scroll = l:saved_scroll
if l:saved_scrolloff
let &scrolloff = l:saved_scrolloff
endif
endfunction
""
" Scroll the window up by one line, or move the cursor up if the window is
" already at the top. Return 1 if cannot move any higher.
function s:step_up()
if line('.') > 1
if s:cursor_movement
exe 'normal! k'
return 0
endif
call s:execute_preserving_scroll("normal! 1\<C-U>")
return 0
else
return 1
endif
endfunction
""
" Scroll the window down by one line, or move the cursor down if the window is
" already at the bottom. Return 1 if cannot move any lower.
function s:step_down()
let l:initial_winline = winline()
if line('.') < line('$')
if s:cursor_movement
exe 'normal! j'
return 0
endif
" NOTE: the three lines of code following this comment block
" have been implemented as a temporary workaround for a vim issue
" regarding Ctrl-D and folds.
"
" See: neovim/neovim#13080
if foldclosedend('.') != -1
call cursor(foldclosedend('.'), col('.'))
endif
call s:execute_preserving_scroll("normal! 1\<C-D>")
if s:ctrl_f_invoked && winline() > l:initial_winline
" ^F is pressed, and the last motion caused cursor postion to change
" scroll window to keep cursor position fixed
call s:execute_preserving_scroll("normal! \<C-E>")
endif
return 0
elseif s:ctrl_f_invoked && winline() > 1
" cursor is already on last line of buffer, but not on last line of window
" ^F can scroll more
call s:execute_preserving_scroll("normal! \<C-E>")
return 0
else
return 1
endif
endfunction
""
" Perform as many steps up or down to move {lines} lines from the starting
" position (negative {lines} value means to go up). Return 1 if hit either
" top or bottom, and cannot move further.
function s:step_many(lines)
let l:remaining_lines = a:lines
while 1
if l:remaining_lines < 0
if s:step_up()
return 1
endif
let l:remaining_lines += 1
elseif l:remaining_lines > 0
if s:step_down()
return 1
endif
let l:remaining_lines -= 1
else
return 0
endif
endwhile
endfunction
""
" A Number indicating how many lines do we need yet to move down (or up, if
" it's negative), to achieve what the user wants.
let s:target_displacement = 0
""
" A Float between -1.0 and 1.0 keeping our position between integral lines,
" used to make the animation smoother.
let s:subline_position = 0.0
""
" Start the animation timer if not already running. Should be called when
" updating the target, when there's a chance we're not already moving.
function s:start_moving()
if ((s:target_displacement < 0) ? line('.') == 1 : (line('.') == line('$') && (s:ctrl_f_invoked ? winline() == 1 : v:true)))
" Invalid command
call s:ring_bell()
endif
if !exists('s:timer_id')
let s:timer_id = timer_start(g:smoothie_update_interval, function('s:movement_tick'), {'repeat': -1})
endif
endfunction
""
" Stop any movement immediately, and disable the animation timer to conserve
" power.
function s:stop_moving()
let s:target_displacement = 0
let s:subline_position = 0.0
if exists('s:timer_id')
call timer_stop(s:timer_id)
unlet s:timer_id
endif
endfunction
""
" Calculate optimal movement velocity (in lines per second, negative value
" means to move upwards) for the next animation frame.
"
" TODO: current algorithm is rather crude, would be good to research better
" alternatives.
function s:compute_velocity()
let l:absolute_speed = g:smoothie_speed_constant_factor + g:smoothie_speed_linear_factor * pow(abs(s:target_displacement - s:subline_position), g:smoothie_speed_exponentiation_factor)
if s:target_displacement < 0
return -l:absolute_speed
else
return l:absolute_speed
endif
endfunction
""
" Execute single animation frame. Called periodically by a timer. Accepts a
" throwaway parameter: the timer ID.
function s:movement_tick(_)
if s:target_displacement == 0
call s:stop_moving()
return
endif
let l:subline_step_size = s:subline_position + (g:smoothie_update_interval/1000.0 * s:compute_velocity())
let l:step_size = float2nr(trunc(l:subline_step_size))
if abs(l:step_size) > abs(s:target_displacement)
" clamp step size to prevent overshooting the target
let l:step_size = s:target_displacement
end
if s:step_many(l:step_size)
" we've collided with either buffer end
call s:stop_moving()
else
let s:target_displacement -= l:step_size
let s:subline_position = l:subline_step_size - l:step_size
endif
if l:step_size
" Usually Vim handles redraws well on its own, but without explicit redraw
" I've encountered some sporadic display artifacts. TODO: debug further.
redraw
endif
endfunction
""
" Set a new target where we should move to (in lines, relative to our current
" position). If we're already moving, try to do the smart thing, taking into
" account our progress in reaching the target set previously.
function s:update_target(lines)
if g:smoothie_break_on_reverse && s:target_displacement * a:lines < 0
call s:stop_moving()
else
" Cursor movements are very delicate. Since the displacement for cursor
" movements is calulated from the "current" line, so immediately stop
" moving, otherwise we will end up at the wrong line.
if s:cursor_movement
call s:stop_moving()
endif
let s:target_displacement += a:lines
call s:start_moving()
endif
endfunction
""
" Helper function to calculate the actual number of screen lines from a line
" to another. Useful for properly handling folds in case of cursor movements.
function s:calculate_screen_lines(from, to)
let l:from = a:from
let l:to = a:to
let l:from = (foldclosed(l:from) != -1 ? foldclosed(l:from) : l:from)
let l:to = (foldclosed(l:to) != -1 ? foldclosed(l:to) : l:to)
if l:from == l:to
return 0
endif
let l:lines = 0
let l:linenr = l:from
while l:linenr != l:to
if l:linenr < l:to
let l:lines +=1
let l:linenr = (foldclosedend(l:linenr) != -1 ? foldclosedend(l:linenr) : l:linenr)
let l:linenr += 1
elseif l:linenr > l:to
let l:lines -= 1
let l:linenr = (foldclosed(l:linenr) != -1 ? foldclosed(l:linenr) : l:linenr)
let l:linenr -= 1
endif
endwhile
return l:lines
endfunction
""
" Helper function to set 'scroll' to [count], similarly to what native ^U and
" ^D commands do.
function s:count_to_scroll()
if v:count
let &scroll=v:count
end
endfunction
""
" Helper function to ring bell.
function s:ring_bell()
if !(&belloff =~# 'all\|error')
let l:belloff = &belloff
set belloff=
exe "normal \<Esc>"
let &belloff = l:belloff
endif
endfunction
""
" Smooth equivalent to ^D.
function smoothie#downwards()
if !g:smoothie_enabled
exe "normal! \<C-d>"
return
endif
let s:ctrl_f_invoked = v:false
call s:count_to_scroll()
call s:update_target(&scroll)
endfunction
""
" Smooth equivalent to ^U.
function smoothie#upwards()
if !g:smoothie_enabled
exe "normal! \<C-u>"
return
endif
let s:ctrl_f_invoked = v:false
call s:count_to_scroll()
call s:update_target(-&scroll)
endfunction
""
" Smooth equivalent to ^F.
function smoothie#forwards()
if !g:smoothie_enabled
exe "normal! \<C-f>"
return
endif
let s:ctrl_f_invoked = v:true
call s:update_target(winheight(0) * v:count1)
endfunction
""
" Smooth equivalent to ^B.
function smoothie#backwards()
if !g:smoothie_enabled
exe "normal! \<C-b>"
return
endif
let s:ctrl_f_invoked = v:false
call s:update_target(-winheight(0) * v:count1)
endfunction
""
" Smoothie equivalent for G and gg
" NOTE: I have also added - movement to dempnstrate how to add more new
" movements in the future
function smoothie#cursor_movement(movement)
let l:movements = {
\'gg': {
\'target_expr': 'v:count1',
\'startofline': &startofline,
\'jump_commmand': v:true,
\},
\'G' : {
\'target_expr': "(v:count ? v:count : line('$'))",
\'startofline': &startofline,
\'jump_commmand': v:true,
\},
\'-' : {
\'target_expr': "line('.') - v:count1",
\'startofline': v:true,
\'jump_commmand': v:false,
\},
\}
if !has_key(l:movements, a:movement)
return 1
endif
call s:do_vertical_cursor_movement(a:movement, l:movements[a:movement])
endfunction
""
" Helper function to preform cursor movements
function s:do_vertical_cursor_movement(movement, properties)
let s:cursor_movement = v:true
let s:ctrl_f_invoked = v:false
" If in operator pending mode, disable vim-smoothie and use the normal
" non-smoothie version of the movement
if !g:smoothie_enabled || mode(1) =~# 'o' && mode(1) =~? 'no'
" If in operator-pending mode, prefer the movement to be linewise
exe 'normal! ' . (mode(1) ==# 'no' ? 'V' : '') . v:count . a:movement
return
endif
let l:target = eval(a:properties['target_expr'])
let l:target = (l:target > line('$') ? line('$') : l:target)
let l:target = (foldclosed(l:target) != -1 ? foldclosed(l:target) : l:target)
if foldclosed('.') == l:target
let s:cursor_movement = v:false
return
endif
" if this is a jump command, append current position to the jumplist
if a:properties['jump_commmand']
execute "normal! m'"
endif
call s:update_target(s:calculate_screen_lines(line('.'), l:target))
" suspend further commands till the destination is reached
" see point (3) of https://github.com/psliwka/vim-smoothie/issues/1#issuecomment-560158642
while line('.') != l:target
exe 'sleep ' . g:smoothie_update_interval . ' m'
endwhile
let s:cursor_movement = v:false " reset s:cursor_movement to false
if a:properties['startofline']
" move cursor to the first non-blank character of the line
call cursor(line('.'), match(getline('.'),'\S')+1)
endif
endfunction
" vim: et ts=2

View File

@ -0,0 +1,55 @@
if !has('float') || !has('timers')
let g:smoothie_enabled = 0
echohl WarningMsg
echom 'vim-smoothie needs +timers and +float'
echohl None
finish
endif
if has('nvim') || has('patch-8.2.1978')
noremap <silent> <Plug>(SmoothieDownwards) <cmd>call smoothie#downwards() <CR>
noremap <silent> <Plug>(SmoothieUpwards) <cmd>call smoothie#upwards() <CR>
noremap <silent> <Plug>(SmoothieForwards) <cmd>call smoothie#forwards() <CR>
noremap <silent> <Plug>(SmoothieBackwards) <cmd>call smoothie#backwards() <CR>
noremap <silent> <Plug>(Smoothie_gg) <cmd>call smoothie#cursor_movement('gg') <CR>
noremap <silent> <Plug>(Smoothie_G) <cmd>call smoothie#cursor_movement('G') <CR>
if !get(g:, 'smoothie_no_default_mappings', v:false)
silent! map <unique> <C-D> <Plug>(SmoothieDownwards)
silent! map <unique> <C-U> <Plug>(SmoothieUpwards)
silent! map <unique> <C-F> <Plug>(SmoothieForwards)
silent! map <unique> <S-Down> <Plug>(SmoothieForwards)
silent! map <unique> <PageDown> <Plug>(SmoothieForwards)
silent! map <unique> <C-B> <Plug>(SmoothieBackwards)
silent! map <unique> <S-Up> <Plug>(SmoothieBackwards)
silent! map <unique> <PageUp> <Plug>(SmoothieBackwards)
if get(g:, 'smoothie_experimental_mappings', v:false)
silent! map <unique> gg <Plug>(Smoothie_gg)
silent! map <unique> G <Plug>(Smoothie_G)
endif
endif
else
nnoremap <silent> <Plug>(SmoothieDownwards) :<C-U>call smoothie#downwards() <CR>
nnoremap <silent> <Plug>(SmoothieUpwards) :<C-U>call smoothie#upwards() <CR>
nnoremap <silent> <Plug>(SmoothieForwards) :<C-U>call smoothie#forwards() <CR>
nnoremap <silent> <Plug>(SmoothieBackwards) :<C-U>call smoothie#backwards() <CR>
nnoremap <silent> <Plug>(Smoothie_gg) :<C-U>call smoothie#cursor_movement('gg') <CR>
nnoremap <silent> <Plug>(Smoothie_G) :<C-U>call smoothie#cursor_movement('G') <CR>
if !get(g:, 'smoothie_no_default_mappings', v:false)
silent! nmap <unique> <C-D> <Plug>(SmoothieDownwards)
silent! nmap <unique> <C-U> <Plug>(SmoothieUpwards)
silent! nmap <unique> <C-F> <Plug>(SmoothieForwards)
silent! nmap <unique> <S-Down> <Plug>(SmoothieForwards)
silent! nmap <unique> <PageDown> <Plug>(SmoothieForwards)
silent! nmap <unique> <C-B> <Plug>(SmoothieBackwards)
silent! nmap <unique> <S-Up> <Plug>(SmoothieBackwards)
silent! nmap <unique> <PageUp> <Plug>(SmoothieBackwards)
if get(g:, 'smoothie_experimental_mappings', v:false)
silent! nmap <unique> gg <Plug>(Smoothie_gg)
silent! nmap <unique> G <Plug>(Smoothie_G)
endif
endif
endif
" vim: et ts=2

View File

@ -815,26 +815,28 @@ SpaceVim 的文件树提供了版本控制信息的接口,但是这一特性
以下列出了最常用的移动光标以及滚屏的快捷键:
| 快捷键 | 功能描述 |
| ----------------- | ------------------------------ |
| `h` | 向左移动光标 |
| `j` | 向下移动光标 |
| `k` | 向上移动光标 |
| `l` | 向右移动光标 |
| `<Up>` | 向上移动光标,不跳过折行 |
| `<Down>` | 向下移动光标,不跳过折行 |
| `H` | 移动光标至屏幕顶部 |
| `L` | 移动光标至屏幕底部 |
| `<` | 向左移动文本 |
| `>` | 向右移动文本 |
| `}` | 向前移动一个段落 |
| `{` | 向后移动一个段落 |
| `Ctrl-f` | 向下翻页 (`Ctrl-f` / `Ctrl-d`) |
| `Ctrl-b` | 向上翻页 (`C-b` / `C-u`) |
| `Ctrl-e` | 向下滚屏 (`3 Ctrl-e/j`) |
| `Ctrl-y` | 向上滚屏 (`3Ctrl-y/k`) |
| `Ctrl-Shift-Up` | 向上移动当前行 |
| `Ctrl-Shift-Down` | 向下移动当前行 |
| 快捷键 | 功能描述 |
| -------------------------------------- | ------------------------ |
| `h` | 向左移动光标 |
| `j` | 向下移动光标 |
| `k` | 向上移动光标 |
| `l` | 向右移动光标 |
| `<Up>` | 向上移动光标,不跳过折行 |
| `<Down>` | 向下移动光标,不跳过折行 |
| `H` | 移动光标至屏幕顶部 |
| `L` | 移动光标至屏幕底部 |
| `<` | 向左移动文本 |
| `>` | 向右移动文本 |
| `}` | 向前移动一个段落 |
| `{` | 向后移动一个段落 |
| `Ctrl-f` / `Shift-Down` / `<PageDown>` | 向下翻页 |
| `Ctrl-b` / `Shift-Up` / `<PageUp>` | 向上翻页 |
| `Ctrl-d` | 向下滚屏 |
| `Ctrl-u` | 向上滚屏 |
| `Ctrl-e` | 向下滚屏 (`3 Ctrl-e/j`) |
| `Ctrl-y` | 向上滚屏 (`3Ctrl-y/k`) |
| `Ctrl-Shift-Up` | 向上移动当前行 |
| `Ctrl-Shift-Down` | 向下移动当前行 |
### 原生功能

View File

@ -885,23 +885,25 @@ If only one file buffer is opened, a file is opened in the active window, otherw
The following key bindings are the general key bindings for moving the cursor.
| Key Bindings | Descriptions |
| ---------------- | ---------------------------------------- |
| `h` | move cursor left |
| `j` | move cursor down |
| `k` | move cursor up |
| `l` | move cursor right |
| `<Up>`, `<Down>` | Smart up and down |
| `H` | move cursor to the top of the screen |
| `L` | move cursor to the bottom of the screen |
| `<` | Indent to left and re-select |
| `>` | Indent to right and re-select |
| `}` | paragraphs forward |
| `{` | paragraphs backward |
| `Ctrl-f` | Smart page forward (`Ctrl-f` / `Ctrl-d`) |
| `Ctrl-b` | Smart page backward (`C-b` / `C-u`) |
| `Ctrl-e` | Smart scroll down (`3 Ctrl-e/j`) |
| `Ctrl-y` | Smart scroll up (`3Ctrl-y/k`) |
| Key Bindings | Descriptions |
| -------------------------------------- | --------------------------------------- |
| `h` | move cursor left |
| `j` | move cursor down |
| `k` | move cursor up |
| `l` | move cursor right |
| `<Up>`, `<Down>` | Smart up and down |
| `H` | move cursor to the top of the screen |
| `L` | move cursor to the bottom of the screen |
| `<` | Indent to left and re-select |
| `>` | Indent to right and re-select |
| `}` | paragraphs forward |
| `{` | paragraphs backward |
| `Ctrl-f` / `Shift-Down` / `<PageDown>` | Smooth scrolling forwards |
| `Ctrl-b` / `Shift-Up` / `<PageUp>` | Smooth scrolling backwards |
| `Ctrl-d` | Smooth scrolling downwards |
| `Ctrl-u` | Smooth scrolling upwards |
| `Ctrl-e` | Smart scroll down (`3 Ctrl-e/j`) |
| `Ctrl-y` | Smart scroll up (`3Ctrl-y/k`) |
### Native functions