Basically title... word wrap drove me mad in neovim & vscode-neovim, so I fixed it.
By default, keymaps to move and operate over visual lines (e.g. g$, vg$, yg$) in VSCode-neovim extension are either broken or incomplete. This plugin aims to replicate Neovim word wrap keymap behavior (i.e. vg$ should select to end of visual line) as closely as possible, including macro support and fixes.
Put simply, word wrap keymaps should work the same in VSCode Neovim as they do in Neovim. I believe default Neovim keys should work out of the box for everyone, but as seen in this PR, that's not the case—so I made this plugin.
Trust me — in the VSCode extension, it's not as simple as in Neovim to just set the g0 or g$ keymaps. There are a lot of "hacky" ways to replicate the proper behavior (for example, y0 shouldn't include the character under the cursor, but y$ should). Things break down very quickly if you change just ONE thing. See keymaps-vscode.lua for yourself.
This plugin also improves the word wrap experience in regular Neovim. Motions like gj, gk, and g0 always work as expected with wrap enabled, and macros won't break because of wrap keymaps. If you use word wrap in Neovim or edit many Markdown files, give it a try.
Here's a quick list of features — videos demonstrating them appear below:
- Create mappings that respect visual lines for:
gj,gk,g0,g$,g^,j,k,0,$,^. j/kmotions respect relative line numbers:jalone moves down one visual line, while4jmoves down four relative lines.- Mappings are provided for normal, visual, and operator-pending modes. That means
vg0/v0oryg$/y$apply their respective operations to the visual line. - Additional useful mappings:
I,A,Y,C,D— each applies its operation to the visual line. - One-to-one implementation of text operations: backward operations (e.g.,
yg0/y0) do not include the character under the cursor, while forward operations do. g$and$respect the user'svirtualeditsetting.- The plugin automatically falls back to non-wrap keymaps in visual-block mode, while recording macros, and during macro execution (even with wrap enabled!). This prevents unintended behavior when recording or replaying macros.
- Provides the
:ToggleWrapuser command and a keymap to quickly toggle global word wrap for all open buffers/windows/tabs (yes even in Neovim :D).
macros.mp4
respect_line_numbers.mov
select_end_visual_line.mov
delete_beginning_visual_line.mov
Install the plugin with your favorite plugin manager.
{
"YouSame2/vscode-neovim-fix-word-wrap",
event = "VeryLazy",
opts = {},
}Default config
{
togglewrap_keymap = "<leader>uw",
-- autocmd toggles
enable_recording_enter_autocmd = true,
enable_vscode_sync_autocmd = true,
keymaps = {
n = {
gj = { enabled_vscode = true, keymap = keymaps.n_gj },
gk = { enabled_vscode = true, keymap = keymaps.n_gk },
g0 = { enabled_vscode = true, keymap = keymaps.n_g0 },
["g^"] = { enabled_vscode = true, keymap = keymaps.n_g_caret },
["g$"] = { enabled_vscode = true, keymap = keymaps.n_g_dollar },
I = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_I },
A = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_A },
D = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_D },
C = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_C },
Y = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_Y },
j = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_j },
k = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_k },
["0"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_0 },
["^"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_caret },
["$"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.n_dollar },
},
v = {
gj = { enabled_vscode = true, keymap = keymaps.v_gj },
gk = { enabled_vscode = true, keymap = keymaps.v_gk },
g0 = { enabled_vscode = true, keymap = keymaps.v_g0 },
["g^"] = { enabled_vscode = true, keymap = keymaps.v_g_caret },
["g$"] = { enabled_vscode = true, keymap = keymaps.v_g_dollar },
j = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.v_j },
k = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.v_k },
["0"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.v_0 },
["^"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.v_caret },
["$"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.v_dollar },
},
o = {
g0 = { enabled_vscode = true, keymap = keymaps.o_g0 },
["g^"] = { enabled_vscode = true, keymap = keymaps.o_g_caret },
["g$"] = { enabled_vscode = true, keymap = keymaps.o_g_dollar },
["0"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.o_0 },
["^"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.o_caret },
["$"] = { enabled_vscode = true, enabled_neovim = true, keymap = keymaps.o_dollar },
},
},
}togglewrap_keymap: Keymap to toggle word wrap (default:<leader>uw). Set to""to disable.enable_recording_enter_autocmd: Iftrue, disables wrap and keymaps before entering a macro recording to avoid breaking macro behavior. This is a fix and is recommended to keep enabled.enable_vscode_sync_autocmd: Iftrue, keeps Neovim and VSCode word wrap state in sync automatically. It usesCursorHoldautocmd to check and set wrap settings & keymaps. Unless you constantly change word wrap in vscode i'd recommend disabling this to min/max your performance. Just remember to use the keymap instead.keymaps: Table of keymaps for normal/visual/operator modes. Format matches the default config. Some keymaps are only available in Neovim or VSCode—refer to the default config for details. You can disable a specific key in a specific mode, or provide your own keymap table for the plugin to use.
Use :ToggleWrap to toggle word wrap in both Neovim and VSCode. You can also use the configured keymap (default: <leader>uw).
- VSCode word wrap keymaps don't function in the
:normcommand when used inside of it. No fix is known—if you find one, please open an issue or PR. As a workaround, use:norm!or disable wrap keymaps (:ToggleWrapoff) before running:norm.