VIMonad is a fork of XMonad that manages windows in a modal way like that in VIM. Some of the features include
- Split layout: a 3-level nested layout that divides each workspace into a grid of rectangular areas each containing unlimited number of tabs (like Notion, with added advantage of dynamic resizing)
- Motion: navigating around windows, split panes, workspaces with countable motion keys
- Command: delete, yank windows with register + motion support
- Macro: record and play macros
- Visual: advanced visual mode support that allows selection of windows via motion keys
- Prompt: execute common actions via a variety of prompts that are capable of
- dynamically displaying the response of an action (e.g.,
find
andgrep
) as complete-able options (for selecting files) - previewing files in the completion window
- providing application-specific completion window (e.g,
taskwarrior
)
- dynamically displaying the response of an action (e.g.,
VIMonad is built upon XMonad and it borrows a lot of great modules already existing in its Contrib library. I'd like to thank all the people who have made XMonad a fantastic window manager in the first place.
- cabal: for installing packages
- wmctrl: for activating windows from the command line
- rpd: if you'd like to use the radio service in VIMonad
- rpc: if you'd like to use the radio service in VIMonad
- taskwarrior: task management from the prompt
- vimb: light-weight browser with its histories and bookmarks accessible from the prompt
- xdotool: for playing back text macros
- ranger: for deciding the program to launch files from Dynamic prompt
- feh: for changing wallpapers via Wallpaper prompt
- rss-image-download: for subscribing to rss image feeds on the fly via Wallpaper prompt
- clone the repo somewhere
- move all the scripts under bin to a directory included in
$PATH
- copy files under .xmonad to
~/.xmonad
(note your old xmonad configuration will be overriden)- .xmonad/xmonad.hs serves as a template config file; modify it if you'd like
cd
into xmonad;cabal install
cd
into XMonadContrib;cabal install
(Zoom in to see how the layout is represented on the statusbar)
workspace(s)
└── row(s)
└── line(s)
└── tab(s)
- workspaces: the conventional sense of workspace as used in most window managers
- the workspace labels are the symbols before
:<name>
- the system starts up with only one workspace
`
(the temporary workspace) - new workspaces are added dynamically via prompt; the aim is to organize workspaces by context (each workspace for one task)
- each workspace has its own current directory; this can be changed by
cd
in the DynamicPrompt
- the workspace labels are the symbols before
- rows: vertical columns each spanning the entire the height of the screen
- each row has a label attached to it - the first row in the workspace gets
1
, the second gets2
, so on and so forth; the entire stream of symbols used as labels can be found in the source - row labels are displayed in the statusbar (before each group of square brackets)
- each row has a label attached to it - the first row in the workspace gets
- lines: horizontal rectangular regions contained in rows, each holding an arbitrary number of tabs
- the lines don't have labels; however, you'll be able to reference lines using
[num]G
as in vim
- the lines don't have labels; however, you'll be able to reference lines using
- tabs: as the ones seen in modern browsers; each tab is a window
- the tab labels are shown near the leftmost edge of each tab
- each tab might have a different colorscheme according to the definition of the task group it belongs to e.g., in the image above, vim windows have a brownish tab color, whereas vimb windows have a green one
- tabs are only shown for lines with more than one window (due to a bug in the tabbed layout, currently it's not possible to show tabs at all time)
- minimized: a special case is that windows can be minimized in each workspace
M-<
: shrink current rowM->
: expand current rowM-\
: restore default size for the current rowM--
: shrink current lineM-+
: expand current lineM-=
: restore default size for the current line
A major concept of VIMonad is task group. A task group defines a groups of windows that share the same or similar characteristics e.g., vim terminal windows, browser windows, etc.
Each task group has
- a
filterKey
to be referenced in motions. This can be a single key or a sequence of keys. - a construction function that constructs a new instance of the group.
- a list of
manageHooks
that define the window styles that you can cycle usingM-t
andM-S-t
Checkout xmonad.hs for how to define task groups.
Checkout Taskgroup.hs for source about task groups.
Registers server two purposes in VIMonad:
- they act as marks attached to windows, by referencing which we can cycle within a specific group of windows
- they also act as registers like those in vim - we can delete, move, yank windows into registers
- when we delete or move windows into registers, they are actually minimized as mentioned before
- when we yank windows into registers, the windows stay in their original places
Attached registers are displayed for each window within the sqaure brackets of its tab label as '<reg>['<another reg>]
.
Insertion of windows/workspaces emulate the relevant aspects from vim
a
: insert after the current window/workspacei
: insert before the current window/workspaceA
: insert at the end of the line/workspacesI
: insert at the beginning of the line/workspaces
When a window is inserted, you can choose to keep the focus on the old window without switching to the new window
M-C-a
toggles this behavior- for some commands, an additional
g
temporarily toggles this behavior as well
Some points to note:
- to perform any key sequence mentioned below, you need to press the first character with the modMask defined by you, e.g., to trigger
g2w
(move down 2 windows), pressM-g
followed by2
andw
.- exceptions are keystrokes with a dash inside, e.g.,
M1-<tab>
, for which you press the exact key combo
- exceptions are keystrokes with a dash inside, e.g.,
{}
means the key inside is not needed when the motion is passed as an argument to a command[]
means the key inside is optional<num>
can be any number from 2 to 9<tab>
means any tab label in the current line<row>
means any row label in the current workspace<workspace>
means any workspace label in the current X session<reg>
means a single alpha-numeric character register/
: a special register that pops out a prompt for you to enter the exact arbitrary name for the register"
: the unnamed register used by move and paste- as in vim, the unnamed register pushes content through registers
1
to9
- as in vim, the unnamed register pushes content through registers
*
: always references the minimized windows that aren't present in any other register'
: always references the last visited window- an uppercase letter register appends the content to its lowercase letter register; the lowercase register replaces its original content (like in vim)
- e.g.,
'Adw
deletes the window and appends it to registera
, whereas'adw
deletes the window and it then replaces the original content in registera
- e.g.,
<group>
means thefilterKey
of a task group in the current workspace- a special group is
c
, which is the task group of the current window - so for example
gc
cycles to the next window for the current task group,dgc
deletes all the windows in the current task group
- a special group is
<macro>
means a single- or multi- character macro register; the characters can be any that can be typed on the keyboard, except/
: a special register that pops out a prompt for you to enter the exact name for the macro
f<tab>
: moves to the tab with label<tab>
<M1-<tab>>
: leaps to the given tab- the difference between leap and
f
is that leap references the tab without travelling along any window - when used as selection leap only selects the given specific tab
- when used for navigation under normal mode, leap allows you to leap to the last visited window/row/workspace if the argument selects the currently focused window/row/workspace
- the difference between leap and
f<C-<row>>
: moves to the given row<C-<row>>
: leaps to the given row- when used as selection only selects the given specific row
f<M-<workspace>>
: moves to the given workspaceM-<workspace>
: leaps to the given workspace- when used as selection only selects the given specific workspace
[{g}<num>]b
: to the<num>
'th windows/tabs back[{g}<num>]w
: to the<num>
'th windows/tabs forward{g}0
: to the beginning of the line{g}$
: to the end of the line{g}<C-0>
: to the first row{g}<C-$>
: to the last row{g}<M-0>
: to the first workspace{g}<M-$>
: to the last workspacegg
: go to the top line- when used as selection this performs line-wise selection i.e. from the current line to the first line
{g}G
: go to the bottom line- when used as selection this performs line-wise selection
{g}<num>G
: go to the<num>
'th line- when used as selection this performs line-wise selection
g<group>
: cycle to the next window in<group>
- when used as selection this selects all windows in that group in the current workspace
G<group>
: cycle to the previous window in<group>
{g}'<reg>
: cycle to the next window in the<reg>
- when used as selection this selects all windows in that register
[{g}<num>]k
: to the<num>
'th line up- when used as selection this performs line-wise selection
[{g}<num>]j
: to the<num>
'th line down- when used as selection this performs line-wise selection
[{g}<num>]h
: to the<num>
'th row back- when used as selection this performs row-wise selection
[{g}<num>]l
: to the<num>
'th row forward- when used as selection this performs row-wise selection
[{g}<num>][
: to the<num>
'th workspace back- when used as selection this performs workspace-wise selection
[{g}<num>]]
: to the<num>
'th workspace forward- when used as selection this performs workspace-wise selection
;
: repeat the lastg<group>
,G<group>
,'<tag>
search motion,
: reverse the lastg<group>
,G<group>
,'<tag>
search motion
M-o
: move to the previous window in the jumplistM-i
: move to the next window in the jumplist
Objects are used with commands, where the same command is applied to all the windows contained in the object.
[<num>]<command>
: apply the command to<num>
of lines starting from the current line<command>
means the same letter as the command, e.g.,dd
deletes the current line
[<num>]r
: apply the command to<num>
of rows starting from the current one[<num>]s
: apply the command to<num>
of workspaces starting from the current one
['<reg>]d<motion/object>
- if
'<reg>
is given, minimize the windows selected by the<motion/object>
and then attach them to register<reg>
- else delete the windows selected by
<motion/object>
- note: when
<motion/object>
is workspace-wise, and the range includes the temporary workspace,`
, the windows inside`
will be deleted but the temporary workspace itself will always persist
['<reg>]m<motion/object>
Same as delete, except
- when
'<reg>
is not given, minimize the windows into"
register- push windows in registers
"12345678
one step forward to123456789
and then forgetting the windows in register9
- the 'forgotten' windows are not deleted, they are just not referenced by any register anymore (except
*
)
- push windows in registers
'<reg>y<motion/object>
yank the windows selected by <motion/object>
into register <reg>
- workspace-wise
<motion/object>
are not supported
['<reg>][g]p
paste the windows in register <reg>
- if
g
is supplied, temporarily alter the current insertion order - if
<reg>
is not supplied, paste windows in the unnamed register"
['<reg>]c<motion/object><group>
delete the windows selected by <motion/object>
into <reg>
and replace them by a new window constructed for group <group>
- here a special group is
/
, which brings up the DynamicPrompt to launch arbitrary windows
['<reg>]c[<num>][g]<insert position><group>
delete the visually selected windows into <reg>
(only applicable within active visual mode), construct <num>
of new window(s) for group <group>
and insert it/them at the position specified by <insert position>
, which can be:
-
a
: after the current window -
i
: before the current window (the default behavior of XMonad) -
A
: at the end of the line -
I
: at the beginning of the line -
C-j
: as a new line down -
C-k
: as a new line up -
C-h
: as a new row on the left -
C-l
: as a new row on the right -
similar to change, a special group is
/
, which brings up the DynamicPrompt to launch arbitrary windows -
when
g
is supplied, temporarily alter the current insertion order
u<motion/object>
remove the association between the windows selected by <motion/object>
with any registers
.
repeat the last command
Window style is a convenient way to configure the commonly used positions and dimensions for float windows.
For each type of window (as defined by the task group), you have the opportunity to define the list of float styles.
Note you would almost certainly want to include doSink
in the list so that one of the styles is sinking the window back into the tiles.
M-t
: cycle forward in the style list for the current windowM-S-t
: cycle backward in the style list for the current window
Macros are by default stored under ~/.macros
. There are two types of macros
- a saved list of key sequences in VIMonad; these can be recorded directly in VIMonad
- an arbitrary piece of text; when triggered it is typed into the focused window via
xdotool
, like a snippet
q<macro>
: start recording the ensuing key sequences into<macro>
- when recording the statusbar will show a
●
followed by the name of the macro
- when recording the statusbar will show a
q<Esc>
: stop recordinga<macro>
: play the macro stored in<macro>
Similar to vim, VIMonad allows you to visually select windows. There is, however, an added subtlety about passive/active visual.
Passive visual is indicated by a small triangle on the right edge of the selected tab. It stays on unless M-<Esc>
is pressed.
Passive visual is useful for chaining commands - you can for example, move the selected windows into the left adjacent row and continue moving them up/down different lines.
Active visual is indicated by a small square on the right edge of the selected tab. This is more like the visual mode in vim.
You can enter the active visual mode by
M-v
: visually select the current window and enter window-wise selection modeM-S-v
: visually select the current line and enter line-wise selection modeM-C-v
: visually select the current row and enter row-wise selection mode
In the active visual mode you can perform any motion to toggle the selection of windows in the respective range.
To quit the active visual mode, press M-<Esc>
; this commits the selected windows to passive visual (press M-<Esc>
again to remove passive visual).
These commands operate on
- when inside active visual mode, any windows currently selected
- or window(s) with passive visual in the current line, if any
- or the currently focused window
['<reg>]x
: delete counterpart for selection['<reg>]X
: same as['<reg>]x
, but focuses the previous window after deleting the selected ones['<reg>]C-x
: move counterpart for selection['<reg>]C-S-x
: same as['<reg>]C-x
, but focuses the previous window after moving the selected onesy<reg>
: yank counterpart for selectionM-S-u
: unregister counterpart for selectionM-S-j
: move the selected windows down a lineM-S-k
: move the selected windows up a lineM-S-h
: move the selected windows left a rowM-S-l
: move the selected windows right a rowM-C-j
: move the selected windows to a new line downM-C-k
: move the selected windows to a new line upM-C-h
: move the selected windows to a new row leftM-C-l
: move the selected windows to a new row right[g<num>]W
: swap the selected windows<num>
of times down the line- when the selected windows are not adjacent to each other, merge them together at the location of the one that is nearest to the end of the line
[g<num>]B
: swap the selected window<num>
of times up the line- when the selected windows are not adjacent to each other, merge them together at the location of the one that is nearest to the beginning of the line
[g<num>]}
: shift the selected windows to the workspace<num>
of distance down[g<num>]{
: shift the selected windows to the workspace<num>
of distance up<M1-S-<tab>>
: insert the selected windows at<tab>
<C-S-<row>>
: shift the selected windows to<row>
<M-S-<workspace>>
: shift the selected windows to<workspace>
<M-S-6>
: shift the selected windows to the last visited workspace
[g<num>]<S-C-j>
: swap the current line<num>
of times down[g<num>]<S-C-k>
: swap the current line<num>
of times up[g<num>]<S-C-h>
: swap the current row<num>
of times left[g<num>]<S-C-l>
: swap the current row<num>
of times right[g<num>]<C-]>
: swap the current workspace<num>
of times down[g<num>]<C-[>
: swap the current workspace<num>
of times up<M-6>
: switch to the last visited workspace<M-C-6>
: swap the current workspace with the last visited workspaceM-<Space>
: toggleFull
layout for the current row (maximize the focused window in the current row)M-S-<Space>
: toggleFull
layout for the current workspace (maximize the current row in the workspace)
As mentioned before, each workspace in VIMonad is dynamically allocated, and linked to a directory when being created. This is achieved via the workspace prompt - the prompt allows you to search in a tag database composed of directories with their names as tags (by default under ~/DB
), and select/create the directory for initializing the workspace.
For example, say I want to create a new workspace after the current one for research on VIMonad
, I can
- press
M-s a
(mnemonic: space create after), and enter in the promptVIMonad
- if this tag already exists, I can see the directory in the completion window and choose it to have a new workspace rooted on that
- if it doesn't, I can instead input something like
indie/VIMonad
to create a new tag insideindie
and root the new workspace in that new directory - on the other hand I can also choose to not select any tag and just stick with
VIMonad
in the prompt; this gives me a new workspace rooted on home directory - for 2 and 3 the new workspace will have an abbreviated version of the tag name as its workspace name; for 4 it will be whatever text you've put into the prompt
The full syntax for the workspace prompt is:
s[<num>]<space action>
where <space action>
can be
a
: create workspace after the current one (or switch to an existing workspace)<M-a>
: clone the current workspace after itself- clone means copying the name and directory of a workspace
i
: create workspace before the current one (or switch to an existing workspace)<M-i>
: clone the current workspace before itselfA
: create workspace at the end of the workspace list (or switch to an existing workspace)<M-A>
: clone the current workspace at the end of the workspace listI
: create workspace at the beginning of the workspace list (or switch to an existing workspace)<M-I>
: clone the current workspace at the beginning of the workspace listC
: change the name of the current directory
Dynamic prompt is an integral part of VIMonad as it handles almost all program-related aspects of the system. Instead of being a simple program launcher like dmenu, dynamic prompt is more like a full-fledged shell environment.
Dynamic prompt is activated via M-r
.
You can
-
execute arbitrary shell commands, with full shell completion support
- if the program name is eliminated, it defaults to opening/launching the file using
rifle
fromranger
- when
<Enter>
is pressed, the prompt will run the command on the prompt, block user input and eventually show the output of the command via completion - when
C-<Enter>
is pressed, the prompt will silently run the command on the prompt in the background- this can also be achieved by prepending your command with the keyword
sil
, e.g.,sil chmod +x bin/binfile
- this can also be achieved by prepending your command with the keyword
- if the program name is eliminated, it defaults to opening/launching the file using
-
preview files
-
search files dynamically with search widgets
l
(locate): search for files recursively in a given directory (or the current one, if not specified) usingfind
l [directory/to/search] KEYWORD KEYWORD ...
- press
<Tab>
andS-<Tab>
to select the files/directories
t
(tag): search for a directory in the tag database using findg
(grep): list all files containing the given words inside a given directory (or the current one, if not specified)g [directory/to/search] KEYWORD KEYWORD ...
- press
<Tab>
andS-<Tab>
to select the files
h
(history): search for files launched in the past
-
enter application-specific completion environment
-
execute shortcut commands, e.g.
-
input Chinese words
- while inside the prompt, enter pinyin preceded by backslash e.g.,
\pinyin
- the prompt will show Chinese word candidates (of best match) for the pinyin immediately after the backslash
- use
<Tab>
orS-<Tab>
to select the word you'd like - if the prompt only completes part of the pinyin, e.g. only showing
拼
for\pinyin
, after selecting拼
the prompt will become拼yin
with cursor positioned after拼
- now just type backslash again to trigger completion for the remaining part
- while inside the prompt, enter pinyin preceded by backslash e.g.,
Search windows by register/title/workspace name in the current X session
M-/
: search windows and navigate to the selected oneM-?
: search windows and bring the selected one to the current workspace
The wallpaper prompt enables you to switch wallpapers on the fly; it also has a wallhaven backend as well as a rss downloader backend (for flickr) for you to search and download wallpapers by tag/description.
Wallpaper prompt is triggered by M-z
. When the prompt is active, all the windows will be painted semi-transparent so that you can look through them to see the glory of the wallpapers.
I've put a lot of effort into VIMonad over the years, but there is no guarantee that it will work on your system. More specifically:
- I haven't got a chance to test it on any computer other than my own personal one
- VIMonad might cause slowdown (as compared to the vanilla XMonad)
If you intend to try VIMonad and have encountered any problem, you're welcome to use the issue tracker.