Vim Inspired Visual Graph Creation
VimMapper is a visual graph editor with vim-like keybindings. It uses a force-directed algorithm to dynamically position nodes. Its goal is to allow a user create a tree of text nodes for any purpose at the speed of thought without manual positioning or moving their hands from the home row of the keyboard.
VimMapper can be installed by going to the releases page and downloading the latest binaries for your operating system.
VimMapper currently supports 64-bit Windows natively and Linux via GTK, GDK, and Cairo. Most Linux distributions with GUIs include these libraries by default.
- Easy text node creation and navigation for rapid creation of related textual information
- Vim-style keybindings for text entry and editing
- Dynamic node placement using a dense force-directed algorithm
- Vim-style keybindings for all node operations
- Quick navigation without resorting to mouse operations
- Copy/paste functionality to easily reorganize whole tree branches
- Search functionality to quickly find and select specific nodes even in dense trees
- Tabbed sheets to organize multiple unrelated trees in a single file and move nodes between them
VimMapper is Open Source Software licensed under the Apache 2.0 Licence. Feel free to modify, build, and redistribute VimMapper. The build environment is currently configured for Windows and Linux. To build VimMapper for MacOS, changes to the source code may need to be made. Full support for MacOS is planned for future releases.
First, install rustup.
Ensure that rustc v1.66.0 is installed:
$ rustup toolchain install 1.66.0For WSL2 cross-compilation or Linux compilation on Debian, install librust-gtk-dev:
$ sudo apt install librust-gtk-devOther packages such as build-essentials may be necessary if a Linux build environment hasn't been configured. For non-Debian Linux distributions, please search the appropriate repositories for the relevant rust GTK dev libraries.
Clone the repository:
$ git clone https://github.com/dougpowers/vim-mapper vim-mapper Compile VimMapper:
$ cd vim-mapper
$ cargo build --releaseCompiled binaries will be located in ./target/release/.
VimMapper presents a simple interface. All new sheets will start with a single tab and a single node named "Root". Subsequent nodes will connect back to this root node.
VimMapper starts in Sheet mode. Keys pressed in this mode will navigate and manipulate whole nodes or node trees. The sheet can be panned by pressing h, j, k, or l. Holding Shift while pressing these will pan by a larger amount. Ctrl-k and Ctrl-j will zoom the sheet in or out.
The current active node is outlined in blue. The current targeted child node is outlined in light red. Press the n key to cycle target clockwise or N to cycle counter-clockwise. Press Enter to activate the targeted node.
Create a child to the active node by pressing O. To create and activate a child as well as switch to Insert mode, press o.
To cut a node and its ancestors, press d. A confirmation dialog will be displayed if more than one node is to be removed. All cut nodes are automatically copied ("yanked") and can be pasted elsewhere if desired using p.
To edit the active node, press c to enter Edit mode. This is analogous to vim's Normal mode. Common vim bindings to navigate and edit text are available in this mode.
When in Edit mode, press i or a to enter Insert mode. Press I or A to enter Insert mode and place the caret at the beginning or end of the text, respectively.
For a full list of keybindings, please see Keybindings.
Though VimMapper is intended to be used via the keyboard, mouse operations are available. Clicking and dragging an empty space will pan the sheet as will scrolling or holding Shift while scrolling. Holding Control and scrolling will zoom the sheet in or out. Right clicking an empty space will open a context menu for creating or pasting external nodes.
On most Windows laptop tackpads, vertical and horizontal scrolling will pan the sheet and pinching will zoom the sheet.
Nodes can be moved by dragging them around the screen. Nodes can be activated by left click and edited by double left click. The text caret can be moved by clicking on the text in Edit mode. Right clicking a node will open a context menu with various options.
When invoked from a terminal, VimMapper can open existing sheets by specifying a valid .vmd file as the first argument.
VimMapper, like Vim, is designed to be used by touch typists without movement of the fingers from the home row of the keyboard. As such, it uses modes to separate functionality and allow the same keys to be used for different functions. The current mode is shown in the bottom-right of the interface.
VimMapper includes the following modes:
- Sheet - The default mode used for navigating between, yanking, and pasting nodes
- Move - Accessed using
`. Allows movement of a single node around the sheet. - Edit - Accessed using
c. Allows navigation through the text of the active node using standard Vim keybinds. Can be exited usingEscorEnter. - Insert - Accessed using
i,I,a, orA. Allows text insertion for nodes. The caret can be moved using the arrow keys. Text can be deleted usingBackspaceorDelete. PressEscorjjto return to Edit mode or pressEnterto return to Sheet mode. - Search - Accessed using
/. Entering a string in this mode will search all the non-active nodes in the current tab for a specific string (case-insensitively). - Select - Accessed by pressing
Enterafter entering a search string in Search mode. PressnorNto cycle the target through search results. PressEnteragain to active the targeted node. - Mark - Accessed by pressing
m. Pressing a printable character after entering this mode will mark any non-root node. PressingSpacewill remove a mark from a node. If a mark is already in use, reusing that mark will remove it from the old node and place it on the new node. - Jump - Accessed by press
'(apostrophe). Pressing a non-numeric printable character after entering this mode will activate a node marked with that character. Pressing0will activate the default root node. Pressing1-9will jump to the root node of the corresponding tree index.
VimMapper supports copying ("yanking" in vim parlance) and pasting of nodes and node trees. A node and all its descendants can be yanked by pressing yy. A single node can be yanked by pressing yi.
Any node deletion operation is also a yank operation. This allows for quickly moving nodes or node trees around the sheet. Be aware of this behavior when performing yank and cut operations consecutively as yanked nodes in the clipboard may be inadvertently overwritten by deleted ones.
Press p to attach a yanked node or node tree to the active node. Press P to paste the node or node tree as a new external tree. Press Ctrl-p to paste the node or node tree into a new tab.
❔FAQ - What about Vim-style registers?
Vim-style registers are planned for future releases. Currently, only a single register is available for nodes.
VimMapper supports tabbed sheets. Press Ctrl-t to create a new tab. Press Ctrl-Tab and Ctrl-Shift-Tab to move between tabs. Press Ctrl-Shift-t to create a new tab with a prompt to enter a tab name. Press Ctrl-r to rename the active tab. Press Ctrl-w to delete a tab. This operation cannot be undone and will remove any nodes in that tab. Use yanks to move any desired nodes to other tabs before deleting a tab.
A non-root node in a linear chain (one that has only two neighbors) can be cut and its neighbors joined by pressing x.
A node can be inserted between the active and target nodes by pressing i.
The root node of a VimMapper sheet will be anchored by default and will not move in relation to any other node. All node trees must have at least 1 anchored node and VimMapper will not allow a cut operation if any of the removed nodes are the sole anchored node in that component. New child nodes are, by default, unanchored. The anchoring state of any node can be toggled by pressing the @ key. A ⚓ badge will appear on the node to indicate that it is anchored.
Nodes can be moved by pressing ` (backtick). This anchors the node and enables Move mode. Press hjkl or HJKL to move the node around the canvas. Pressing @ will unanchor the node and exit Move mode. Pressing Enter will confirm the new position for the node and exit Move mode. Subsequently unanchoring the node will cause it once again to reposition itself relative to its connected and unconnected neighbor nodes.
New root nodes ("externals") can be created with Ctrl-Shift-o. These appear on top of the root node in Mode mode. Move them using hjkl or HJKL and press Enter to place them. These nodes constitute a new "tree" that is unconnected and therefore not attracted to the default tree (though they will still repel each other). New root nodes will be assigned a numerical mark from 1 to 9, corresponding to their respective tree indices. These indices will shift to remain contiguous if an external is removed. External roots past index 9 are supported but will not be marked and can only be selected via the mouse or by searching. If more than 10 trees are desired, consider creating a new tab.
VimMapper allows the user to "mark" a non-root node with any non-numeric printable character. Press m to enter Mark mode then press any printable character to mark the active node. Marking any non-root node with Space will clear its mark. Press ' (apostrophe) to enter Jump mode then press any non-numeric printable character to activate the node marked with that character.
Root nodes will have an unchangeable numeric mark corresponding to the index of the component. This mark may change if external nodes are removed.
Note the red m or ' indicator in the bottom-left of the screen denoting that the user is now in Mark or Jump mode. Press Esc to exit Mark or Jump mode.
Nodes can be navigated to via a case-insensitive text search. Press / to enter Search mode and type a string to begin filtering through all non-active nodes. Results will be displayed in a pane on the left of the interface. Nodes that do not match will be grayed out on the sheet as the string is entered. Press Enter to enter Select mode and begin search result navigation. Press n or N to cycle through matched nodes and press Enter to select the desired match. If only one node matches the search string, pressing Enter will skip Select mode activate it directly.
VimMapper nodes have a default "mass" which affects how much other nodes are repelled by it. Press the + or - keys to increment or decrement this mass for the active node. Press the = key to return the node to its default mass. A + or - badge will appear on the node if its mass is above or below the default.
VimMapper supports dark mode. It will attempt to detect the OS theme on first start-up. If this fails, press Alt+F10 to toggle between dark mode and light mode. This preference will be saved.
The "File" menu can be hidden by pressing Alt-F11. This may make dark mode more complete and provide a cleaner interface in a maximized screen. This preference is saved.
VimMapper stores its configuration in JSON format at ~/AppData/Roaming/vim-mapper/vmconfig on Windows and ~/.config/vim-mapper/vmconfig on Linux. This file can be edited manually to change color values but this is only recommended for advanced users. New versions of VimMapper may not persist these custom changes and malformed configurations may cause unintended behavior or crashes.
| Key Combination | Mode | Description |
|---|---|---|
| Ctrl+n | Any | Create new sheet, discarding the current sheet |
| Ctrl+o | Any | Open an existing sheet, discarding the current sheet |
| Ctrl+s | Any | Save sheet |
| Ctrl+Shift+s | Any | Open a dialog to save sheet to specific file |
| Alt+F4 | Any | Close VimMapper (displays a confirmation dialog if the sheet has unsaved changes) |
| Ctrl+t | Sheet | Create a new tab |
| Ctrl+Shift+t | Sheet | Create and name a new tab |
| Ctrl+Tab | Sheet | Select the next tab |
| Ctrl+Shift+Tab | Sheet | Select the previous tab |
| Ctrl+r | Sheet | Rename the active tab |
| Ctrl+w | Sheet | Delete the active tab |
| Enter | Sheet | Set targeted child node as active |
| n | Sheet | Cycle clockwise target through child nodes |
| N | Sheet | Cycle counter-clockwise through child nodes |
| j / J | Sheet | Pan the viewport down by a little / a lot |
| k / K | Sheet | Pan the viewport up by a little / a lot |
| h / H | Sheet | Pan the viewport left by a little / a lot |
| l / L | Sheet | Pan the viewport right by a little / a lot |
| Ctrl+j | Sheet | Zoom the viewport out |
| Ctrl+k | Sheet | Zoom the viewport in |
| c | Sheet | Enter Edit mode |
| I | Sheet | Enter Insert mode, placing the caret at the beginning of the text |
| A | Sheet | Enter Insert mode, placing the caret at the end of the text |
| o | Sheet | Create new child node, set as active, and enter Insert mode |
| O | Sheet | Create new child node |
| Ctrl+Shift+o | Sheet | Create a new external root node and enter Move mode |
| i | Sheet | Insert a new node between the active and target nodes |
| d | Sheet | Cut node and any children radiating away from root (displays confirmation dialog if more than one node is to be removed) |
| x | Sheet | Cut a node with only two neighbors and join them together |
| yy | Sheet | Yank a node tree |
| yi | Sheet | Yank a single node |
| p | Sheet | Attached a yanked node or node tree to the active node |
| P | Sheet | Paste a yanked node or node tree as a new external tree |
| Ctrl+p | Sheet | Paste a yanked node or node tree as a new tab |
| gg | Sheet | Center viewport on the active node |
| G | Sheet | Center viewport on the default root node |
| / | Sheet | Enter Search mode |
| Enter | Search | Enter Select mode |
| Esc | Search | Cancel Search mode and return to Sheet mode |
| n | Select | Cycle forward through search results |
| N | Select | Cycle backward through search results |
| Enter | Select | Activate selected search result |
| Esc | Select | Cancel Select mode and return to Sheet mode |
| ` | Sheet | Enter Move mode for the active node and anchor it |
| j / J | Move | Move the node down by a little / a lot |
| k / K | Move | Move the node up by a little / a lot |
| h / H | Move | Move the node left by a little / a lot |
| l / L | Move | Move the node right by a little / a lot |
| @ | Move | Cancel Move mode and unanchor node |
| + | Sheet | Increase node mass |
| - | Sheet | Decrease node mass |
| = | Sheet | Reset node mass |
| @ | Sheet | Anchor the active node |
| m<char> | Sheet | Mark the active node with <char> |
| m<Space> | Sheet | Clear the mark on the active node |
| '<char> | Sheet | Jump to the node marked with <char> |
| Alt+F10 | Sheet, Start | Toggle between dark and light mode |
| Alt+F11 | Sheet, Start | Hide app menu |
| Key Combination | Mode | Description |
|---|---|---|
| Enter / Esc | Edit | Exit Edit mode and return to Sheet mode |
| i | Edit | Enter Insert mode |
| a | Edit | Enter Insert mode, advancing the carat one character to the right |
| I | Edit | Enter Insert mode, placing the carat at the beginning of the text |
| A | Edit | Enter Insert mode, placing the carat at the end of the text |
| s | Edit | Remove the character under the carat and enter Insert mode |
| u | Edit | Undoes the last change to the the text |
| Ctrl+r | Edit | If no changes have been made since the last undo, reverts the undo |
| l / Right Arrow | Edit (Movement) | Advance the carat one character to the right |
| h / Left Arrow | Edit (Movement) | Advance the carat one character to the left |
| w | Edit (Movement) | Advance the carat to the beginning of the next word |
| e | Edit (Movement) | Advance the carat to the end of the next word |
| ^ | Edit (Movement) | Move the carat to the beginning of the text |
| $ | Edit (Movement) | Move the carat to the end of the text |
| t<char> | Edit (Movement) | Move the carat to the next occurrence of <char> |
| f<char> | Edit (Movement) | Move the carat past the next occurrence of <char> |
| r<char> | Edit | Replace the character under the carat with <char> |
| D | Edit | Delete text from the carat position to the end of the line |
| d<movement> | Edit | Delete text from the current carat position to <movement> |
| di<delimiter> | Edit | Delete text under the carat between two delimiters |
| diw | Edit | Delete the word underneath the carat and one leading or trailing whitespace character |
| C | Edit | Delete text from the carat position to the end of the line and enter Insert mode |
| c<movement> | Edit | Delete text from the current carat position to <movement> and enter Insert mode |
| ciw | Edit | Delete the word underneath the carat and one leading or trailing whitespace character and enter Insert mode |
| ci<delimiter> | Edit | Delete text under the carat between two delimiters and enter Insert mode |
| S | Edit | Delete all text and enter Insert mode |
| Esc / jj | Insert | Exit Insert mode and enter Edit mode |
| Enter | Insert | Exit Insert mode and enter Sheet mode |
VimMapper recognizes " ... ", ' ... ', [ ... ], ( ... ), < ... >, and { ... } as valid delimiters.
❔FAQ - Where is my favorite vim command?
VimMapper is not intended for extensive text entry and therefore only emulates a subset of vim functionality. Currently, support for more movements and well as numerical counts, sentence objects, visual mode, and other operations is planned for future releases. However, fully emulating vim or even vi is outside the scope of the VimMapper project.
❔FAQ - What about text registers?
Text registers are planned for a future release.
- VimMapper doesn't support Visual mode. This functionality is planned for a future release.
- As in vim,
dwandcware the same operation asdeandce, respectively. - VimMapper currently uses the unicode-segmentation crate to determine words and whitespace. This behavior will differ in some ways to vim's words and WORDS.
- While it does support Unicode, VimMapper does not support non-LTR languages. Likewise, it does not support some IMEs.
VimMapper uses a forked version of the force-graph-rs crate by @tobmansf to position and manage nodes. The vm-force-graph-rs crate is not currently planned to be published on crates.io.
Doug Powers - dougpowers@gmail.com - LinkedIn
Project Link: https://github.com/dougpowers/vim-mapper
