Skip to content

Commit

Permalink
Merge pull request #87 from mrjones2014/mrj/85/user-defined-at-edge
Browse files Browse the repository at this point in the history
feat(api): `at_edge` may now be a user-defined function
  • Loading branch information
mrjones2014 authored Apr 19, 2023
2 parents 49a7e22 + c677a76 commit e27791a
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 63 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ require('smart-splits').setup({
-- 'wrap' => Wrap to opposite side
-- 'split' => Create a new split in the desired direction
-- 'stop' => Do nothing
-- function => You handle the behavior yourself
-- NOTE: If using a function, the function will be called with
-- a context object with the following fields:
-- {
-- mux = {
-- type:'tmux'|'wezterm'|'kitty'
-- current_pane_id():number,
-- is_in_session(): boolean
-- current_pane_is_zoomed():boolean,
-- -- following methods return a boolean to indicate success or failure
-- current_pane_at_edge(direction:'left'|'right'|'up'|'down'):boolean
-- next_pane(direction:'left'|'right'|'up'|'down'):boolean
-- resize_pane(direction:'left'|'right'|'up'|'down'):boolean
-- },
-- direction = 'left'|'right'|'up'|'down',
-- split(), -- utility function to split current Neovim pane in the current direction
-- wrap(), -- utility function to wrap to opposite Neovim pane
-- }
-- NOTE: `at_edge = 'wrap'` is not supported on Kitty terminal
-- multiplexer, as there is no way to determine layout via the CLI
at_edge = 'wrap',
Expand Down
87 changes: 50 additions & 37 deletions lua/smart-splits/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ local function at_right_edge()
return vim.fn.winnr() == vim.fn.winnr('l')
end

---@param direction Direction
---@param direction SmartSplitsDirection
---@return WinPosition
function M.win_position(direction)
if direction == Direction.left or direction == Direction.right then
Expand All @@ -139,7 +139,7 @@ function M.win_position(direction)
return WinPosition.middle
end

---@param direction Direction
---@param direction SmartSplitsDirection
---@return WincmdResizeDirection
local function compute_direction_vertical(direction)
local current_pos = M.win_position(direction)
Expand All @@ -150,7 +150,7 @@ local function compute_direction_vertical(direction)
return direction == Direction.down and WincmdResizeDirection.smaller or WincmdResizeDirection.bigger
end

---@param direction Direction
---@param direction SmartSplitsDirection
---@return WincmdResizeDirection
local function compute_direction_horizontal(direction)
local current_pos = M.win_position(direction)
Expand Down Expand Up @@ -189,7 +189,7 @@ local function compute_direction_horizontal(direction)
return result
end

---@param direction Direction
---@param direction SmartSplitsDirection
---@param amount number
local function resize(direction, amount)
amount = amount or config.default_amount
Expand Down Expand Up @@ -273,16 +273,31 @@ local function resize(direction, amount)
end
end

---@param at_edge_and_moving_to_edge boolean
---@param will_wrap boolean
---@param dir_key DirectionKeys
local function move_to_edge(at_edge_and_moving_to_edge, dir_key)
local function next_win_or_wrap(will_wrap, dir_key)
-- if someone has more than 99999 windows then just LOL
vim.api.nvim_set_current_win(
vim.fn.win_getid(vim.fn.winnr(string.format('%s%s', at_edge_and_moving_to_edge and '99999' or '1', dir_key)))
vim.fn.win_getid(vim.fn.winnr(string.format('%s%s', will_wrap and '99999' or '1', dir_key)))
)
end

---@param direction Direction
---@param direction SmartSplitsDirection
local function split_edge(direction)
if direction == Direction.left or direction == Direction.right then
vim.cmd('vsp')
if vim.opt.splitright and direction == Direction.left then
vim.cmd('wincmd h')
end
else
vim.cmd('sp')
if vim.opt.splitbelow and direction == Direction.up then
vim.cmd('wincmd k')
end
end
end

---@param direction SmartSplitsDirection
---@param opts table
local function move_cursor(direction, opts)
-- backwards compatibility, if opts is a boolean, treat it as historical `same_row` argument
Expand Down Expand Up @@ -314,19 +329,32 @@ local function move_cursor(direction, opts)
local at_bottom = at_bottom_edge()

-- are we at an edge and attempting to move in the direction of the edge we're already at?
local at_edge_and_moving_to_edge = (direction == Direction.left and at_left)
local will_wrap = (direction == Direction.left and at_left)
or (direction == Direction.right and at_right)
or (direction == Direction.up and at_top)
or (direction == Direction.down and at_bottom)

if at_edge_and_moving_to_edge then
if will_wrap then
-- if we can move with mux, then we're good
if mux.move_pane(direction, at_edge_and_moving_to_edge, at_edge) then
if mux.move_pane(direction, will_wrap, at_edge) then
return
end

-- otherwise check at_edge behavior
if at_edge == AtEdgeBehavior.stop then
if type(at_edge) == 'function' then
local ctx = { ---@type SmartSplitsContext
mux = mux.get(),
direction = direction,
split = function()
split_edge(direction)
end,
wrap = function()
next_win_or_wrap(will_wrap, DirectionKeysReverse[direction])
end,
}
at_edge(ctx)
return
elseif at_edge == AtEdgeBehavior.stop then
return
elseif at_edge == AtEdgeBehavior.split then
-- if at_edge = 'split' and we're in an ignored buffer, just stop
Expand All @@ -337,32 +365,17 @@ local function move_cursor(direction, opts)
return
end

if direction == Direction.left or direction == Direction.right then
vim.cmd('vsp')
if vim.opt.splitright and direction == Direction.left then
vim.cmd('wincmd h')
end
else
vim.cmd('sp')
if vim.opt.splitbelow and direction == Direction.up then
vim.cmd('wincmd k')
end
end
split_edge(direction)
return
else -- at_edge == AtEdgeBehavior.wrap
-- reverse direction and continue
dir_key = DirectionKeysReverse[direction]
end
-- else, at_edge == AtEdgeBehavior.wrap, continue
end

if at_edge_and_moving_to_edge then
dir_key = DirectionKeysReverse[direction]
end

move_to_edge(at_edge_and_moving_to_edge, dir_key)
next_win_or_wrap(will_wrap, dir_key)

if
(direction == Direction.left or direction == Direction.right)
and (same_row or (same_row == nil and config.move_cursor_same_row))
then
if (direction == Direction.left or direction == Direction.right) and same_row then
offset = offset - vim.api.nvim_win_get_position(0)[1]
vim.cmd('normal! ' .. offset .. 'H')
end
Expand All @@ -378,23 +391,23 @@ local function set_eventignore()
vim.o.eventignore = eventignore
end

---@param direction Direction
---@param direction SmartSplitsDirection
---@param opts table
local function swap_bufs(direction, opts)
opts = opts or {}
local buf_1 = vim.api.nvim_get_current_buf()
local win_1 = vim.api.nvim_get_current_win()

local dir_key = DirectionKeys[direction]
local at_edge_and_moving_to_edge = (direction == Direction.right and at_right_edge())
local will_wrap = (direction == Direction.right and at_right_edge())
or (direction == Direction.left and at_left_edge())
or (direction == Direction.up and at_top_edge())
or (direction == Direction.down and at_bottom_edge())
if at_edge_and_moving_to_edge then
if will_wrap then
dir_key = DirectionKeysReverse[direction]
end

move_to_edge(at_edge_and_moving_to_edge, dir_key)
next_win_or_wrap(will_wrap, dir_key)
local buf_2 = vim.api.nvim_get_current_buf()
local win_2 = vim.api.nvim_get_current_win()

Expand Down
4 changes: 2 additions & 2 deletions lua/smart-splits/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ local Multiplexer = types.Multiplexer
---@field ignored_buftypes string[]
---@field ignored_filetypes string[]
---@field default_amount number
---@field at_edge AtEdgeBehavior
---@field at_edge SmartSplitsAtEdgeBehavior
---@field move_cursor_same_row boolean
---@field cursor_follows_swapped_bufs boolean
---@field resize_mode SmartResizeModeConfig
---@field ignored_events string[]
---@field multiplexer_integration MultiplexerType|false
---@field multiplexer_integration SmartSplitsMultiplexerType|false
---@field disable_multiplexer_nav_when_zoomed boolean
---@field kitty_password string|nil
---@field setup fun(cfg:table)
Expand Down
16 changes: 12 additions & 4 deletions lua/smart-splits/mux/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,16 @@ end
local M = {}

---Get the currently configured multiplexer
---@return Multiplexer|nil
---@return SmartSplitsMultiplexer|nil
function M.get()
if
config.multiplexer_integration == nil
or config.multiplexer_integration == false
or #tostring(config.multiplexer_integration or '') == 0
then
return nil
end

local ok, mux = pcall(require, string.format('smart-splits.mux.%s', config.multiplexer_integration))
return ok and mux or nil
end
Expand All @@ -52,9 +60,9 @@ function M.is_enabled()
end

---Try moving with multiplexer
---@param direction Direction direction to move
---@param direction SmartSplitsDirection direction to move
---@param will_wrap boolean whether to wrap around edge
---@param at_edge AtEdgeBehavior behavior at edge
---@param at_edge SmartSplitsAtEdgeBehavior behavior at edge
---@return boolean whether we moved with multiplexer or not
function M.move_pane(direction, will_wrap, at_edge)
at_edge = at_edge or config.at_edge
Expand All @@ -80,7 +88,7 @@ function M.move_pane(direction, will_wrap, at_edge)
end

---Try resizing with multiplexer
---@param direction Direction direction to resize
---@param direction SmartSplitsDirection direction to resize
---@param amount number amount to resize
---@return boolean whether we resized with multiplexer or not
function M.resize_pane(direction, amount)
Expand Down
4 changes: 3 additions & 1 deletion lua/smart-splits/mux/kitty.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ local function kitty_exec(args)
return vim.fn.system(arguments)
end

---@type Multiplexer
---@type SmartSplitsMultiplexer
local M = {}

M.type = 'kitty'

function M.current_pane_id()
local output = kitty_exec({ 'ls' })
local kitty_info = vim.json.decode(output)
Expand Down
4 changes: 3 additions & 1 deletion lua/smart-splits/mux/tmux.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ local function tmux_exec(cmd, as_list)
return vim.fn.system(cmd_str)
end

---@type Multiplexer
---@type SmartSplitsMultiplexer
local M = {}

M.type = 'tmux'

function M.current_pane_at_edge(direction)
if not M.is_in_session() then
return false
Expand Down
4 changes: 3 additions & 1 deletion lua/smart-splits/mux/wezterm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ local function current_pane_info()
return nil
end

---@type Multiplexer
---@type SmartSplitsMultiplexer
local M = {}

M.type = 'wezterm'

function M.current_pane_id()
local current_pane = current_pane_info()
-- uses API that requires newest version of Wezterm
Expand Down
40 changes: 23 additions & 17 deletions lua/smart-splits/types.lua
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
---@class Multiplexer
---@class SmartSplitsMultiplexer
---@field current_pane_id fun():number|nil
---@field current_pane_at_edge fun(direction:Direction):boolean
---@field current_pane_at_edge fun(direction:SmartSplitsDirection):boolean
---@field is_in_session fun():boolean
---@field current_pane_is_zoomed fun():boolean
---@field next_pane fun(direction:Direction):boolean
---@field resize_pane fun(direction:Direction, amount:number):boolean
---@field next_pane fun(direction:SmartSplitsDirection):boolean
---@field resize_pane fun(direction:SmartSplitsDirection, amount:number):boolean
---@field type SmartSplitsMultiplexerType

---@alias Direction 'left'|'right'|'up'|'down'
---@alias SmartSplitsDirection 'left'|'right'|'up'|'down'

---@alias AtEdgeBehavior 'split'|'wrap'|'stop'
---@alias SmartSplitsAtEdgeBehavior 'split'|'wrap'|'stop'|function

---@alias MultiplexerType 'tmux'|'wezterm'|'kitty'
---@alias SmartSplitsMultiplexerType 'tmux'|'wezterm'|'kitty'

---@class SmartSplitsContext
---@field mux SmartSplitsMultiplexer|nil Multiplexer API, if one is currently in use
---@field direction SmartSplitsDirection Which direction you're moving (also indicates edge your cursor is currently at)
---@field split fun() Utility function to split the window into the current direction

local M = {
Direction = {
---@type Direction
---@type SmartSplitsDirection
left = 'left',
---@type Direction
---@type SmartSplitsDirection
right = 'right',
---@type Direction
---@type SmartSplitsDirection
up = 'up',
---@type Direction
---@type SmartSplitsDirection
down = 'down',
},
AtEdgeBehavior = {
---@type AtEdgeBehavior
---@type SmartSplitsAtEdgeBehavior
split = 'split',
---@type AtEdgeBehavior
---@type SmartSplitsAtEdgeBehavior
wrap = 'wrap',
---@type AtEdgeBehavior
---@type SmartSplitsAtEdgeBehavior
stop = 'stop',
},
Multiplexer = {
---@type MultiplexerType
---@type SmartSplitsMultiplexerType
tmux = 'tmux',
---@type MultiplexerType
---@type SmartSplitsMultiplexerType
wezterm = 'wezterm',
---@type MultiplexerType
---@type SmartSplitsMultiplexerType
kitty = 'kitty',
},
}
Expand Down

0 comments on commit e27791a

Please sign in to comment.