aboutsummaryrefslogtreecommitdiff
path: root/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/win.lua
blob: 47f6821b845d7069c2c68f7c7082be8e2c152b04 (plain) (blame)
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