moving-around-windows.mov
- Requirements
- Installing
- Configuration
- Autocommands
- Public API
- Contributing
- FAQ
- Showcase
- Similar projects
- Neovim 0.9.0+
Plug 'MisanthropicBit/winmove.nvim'
use 'MisanthropicBit/winmove.nvim'
If you are content with the defaults that are shown below, you don't need to
call the configure
function. No default keymaps are set other than those
active during modes.
Note
For mode highlight groups that only have a foreground color, winmove
will
automatically use the foreground color as a background color for the given
mode so you do not have to create a custom highlight group yourself.
require('winmove').configure({
keymaps = {
help = "?", -- Open floating window with help for the current mode
help_close = "q", -- Close the floating help window
quit = "q", -- Quit current mode
toggle_mode = "<tab>", -- Toggle between modes when in a mode
},
modes = {
move = {
highlight = "Visual", -- Highlight group for move mode
at_edge = {
horizontal = at_edge.AtEdge.None, -- Behaviour at horizontal edges
vertical = at_edge.AtEdge.None, -- Behaviour at vertical edges
},
keymaps = {
left = "h", -- Move window left
down = "j", -- Move window down
up = "k", -- Move window up
right = "l", -- Move window right
far_left = "H", -- Move window far left and maximize it
far_down = "J", -- Move window down and maximize it
far_up = "K", -- Move window up and maximize it
far_right = "L", -- Move window right and maximize it
split_left = "sh", -- Create a split with the window on the left
split_down = "sj", -- Create a split with the window below
split_up = "sk", -- Create a split with the window above
split_right = "sl", -- Create a split with the window on the right
},
},
swap = {
highlight = "Substitute", -- Highlight group for swap mode
at_edge = {
horizontal = at_edge.AtEdge.None, -- Behaviour at horizontal edges
vertical = at_edge.AtEdge.None, -- Behaviour at vertical edges
},
keymaps = {
left = "h", -- Swap left
down = "j", -- Swap down
up = "k", -- Swap up
right = "l", -- Swap right
},
},
resize = {
highlight = "Todo", -- Highlight group for resize mode
default_resize_count = 3, -- Default amount to resize windows
keymaps = {
-- When resizing, the anchor is in the top-left corner of the window by default
left = "h", -- Resize to the left
down = "j", -- Resize down
up = "k", -- Resize up
right = "l", -- Resize to the right
left_botright = "<c-h>", -- Resize left with bottom-right anchor
down_botright = "<c-j>", -- Resize down with bottom-right anchor
up_botright = "<c-k>", -- Resize up with bottom-right anchor
right_botright = "<c-l>", -- Resize right with bottom-right anchor
},
},
},
})
There are three behaviour when moving or swapping towards an edge of the editor:
winmove.AtEdge.None
: Do nothing.winmove.AtEdge.Wrap
: Wrap around to the opposite edge.winmove.AtEdge.MoveToTab
: Move to the previous/next tab if possible.
You can define autocommands that trigger when a mode starts and ends.
vim.api.nvim_create_autocmd("WinmoveModeStart", {
callback = function(event)
vim.print("Started ".. event.data.mode .. " mode")
end,
})
vim.api.nvim_create_autocmd("WinmoveModeEnd", {
callback = function(event)
vim.print("Ended ".. event.data.mode .. " mode")
end,
})
Warning
Consider only the functions below part of the public API. All other functions are subject to change.
Configure winmove
. Also see Configuration.
Get the current version of winmove
.
Check which mode is currently active. Returns "move"
(winmove.Mode.Move
),
"swap"
(winmove.Mode.Swap
), "resize"
(winmove.Mode.Resize
), or nil
.
Start a mode.
---@param mode winmove.Mode
winmove.start_mode(mode)
-- Example:
winmove.start_mode(winmove.Mode.Move)
winmove.start_mode("swap")
winmove.start_mode("move")
winmove.start_mode(winmove.Mode.Resize)
Stop the current mode. Fails if no mode is currently active.
Move a window (does not need to be the current window). See this showcase.
---@param win_id integer
---@param dir winmove.Direction
winmove.move_window(win_id, dir)
-- Example:
winmove.move_window(1000, "k")
Split into a window (does not need to be the current window). See this showcase.
---@param win_id integer
---@param dir winmove.Direction
winmove.split_into(win_id, dir)
-- Example:
winmove.split_into(1000, "l")
Move a window as far as possible in a direction (does not need to be the current window). See this showcase.
---@param win_id integer
---@param dir winmove.Direction
winmove.move_window_far(win_id, dir)
-- Example:
winmove.move_window_far(1000, "h")
Swap a window in a given direction (does not need to be the current window).
---@param win_id integer
---@param dir winmove.Direction
winmove.swap_window_in_direction(win_id, dir)
-- Example:
winmove.swap_window_in_direction(1000, "j")
winmove.swap_window_in_direction(1000, "l")
Swap a window (does not need to be the current window). When called the first time, highlights the selected window for swapping. When called the second time with another window will swap the two selected windows.
---@param win_id integer
---@param dir winmove.Direction
winmove.swap_window(win_id, dir)
-- Example:
winmove.swap_window(1000)
winmove.swap_window(1000)
Resize a window (does not need to be the current window). The window can be resized relative to an anchor in the top-left or bottom-right corner of the window.
Resizing respects the global winwidth
/winminwidth
and
winheight
/winminheight
options respectively, with the largest value taking
priority. If a window being resized would shrink another window's size beyond
the values of those options, the whole row/column of windows are adjusted except
if all windows in the direction of resizing are as small as they can get.
See this showcase.
---@param win_id integer
---@param dir winmove.Direction
---@param count integer
---@param anchor winmove.ResizeAnchor?
winmove.resize_window(win_id, dir, count, anchor)
-- Example:
winmove.resize_window(1000, "j", 3, winmove.ResizeAnchor.TopLeft)
winmove.resize_window(1000, "l", 1, winmove.ResizeAnchor.BottomRight)
See here.
A: There are already a few projects for moving windows but none of them felt intuitive to me so I did the only rational thing a developer would do in this situation and created my own plugin. If any of the others suit your needs then by all means use them.
Important
Moving and swapping windows takes into account the cursor position of the current window relative to the target window in the direction you are moving or swapping.
For example, if your cursor position is closest to the bottom of one window in the target direction, the window will be moved below that window. See this example for a visual explanation.
moving-around-windows.mov
relative-cursor-position.mov
As opposed to moving windows, which will squeeze a window in between other windows, splitting into a window will move it next to a target window.