Skip to content

Commit

Permalink
Readd resize mode (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
MisanthropicBit authored Nov 20, 2024
1 parent 5f06812 commit 255e949
Show file tree
Hide file tree
Showing 21 changed files with 1,909 additions and 26 deletions.
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div align="center">
<br />
<h1>winmove.nvim</h1>
<p><i>Easily move and swap windows</i></p>
<p><i>Easily move, swap, and resize windows</i></p>
<p>
<img src="https://img.shields.io/badge/version-0.1.0-blue?style=flat-square" />
<a href="https://luarocks.org/modules/misanthropicbit/winmove.nvim">
Expand Down Expand Up @@ -106,6 +106,21 @@ require('winmove').configure({
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
},
},
},
})
```
Expand Down Expand Up @@ -144,7 +159,8 @@ Get the current version of `winmove`.

#### `winmove.current_mode`

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

#### `winmove.start_mode`

Expand All @@ -158,6 +174,7 @@ winmove.start_mode(mode)
winmove.start_mode(winmove.Mode.Move)
winmove.start_mode("swap")
winmove.start_mode("move")
winmove.start_mode(winmove.Mode.Resize)
```

#### `winmove.stop_mode`
Expand Down Expand Up @@ -234,6 +251,32 @@ 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](#moving-and-resizing-windows).

```lua
---@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](/CONTRIBUTING.md).
Expand Down Expand Up @@ -272,6 +315,12 @@ windows, splitting into a window will move it next to a target window.

https://github.com/user-attachments/assets/4bf49e27-d08b-4926-9f17-57bf2e702c64

### Resizing windows

https://github.com/user-attachments/assets/8f77c9c4-dca1-4647-9049-8695e5351431

https://github.com/user-attachments/assets/8f1fff43-2830-48f5-a29b-0b1aa7d865b2

### Moving as far as possible in a direction

https://github.com/user-attachments/assets/b3550d2d-287b-4b5d-9ea9-3466ac47c0d1
Expand Down
79 changes: 71 additions & 8 deletions lua/winmove/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,25 @@ local config_loaded = false
---@field at_edge winmove.AtEdgeConfig
---@field keymaps winmove.ConfigSwapModeKeymaps

---@class winmove.ConfigResizeModeKeymaps
---@field left string
---@field down string
---@field up string
---@field right string
---@field left_botright string
---@field down_botright string
---@field up_botright string
---@field right_botright string

---@class winmove.ConfigResizeMode
---@field highlight winmove.Highlight
---@field default_resize_count integer
---@field keymaps winmove.ConfigResizeModeKeymaps

---@class winmove.ConfigModes
---@field move winmove.ConfigMoveMode
---@field swap winmove.ConfigSwapMode
---@field move winmove.ConfigMoveMode
---@field swap winmove.ConfigSwapMode
---@field resize winmove.ConfigResizeMode

---@class winmove.AtEdgeConfig
---@field horizontal winmove.AtEdge
Expand Down Expand Up @@ -96,6 +112,20 @@ local default_config = {
right = "l",
},
},
resize = {
highlight = "Todo",
default_resize_count = 3,
keymaps = {
left = "h",
down = "j",
up = "k",
right = "l",
left_botright = "<c-h>",
down_botright = "<c-j>",
up_botright = "<c-k>",
right_botright = "<c-l>",
},
},
},
}

Expand Down Expand Up @@ -131,6 +161,18 @@ local mapping_descriptions = {
right = "Swap window right",
},
},
resize = {
keymaps = {
left = "Resize window left",
down = "Resize window down",
up = "Resize window up",
right = "Resize window right",
left_botright = "Resize window left with bottom-right anchor",
down_botright = "Resize window down with bottom-right anchor",
up_botright = "Resize window up with bottom-right anchor",
right_botright = "Resize window right with bottom-right anchor",
},
},
},
}

Expand All @@ -154,6 +196,10 @@ function config.valid_string_option(value)
return value ~= nil and type(value) == "string" and #value > 0
end

local function is_positive_non_zero_number(value)
return type(value) == "number" and value > 0
end

local function is_non_empty_string(value)
return type(value) == "string" and #value > 0
end
Expand Down Expand Up @@ -209,6 +255,9 @@ local vertical_validator = {

local non_empty_string_validator = { is_non_empty_string, expected_non_empty_string }

local is_positive_non_zero_number_validator =
{ is_positive_non_zero_number, "a positive, non-zero number" }

--- Validate a config
---@param _config winmove.Config
---@return boolean
Expand All @@ -229,7 +278,7 @@ function config.validate(_config)
highlight = "string",
at_edge = {
horizontal = horizontal_validator,
vertical = vertical_validator,
vertical = vertical_validator,
},
keymaps = {
left = non_empty_string_validator,
Expand All @@ -250,13 +299,27 @@ function config.validate(_config)
highlight = "string",
at_edge = {
horizontal = horizontal_validator,
vertical = vertical_validator,
vertical = vertical_validator,
},
keymaps = {
left = non_empty_string_validator,
down = non_empty_string_validator,
up = non_empty_string_validator,
right = non_empty_string_validator,
left = non_empty_string_validator,
down = non_empty_string_validator,
up = non_empty_string_validator,
right = non_empty_string_validator,
},
},
resize = {
highlight = "string",
default_resize_count = is_positive_non_zero_number_validator,
keymaps = {
left = non_empty_string_validator,
down = non_empty_string_validator,
up = non_empty_string_validator,
right = non_empty_string_validator,
left_botright = non_empty_string_validator,
down_botright = non_empty_string_validator,
up_botright = non_empty_string_validator,
right_botright = non_empty_string_validator,
},
},
},
Expand Down
1 change: 1 addition & 0 deletions lua/winmove/highlight.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ local api = vim.api
local win_highlights = {
move = nil,
swap = nil,
resize = nil,
}

---@type string?
Expand Down
62 changes: 60 additions & 2 deletions lua/winmove/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ local layout = require("winmove.layout")
local message = require("winmove.message")
local _mode = require("winmove.mode")
local State = require("winmove.state")
local resize = require("winmove.resize")
local str = require("winmove.util.str")
local swap = require("winmove.swap")
local validators = require("winmove.validators")
local winutil = require("winmove.winutil")

winmove.Mode = _mode.Mode
winmove.AtEdge = at_edge.AtEdge
winmove.ResizeAnchor = resize.anchor

local api = vim.api
local winmove_version = "0.1.0"
Expand Down Expand Up @@ -281,7 +283,7 @@ function winmove.move_window_far(win_id, dir)

winutil.wincall(win_id, function()
vim.cmd("wincmd " .. dir:upper())
end, dir)
end)
end

---@param win_id integer
Expand Down Expand Up @@ -336,9 +338,15 @@ function winmove.swap_window(win_id)
end)
end

local next_mode = {
[winmove.Mode.Move] = winmove.Mode.Swap,
[winmove.Mode.Swap] = winmove.Mode.Resize,
[winmove.Mode.Resize] = winmove.Mode.Move,
}

local function toggle_mode()
local mode = winmove.current_mode()
local new_mode = mode == winmove.Mode.Move and winmove.Mode.Swap or winmove.Mode.Move
local new_mode = next_mode[mode]

stop_mode(mode)
start_mode(new_mode)
Expand Down Expand Up @@ -395,6 +403,38 @@ local function swap_mode_key_handler(keys)
end
end

---@param keys string
local function resize_mode_key_handler(keys)
local count = vim.v.count

-- If no count is provided use the default count
if count == 0 then
count = config.modes.resize.default_resize_count
end

---@type integer
local win_id = state.win_id
local keymaps = config.modes.resize.keymaps

if keys == keymaps.left then
resize.resize_window(win_id, "h", count, resize.anchor.TopLeft)
elseif keys == keymaps.down then
resize.resize_window(win_id, "j", count, resize.anchor.TopLeft)
elseif keys == keymaps.up then
resize.resize_window(win_id, "k", count, resize.anchor.TopLeft)
elseif keys == keymaps.right then
resize.resize_window(win_id, "l", count, resize.anchor.TopLeft)
elseif keys == keymaps.left_botright then
resize.resize_window(win_id, "h", count, resize.anchor.BottomRight)
elseif keys == keymaps.down_botright then
resize.resize_window(win_id, "j", count, resize.anchor.BottomRight)
elseif keys == keymaps.up_botright then
resize.resize_window(win_id, "k", count, resize.anchor.BottomRight)
elseif keys == keymaps.right_botright then
resize.resize_window(win_id, "l", count, resize.anchor.BottomRight)
end
end

--- Set a move mode keymap for a buffer
---@param win_id integer
---@param bufnr integer
Expand Down Expand Up @@ -468,6 +508,7 @@ end
local mode_key_handlers = {
[winmove.Mode.Move] = move_mode_key_handler,
[winmove.Mode.Swap] = swap_mode_key_handler,
[winmove.Mode.Resize] = resize_mode_key_handler,
}

---@param mode winmove.Mode
Expand Down Expand Up @@ -724,6 +765,23 @@ function winmove.stop_mode()
stop_mode(winmove.current_mode())
end

---@param win_id integer
---@param dir winmove.Direction
---@param count integer
---@param anchor winmove.ResizeAnchor?
function winmove.resize_window(win_id, dir, count, anchor)
vim.validate({
win_id = validators.win_id_validator(win_id),
dir = validators.dir_validator(dir),
count = { count, validators.is_nonnegative_number, "a non-negative number" },
anchor = { anchor, resize.is_valid_anchor, "a valid anchor" },
})

winutil.wincall(win_id, function()
resize.resize_window(win_id, dir, count, anchor)
end)
end

function winmove.current_mode()
return state:get("mode")
end
Expand Down
29 changes: 29 additions & 0 deletions lua/winmove/layout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,35 @@ function layout.get_wraparound_neighbor(win_id, dir)
return result
end

--- Apply a function to each neighbor in a direction
---@param win_id integer
---@param dir winmove.Direction
---@param func fun(win_id: integer): boolean, boolean
function layout.apply_to_neighbors(win_id, dir, func)
local neighbor_win_id = layout.get_neighbor(win_id, dir)
local count, applied = 0, 0

while neighbor_win_id do
local continue, did_apply = func(neighbor_win_id)
applied = applied + (did_apply and 1 or 0)

if not continue then
break
end

count = count + 1
local next_neighbor = vim.fn.win_getid(vim.fn.winnr(("%d%s"):format(count + 1, dir)))

if next_neighbor == neighbor_win_id then
break
end

neighbor_win_id = next_neighbor
end

return count, applied
end

--- Determine if two windows are siblings in the same row or column
---@param win_id1 integer
---@param win_id2 integer
Expand Down
3 changes: 2 additions & 1 deletion lua/winmove/mode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local mode = {}
mode.Mode = {
Move = "move",
Swap = "swap",
Resize = "resize",
}

---@param value any
Expand All @@ -13,7 +14,7 @@ function mode.is_valid_mode(value)
return false
end

return value == mode.Mode.Move or value == mode.Mode.Swap
return value == mode.Mode.Move or value == mode.Mode.Swap or value == mode.Mode.Resize
end

return mode
Loading

0 comments on commit 255e949

Please sign in to comment.