Skip to content

A neovim plugin that makes it easy to move, swap, and resize windows

License

Notifications You must be signed in to change notification settings

MisanthropicBit/winmove.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


winmove.nvim

Easily move, swap, and resize windows


moving-around-windows.mov

Requirements

  • Neovim 0.9.0+

Installing

Plug 'MisanthropicBit/winmove.nvim'
use 'MisanthropicBit/winmove.nvim'

Configuration

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.

Autocommands

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,
})

Public API

Warning

Consider only the functions below part of the public API. All other functions are subject to change.

winmove.configure

Configure winmove. Also see Configuration.

winmove.version

Get the current version of winmove.

winmove.current_mode

Check which mode is currently active. Returns "move" (winmove.Mode.Move), "swap" (winmove.Mode.Swap), "resize" (winmove.Mode.Resize), or nil.

winmove.start_mode

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)

winmove.stop_mode

Stop the current mode. Fails if no mode is currently active.

winmove.move_window

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")

winmove.split_into

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")

winmove.move_window_far

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")

winmove.swap_window_in_direction

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")

winmove.swap_window

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)

winmove.resize_window

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)

Contributing

See here.

FAQ

Q: Why create this project?

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.

Showcase

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

moving-around-windows.mov

Moving using relative cursor position

relative-cursor-position.mov

Splitting into windows

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.

splitting-into-windows.mov

Swapping windows

swapping-windows.mov
swap-two-windows.mov

Resizing windows

resizing-windows.mov
resizing-and-pushing-windows.mov

Moving as far as possible in a direction

moving-as-far-as-possible-in-a-direction.mov

Move between tabs

moving-between-tabs.mov

Works with asynchronous output

async-output.mov

Similar projects