1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
---Window utility functions for Neovim split detection.
---@class HerdrSplitsWin
local M = {}
local config = require('herdr-splits.config')
---Check if the current window spans the full terminal width.
---@param winid number|nil window ID, defaults to current
---@return boolean
function M.is_full_width(winid)
return vim.api.nvim_win_get_width(winid or 0) == vim.o.columns
end
---Check if the current window spans the full terminal height.
---Accounts for cmdheight, statusline, and tabline.
---@param winid number|nil window ID, defaults to current
---@return boolean
function M.is_full_height(winid)
local height = vim.o.lines - vim.o.cmdheight
local tabpages = #vim.api.nvim_list_tabpages()
local wins = #vim.api.nvim_tabpage_list_wins(0)
if (vim.o.laststatus == 1 and wins > 1) or vim.o.laststatus > 1 then
height = height - 1
end
if (vim.o.showtabline == 1 and tabpages > 1) or vim.o.showtabline == 2 then
height = height - 1
end
return vim.api.nvim_win_get_height(winid or 0) == height
end
---@return boolean
function M.at_left_edge()
return vim.fn.winnr() == vim.fn.winnr('h')
end
---@return boolean
function M.at_right_edge()
return vim.fn.winnr() == vim.fn.winnr('l')
end
---@return boolean
function M.at_top_edge()
return vim.fn.winnr() == vim.fn.winnr('k')
end
---@return boolean
function M.at_bottom_edge()
return vim.fn.winnr() == vim.fn.winnr('j')
end
---Determine where the current window sits in the Neovim split layout
---for a given direction (horizontal or vertical).
---@param direction '"left"'|'"right"'|'"up"'|'"down"'
---@return '"start"'|'"middle"'|'"last"'
function M.win_position(direction)
if direction == 'left' or direction == 'right' then
if M.at_left_edge() then
return 'start'
end
if M.at_right_edge() then
return 'last'
end
return 'middle'
end
if M.at_top_edge() then
return 'start'
end
if M.at_bottom_edge() then
return 'last'
end
return 'middle'
end
---Check if a window should be ignored during resize operations.
---@param winid number|nil window ID, defaults to current
---@return boolean
function M.is_ignored_win(winid)
local bufnr = vim.api.nvim_win_get_buf(winid or 0)
return vim.tbl_contains(config.ignored_buftypes, vim.api.nvim_get_option_value('buftype', { buf = bufnr }))
or vim.tbl_contains(config.ignored_filetypes, vim.api.nvim_get_option_value('filetype', { buf = bufnr }))
end
---Check if the current window is a floating window.
---@param winid number|nil
---@return boolean
function M.is_floating(winid)
return vim.api.nvim_win_get_config(winid or 0).relative ~= ''
end
---Check if a window is an "embedded" floating window — one that is technically
---floating (relative ~= '') but visually behaves like a sidebar (e.g. snacks
---explorer). Neovim's default floating zindex is 50; anything explicitly set
---below that signals the window is meant to coexist with normal splits.
---@param winid number|nil window ID, defaults to current
---@return boolean
function M.is_embedded_floating_window(winid)
if not M.is_floating(winid) then
return false
end
local cfg = vim.api.nvim_win_get_config(winid or 0)
local threshold = config.floating_zindex_max or 50
return cfg.zindex ~= nil and cfg.zindex < threshold
end
---Same as M.is_ignored_win but also checks previewwindow when opt-in.
---@param winid number|nil window ID, defaults to current
---@return boolean
function M.is_ignored_or_preview(winid)
if M.is_ignored_win(winid) then
return true
end
if config.ignore_previewwindows then
local ok, pw = pcall(vim.api.nvim_win_get_option, winid or 0, 'previewwindow')
if ok and pw then
return true
end
end
return false
end
---Returns true while Neovim's command-line window (q:, q/, q?) is open.
---Inside it all window commands raise E11, so callers must short-circuit.
---@return boolean
function M.is_command_line_window()
return vim.fn.getcmdwintype() ~= ''
end
---Direction key shorthand for wincmd.
M.dir_keys = {
left = 'h',
right = 'l',
up = 'k',
down = 'j',
}
M.dir_keys_reverse = {
left = 'l',
right = 'h',
up = 'j',
down = 'k',
}
---Reverse of a direction name: left<->right, up<->down.
M.reverse_direction = {
left = 'right',
right = 'left',
up = 'down',
down = 'up',
}
return M
|