Wheel is a :
- file group manager
- session manager (tabs & windows)
- navigation plugin
- refactoring tool
for Vim and Neovim.
Our favorite editor has already plenty of nice navigation functions. Wheel enhances their interface by using :
- intuitive completion with multi-pattern support for prompting functions
- dedicated buffers, in which you can filter and select elements, besides using the full power of your editor
- a meta-command with subcommands, actions and completion
- edit modes, that allow you to reflect your changes in a dedicated buffer to the original file(s)
With these tools, any line in any file is only a few keys away.
All is written in lightweight, classical Vimscript. No dependency required.
See the wheel-multimedia repository.
Wheel let you organize your files by creating as many file groups as you need, add the files you want to it and quickly navigate between :
- files of the same group
- file groups
Note that :
- a location contains a name, a filename, as well as a line & column number
- a file group, in fact a location group, is called a circle
- a set of file groups, or a category, is called a torus (a circle of circles)
- the list of toruses is called the wheel
Currently, there are more than a thousand files in my groups, and it runs like a breeze.
At first glance, managing groups with circles in a torus seems to be sufficient. But with time, the torus grows big, and a third level helps you to organize your files by groups and categories:
- the wheel contains all the toruses
- each torus contains a category of files, e.g.:
- configuration, development, publication
- each circle contains a project, e.g.:
- kitty or vifm circles in configuration torus
- shell or vimscript in development torus
- tea or art in publication torus
You can also organize a torus in subprojects. For instance, in the wheel torus, I have the following groups :
- plugin/ dir files
- autoload/ dir files
- doc files
- wiki files
- test files
Wheel is designed to follow your workflow : you only add the files
you want, where you want. For instance, if you have a organize
group
with agenda & todo files, you can quickly alternate them, or display
them in two windows. Then, if you suddenly got an idea to tune vim,
you switch to the vim
group with your favorites configuration files in
it. Same process, to cycle, alternate or display the files. Over time,
your groups will grow and adapt to your style.
The group manager is the core, but it goes far beyond that : you need a quick navigation framework to travel in the wheel, and once it is there, it’s easy to add new functionalities.
- add
- files from anywhere in the filesystem
- a file in more than one group
- file:line-1 and file:line-2 in the same group
- may be saved in wheel file (recommended)
- on demand loading of files
- no slowdown of (neo)vim start
- easy navigation
- switch to matching tab & window if available
- next / previous location, circle or torus
- single or multi-pattern completion in prompting functions
- choose file, group or category in dedicated buffer
- filter candidates
- selection tools
- preview
- folds matching wheel tree structure
- context menus
- auto
:lcd
to project root of current file - history of wheel files
- anywhere
- in same group
- in same category
- signs displayed at wheel locations
- search files
- using locate
- using find
- MRU files not found in wheel
- opened buffers
- visible buffers in tabs & windows
- search inside files
- grep on group files
- navigate
- edit mode : edit and propagate changes by writing the dedicated buffer
- outline
- folds headers in group files (based on fold markers)
- markdown headers
- org mode headers
- tags
- markers
- jumps & changes lists
- grep on group files
- narrow
- current file
- all circle file with a pattern
- yank ring using TextYankPost event
- paste before or after, linewise or characterwise
- switch register ring
- reorganizing
- wheel elements
- tabs & windows
- undo list
- diff between last & chosen state
- command output in buffer
- :ex or !shell command
- async shell command
- result can be filtered, as usual
- dedicated buffers ring to save your searches
- layer ring in each dedicated buffer
- batch operations
- autogroup files by extension or directory
- save tabs & windows in minimal session file
- display files
- split levels : torus, circle, location
- split
- vertical, golden vertical
- horizontal, golden horizontal
- main left, golden left
- main top, golden top
- grid
- mix of above
- circles on tabs, locations on split
- toruses on tabs, circles on split
This project is inspired by :
-
torus, a file group plugin for Emacs, itself inspired by MTorus
-
ctrlspace, a workspace plugin for Vim
-
unite, a search plugin for arbitrary sources
-
quickfix-reflector, for the grep edit mode
-
NrrwRgn, for the narrow dedicated buffers
-
YankRing, whose name is self-explanatory
- vim >= 8.2
- neovim >= 0.6
Basically, it assumes the existence of :map-cmd
and #{...}
syntax
for dictionaries.
If your distribution uses an older version, you can resort to appimages :
These are fast evolving pieces of software, it's worth upgrading anyway.
Some outer rim functions assume a Unix-like OS, like Linux or BSD :
- async functions
- external commands, like locate
- mirror the wheel structure in a filesystem tree
Most of the plugin should work out of the box on other OSes, however. If you encounter some problem, please let me know.
Simply add this line after packager#init()
to your initialisation file :
call packager#add('chimay/wheel', { 'type' : 'start' })
and run :PackagerInstall
(see the
vim-packager readme).
Simply add this line after minpac#init()
to your initialisation file :
call minpac#add('chimay/wheel', { 'type' : 'start' })
and run :PackUpdate
(see the
minpac readme).
The syntax should be similar with other git oriented plugin managers :
Plug 'chimay/wheel'
and run :PlugInstall
to install.
You can clone the repository somewhere in your runtime-search-path
. You
can get a minimal version by asking a shallow clone (depth 1) and
filtering out the screenshots blobs :
mkdir -p ~/.local/share/nvim/site/pack/foo/start
cd ~/.local/share/nvim/site/pack/foo/start
git clone --depth 1 --filter=blob:none https://github.com/chimay/wheel
If you install or update with git, don't forget to run :
:helptags doc
to be able to use the inline help.
Your guide on the wheel tracks :
:help wheel.txt
A wheel wiki is also available.
It is recommended to read at least the
step-by-step
and workflow
pages, either in the wiki or in the wheel.txt
file.
In the help submenu of the main menu (default map : <M-w><M-m>
), you have
access to :
- the inline help (wheel.txt)
- the list of current wheel mappings
- the list of available plug mappings
- the list of :Wheel subcommands and actions
- the list of autocommands of your wheel group
- a dedicated buffer basic help
- local buffer maps
For a thorough list of options, see the configuration and autocommands pages in the wiki.
Here is an example of configuration :
if ! exists("g:wheel_loaded")
" ---- DONT FORGET TO INITIALIZE DICTS BEFORE USING THEM
let g:wheel_config = {}
let g:wheel_config.project = {}
let g:wheel_config.storage = {}
let g:wheel_config.storage.wheel = {}
let g:wheel_config.storage.session = {}
let g:wheel_config.maxim = {}
let g:wheel_config.completion = {}
let g:wheel_config.frecency = {}
let g:wheel_config.display = {}
let g:wheel_config.display.sign = {}
" ---- The bigger it is, the more mappings available
let g:wheel_config.mappings = 10
" ---- Prefix for mappings
let g:wheel_config.prefix = '<M-w>'
" ---- Locate database ; default one if left empty
let g:wheel_config.locate_db = '~/index/locate/home.db'
" ---- Grep command : :grep or :vimpgrep
let g:wheel_config.grep = 'grep'
" Marker of project root
"let g:wheel_config.project.markers = '.git'
"let g:wheel_config.project.markers = '.project-root'
" List of markers
" The project dir is found as soon as one marker is found in it
let g:wheel_config.project.markers = ['.hg' , '.git', '.project-root']
" Auto cd to project root if > 0
let g:wheel_config.project.auto_chdir = 1
" The folder where toruses and circles will be stored and read
let g:wheel_config.storage.wheel.folder = '~/.local/share/wheel'
" Name of the default wheel file
let g:wheel_config.storage.wheel.name = 'wheel.vim'
" Auto read wheel file on startup if > 0
let g:wheel_config.storage.wheel.autoread = 1
" Auto write wheel file on exit if > 0
let g:wheel_config.storage.wheel.autowrite = 1
" The folder where sessions will be stored and read
let g:wheel_config.storage.session.folder = '~/.local/share/wheel/session'
" Name of the default session file
let g:wheel_config.storage.session.name = 'session.vim'
" Auto read default session file on startup if > 0
let g:wheel_config.storage.session.autoread = 1
" Auto write default session file on exit if > 0
let g:wheel_config.storage.session.autowrite = 1
" Number of backups for wheel & session files
let g:wheel_config.storage.backups = 5
" ---- Maximum number of elements in history
let g:wheel_config.maxim.history = 400
" ---- Maximum number of elements in input history
let g:wheel_config.maxim.input = 200
" ---- Maximum number of elements in mru
let g:wheel_config.maxim.mru = 300
" ---- Maximum number of elements in yank ring
let g:wheel_config.maxim.default_yanks = 700
let g:wheel_config.maxim.other_yanks = 100
" ---- Maximum lines of yank to add in yank ring
let g:wheel_config.maxim.yank_lines = 30
" ---- Maximum size of yank to add in yank ring
let g:wheel_config.maxim.yank_size = 3000
" ---- Maximum size of layer ring
let g:wheel_config.maxim.layers = 10
" ---- Maximum number of tabs in layouts
let g:wheel_config.maxim.tabs = 12
" ---- Maximum number of horizontal splits
let g:wheel_config.maxim.horizontal = 3
" ---- Maximum number of vertical splits
let g:wheel_config.maxim.vertical = 4
" ---- Completion
let g:wheel_config.completion.vocalize = 1
let g:wheel_config.completion.wordize = 1
let g:wheel_config.completion.fuzzy = 0
let g:wheel_config.completion.scores = 1
" ---- Frecency
let g:wheel_config.frecency.reward = 120
let g:wheel_config.frecency.penalty = 1
" ---- Mandala & leaf status in statusline ?
let g:wheel_config.display.statusline = 1
" ---- Wheel dedibuf message : one-line or multi-line
let g:wheel_config.display.dedibuf_msg = 'one-line'
" ---- Filter prompt in dedicated buffers
"let g:wheel_config.display.prompt = 'wheel $ '
"let g:wheel_config.display.prompt_writable = 'wheel # '
" ---- Selection marker in dedicated buffers
"let g:wheel_config.display.selection = '-> '
" ---- Signs
let g:wheel_config.display.sign.switch = 1
" ---- Signs at wheel locations
"let g:wheel_config.display.sign.settings = { 'text' : '@' }
" ---- Signs after using Wheel interface to native navigation (buffer, marker, jump, change, tag, ...)
"let g:wheel_config.display.sign.native_settings = { 'text' : '*' }
let g:wheel_config.debug = 0
endif
augroup wheel
" Clear the group
autocmd!
" On vim enter, for autoreading
autocmd VimEnter * call wheel#void#init()
" On vim leave, for autowriting
autocmd VimLeave * call wheel#void#exit()
" Update location line & col before leaving a window
autocmd BufLeave * call wheel#vortex#update()
" For the generalized alternate window command, for all windows in all tabs
autocmd BufLeave * call wheel#caduceus#update_window()
" Executed before jumping to a location
autocmd User WheelBeforeJump call wheel#vortex#update()
" Executed before organizing the wheel
autocmd User WheelBeforeOrganize call wheel#vortex#update()
" Executed before writing the wheel
autocmd User WheelBeforeWrite call wheel#vortex#update()
" Executed after jumping to a location
"autocmd User WheelAfterJump norm zMzx
" For current wheel location to auto follow window changes
autocmd WinEnter * call wheel#projection#follow()
" For current wheel location to follow on editing, buffer loading
"autocmd BufRead * call wheel#projection#follow()
" For current wheel location to follow on entering buffer
"autocmd BufEnter * call wheel#projection#follow()
" Executed after using Wheel interface to a native jump (buffer, marker, jump, change, tag, ...)
"autocmd User WheelAfterNative call wheel#projection#follow()
" Add current non-wheel file to MRU files
autocmd BufRead * call wheel#attic#record()
" To record your yanks in the yank ring
autocmd TextYankPost * call wheel#codex#add()
augroup END
The :Wheel
meta-command gives you access to almost all the plugin
features :
:Wheel subcommand
Completion is available for subcommands. For further details, see the meta-command wiki page.
I suggest you map it to a convenient key. Example :
nnoremap <space>w :Wheel<space>
For a thorough discussion on bindings, see the bindings page in the wiki.
Below are some bindings that you may find useful. They are included in the level 10 mappings :
let nmap = 'nmap <silent>'
let vmap = 'vmap <silent>'
" Menus
exe nmap '<m-m> <plug>(wheel-menu-main)'
exe nmap '<m-=> <plug>(wheel-menu-meta)'
" Sync
exe nmap '<m-i> <plug>(wheel-info)'
exe nmap '<m-$> <plug>(wheel-sync-up)'
exe nmap '<c-$> <plug>(wheel-sync-down)'
" ---- navigate in the wheel
" -- next / previous
exe nmap '<m-pageup> <plug>(wheel-previous-location)'
exe nmap '<m-pagedown> <plug>(wheel-next-location)'
exe nmap '<c-pageup> <plug>(wheel-previous-circle)'
exe nmap '<c-pagedown> <plug>(wheel-next-circle)'
exe nmap '<s-pageup> <plug>(wheel-previous-torus)'
exe nmap '<s-pagedown> <plug>(wheel-next-torus)'
" -- switch
exe nmap '<m-cr> <plug>(wheel-prompt-location)'
exe nmap '<c-cr> <plug>(wheel-prompt-circle)'
exe nmap '<s-cr> <plug>(wheel-prompt-torus)'
exe nmap '<m-space> <plug>(wheel-dedibuf-location)'
exe nmap '<c-space> <plug>(wheel-dedibuf-circle)'
exe nmap '<s-space> <plug>(wheel-dedibuf-torus)'
" -- index
exe nmap '<m-x> <plug>(wheel-prompt-index)'
exe nmap '<m-s-x> <plug>(wheel-dedibuf-index)'
exe nmap '<m-c-x> <plug>(wheel-dedibuf-index-tree)'
" -- history
exe nmap '<m-home> <plug>(wheel-history-newer)'
exe nmap '<m-end> <plug>(wheel-history-older)'
exe nmap '<c-home> <plug>(wheel-history-newer-in-circle)'
exe nmap '<c-end> <plug>(wheel-history-older-in-circle)'
exe nmap '<s-home> <plug>(wheel-history-newer-in-torus)'
exe nmap '<s-end> <plug>(wheel-history-older-in-torus)'
exe nmap '<m-h> <plug>(wheel-prompt-history)'
exe nmap '<m-c-h> <plug>(wheel-dedibuf-history)'
" -- alternate
exe nmap '<c-^> <plug>(wheel-alternate-anywhere)'
exe nmap '<m-^> <plug>(wheel-alternate-same-circle)'
exe nmap '<m-c-^> <plug>(wheel-alternate-same-torus-other-circle)'
" ---- navigate using Wheel interface to vim native tools
" -- buffers
exe nmap '<m-b> <plug>(wheel-prompt-buffer)'
exe nmap '<m-c-b> <plug>(wheel-dedibuf-buffer)'
exe nmap '<m-s-b> <plug>(wheel-dedibuf-buffer-all)'
" -- tabs & windows : visible buffers
exe nmap '<m-v> <plug>(wheel-prompt-tabwin)'
exe nmap '<m-c-v> <plug>(wheel-dedibuf-tabwin-tree)'
exe nmap '<m-s-v> <plug>(wheel-dedibuf-tabwin)'
" -- (neo)vim lists
exe nmap "<m-'> <plug>(wheel-prompt-marker)"
exe nmap "<m-k> <plug>(wheel-prompt-marker)"
exe nmap '<m-j> <plug>(wheel-prompt-jump)'
exe nmap '<m-,> <plug>(wheel-prompt-change)'
exe nmap '<m-c> <plug>(wheel-prompt-change)'
exe nmap '<m-t> <plug>(wheel-prompt-tag)'
exe nmap "<m-c-k> <plug>(wheel-dedibuf-markers)"
exe nmap '<m-c-j> <plug>(wheel-dedibuf-jumps)'
exe nmap '<m-;> <plug>(wheel-dedibuf-changes)'
exe nmap '<m-c-t> <plug>(wheel-dedibuf-tags)'
" ---- organize the wheel
exe nmap '<m-insert> <plug>(wheel-prompt-add-here)'
exe nmap '<m-del> <plug>(wheel-prompt-delete-location)'
exe nmap '<m-r> <plug>(wheel-dedibuf-reorganize)'
" ---- organize other things
exe nmap '<m-c-r> <plug>(wheel-dedibuf-reorg-tabwin)'
" ---- refactoring
exe nmap '<m-c-g> <plug>(wheel-dedibuf-grep-edit)'
exe nmap '<m-n> <plug>(wheel-dedibuf-narrow-operator)'
exe vmap '<m-n> <plug>(wheel-dedibuf-narrow)'
exe nmap '<m-c-n> <plug>(wheel-dedibuf-narrow-circle)'
" ---- search
" -- files
exe nmap '<m-f> <plug>(wheel-prompt-find)'
exe nmap '<m-c-f> <plug>(wheel-dedibuf-find)'
exe nmap '<m-c-&> <plug>(wheel-dedibuf-async-find)'
exe nmap '<m-u> <plug>(wheel-prompt-mru)'
exe nmap '<m-c-u> <plug>(wheel-dedibuf-mru)'
exe nmap '<m-l> <plug>(wheel-dedibuf-locate)'
" -- inside files
exe nmap '<m-o> <plug>(wheel-prompt-occur)'
exe nmap '<m-c-o> <plug>(wheel-dedibuf-occur)'
exe nmap '<m-g> <plug>(wheel-dedibuf-grep)'
exe nmap '<m-s-o> <plug>(wheel-prompt-outline)'
exe nmap '<c-s-o> <plug>(wheel-dedibuf-outline)'
" ---- yank ring
exe nmap '<m-y> <plug>(wheel-prompt-yank-plain-linewise-after)'
exe nmap '<m-p> <plug>(wheel-prompt-yank-plain-charwise-after)'
exe nmap '<m-s-y> <plug>(wheel-prompt-yank-plain-linewise-before)'
exe nmap '<m-s-p> <plug>(wheel-prompt-yank-plain-charwise-before)'
exe nmap '<m-c-y> <plug>(wheel-dedibuf-yank-plain)'
exe nmap '<m-c-p> <plug>(wheel-dedibuf-yank-list)'
" ---- undo list
exe nmap '<m-s-u> <plug>(wheel-dedibuf-undo-list)'
" ---- ex or shell command output
exe nmap '<m-!> <plug>(wheel-dedibuf-command)'
exe nmap '<m-&> <plug>(wheel-dedibuf-async)'
" ---- dedicated buffers
exe nmap '<m-tab> <plug>(wheel-mandala-add)'
exe nmap '<m-backspace> <plug>(wheel-mandala-delete)'
exe nmap '<m-left> <plug>(wheel-mandala-backward)'
exe nmap '<m-right> <plug>(wheel-mandala-forward)'
exe nmap '<c-up> <plug>(wheel-mandala-switch)'
" ---- layouts
exe nmap '<m-z> <plug>(wheel-zoom)'
<M-w><space>
to launch the location navigatori
to go to insert mode- enter the pattern you want
- e.g.
\.vim$
if all your vim locations end with.vim
- e.g.
<enter>
to validate the pattern*
to select all the visible (filtered) locationsv
to open all selected locations in vertical splits
More examples are available in the wiki examples page.
Despite abundant testing, some bugs might remain, so be careful.