Meh I'll figure out submodules later
This commit is contained in:
parent
4ca9d44a90
commit
8cb281f436
352 changed files with 66107 additions and 0 deletions
|
|
@ -0,0 +1,91 @@
|
|||
# Split
|
||||
|
||||
Split is can be used to split your current window or editor.
|
||||
|
||||
```lua
|
||||
local Split = require("nui.split")
|
||||
|
||||
local split = Split({
|
||||
relative = "editor",
|
||||
position = "bottom",
|
||||
size = "20%",
|
||||
})
|
||||
```
|
||||
|
||||
You can manipulate the associated buffer and window using the
|
||||
`split.bufnr` and `split.winid` properties.
|
||||
|
||||
## Options
|
||||
|
||||
### `ns_id`
|
||||
|
||||
**Type:** `number` or `string`
|
||||
|
||||
Namespace id (`number`) or name (`string`).
|
||||
|
||||
### `relative`
|
||||
|
||||
**Type:** `string` or `table`
|
||||
|
||||
This option affects how `size` is calculated.
|
||||
|
||||
**Examples**
|
||||
|
||||
Split current editor screen:
|
||||
|
||||
```lua
|
||||
relative = "editor"
|
||||
```
|
||||
|
||||
Split current window (_default_):
|
||||
|
||||
```lua
|
||||
relative = "win"
|
||||
```
|
||||
|
||||
Split window with specific id:
|
||||
|
||||
```lua
|
||||
relative = {
|
||||
type = "win",
|
||||
winid = 42,
|
||||
}
|
||||
```
|
||||
|
||||
### `position`
|
||||
|
||||
`position` can be one of: `"top"`, `"right"`, `"bottom"` or `"left"`.
|
||||
|
||||
### `size`
|
||||
|
||||
`size` can be `number` or `percentage string`.
|
||||
|
||||
For `percentage string`, size is calculated according to the option `relative`.
|
||||
|
||||
### `enter`
|
||||
|
||||
**Type:** `boolean`
|
||||
|
||||
If `false`, the split is not entered immediately after mount.
|
||||
|
||||
**Examples**
|
||||
|
||||
```lua
|
||||
enter = false
|
||||
```
|
||||
|
||||
### `buf_options`
|
||||
|
||||
Table containing buffer options to set for this split.
|
||||
|
||||
### `win_options`
|
||||
|
||||
Table containing window options to set for this split.
|
||||
|
||||
## Methods
|
||||
|
||||
[Methods from `nui.popup`](/lua/nui/popup#methods) are also available for `nui.split`.
|
||||
|
||||
## Wiki Page
|
||||
|
||||
You can find additional documentation/examples/guides/tips-n-tricks in [nui.split wiki page](https://github.com/MunifTanjim/nui.nvim/wiki/nui.split).
|
||||
378
.config/nvim/pack/tree/start/nui.nvim/lua/nui/split/init.lua
Normal file
378
.config/nvim/pack/tree/start/nui.nvim/lua/nui/split/init.lua
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
local Object = require("nui.object")
|
||||
local buf_storage = require("nui.utils.buf_storage")
|
||||
local autocmd = require("nui.utils.autocmd")
|
||||
local keymap = require("nui.utils.keymap")
|
||||
local utils = require("nui.utils")
|
||||
local split_utils = require("nui.split.utils")
|
||||
|
||||
local u = {
|
||||
clear_namespace = utils._.clear_namespace,
|
||||
get_next_id = utils._.get_next_id,
|
||||
normalize_namespace_id = utils._.normalize_namespace_id,
|
||||
split = split_utils,
|
||||
}
|
||||
|
||||
local split_direction_command_map = {
|
||||
editor = {
|
||||
top = "topleft",
|
||||
right = "vertical botright",
|
||||
bottom = "botright",
|
||||
left = "vertical topleft",
|
||||
},
|
||||
win = {
|
||||
top = "aboveleft",
|
||||
right = "vertical rightbelow",
|
||||
bottom = "belowright",
|
||||
left = "vertical leftabove",
|
||||
},
|
||||
}
|
||||
|
||||
---@param winid integer
|
||||
---@param win_config _nui_split_internal_win_config
|
||||
local function move_split_window(winid, win_config)
|
||||
if win_config.relative == "editor" then
|
||||
vim.api.nvim_win_call(winid, function()
|
||||
vim.cmd("wincmd " .. ({ top = "K", right = "L", bottom = "J", left = "H" })[win_config.position])
|
||||
end)
|
||||
elseif win_config.relative == "win" then
|
||||
local move_options = {
|
||||
vertical = win_config.position == "left" or win_config.position == "right",
|
||||
rightbelow = win_config.position == "bottom" or win_config.position == "right",
|
||||
}
|
||||
|
||||
vim.cmd(
|
||||
string.format(
|
||||
"noautocmd call win_splitmove(%s, %s, #{ vertical: %s, rightbelow: %s })",
|
||||
winid,
|
||||
win_config.win,
|
||||
move_options.vertical and 1 or 0,
|
||||
move_options.rightbelow and 1 or 0
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
---@param winid integer
|
||||
---@param win_config _nui_split_internal_win_config
|
||||
local function set_win_config(winid, win_config)
|
||||
if win_config.pending_changes.position then
|
||||
move_split_window(winid, win_config)
|
||||
end
|
||||
|
||||
if win_config.pending_changes.size then
|
||||
if win_config.width then
|
||||
vim.api.nvim_win_set_width(winid, win_config.width)
|
||||
elseif win_config.height then
|
||||
vim.api.nvim_win_set_height(winid, win_config.height)
|
||||
end
|
||||
end
|
||||
|
||||
win_config.pending_changes = {}
|
||||
end
|
||||
|
||||
--luacheck: push no max line length
|
||||
|
||||
---@alias nui_split_option_relative_type 'editor'|'win'
|
||||
---@alias nui_split_option_relative { type: nui_split_option_relative_type, winid?: number }
|
||||
|
||||
---@alias nui_split_option_position "'top'"|"'right'"|"'bottom'"|"'left'"
|
||||
|
||||
---@alias nui_split_option_size { height?: number|string }|{ width?: number|string }
|
||||
|
||||
---@alias _nui_split_internal_relative { type: nui_split_option_relative_type, win: number }
|
||||
---@alias _nui_split_internal_win_config { height?: number, width?: number, position: nui_split_option_position, relative: nui_split_option_relative, win?: integer, pending_changes: table<'position'|'size', boolean> }
|
||||
|
||||
--luacheck: pop
|
||||
|
||||
---@class nui_split_internal
|
||||
---@field enter? boolean
|
||||
---@field loading boolean
|
||||
---@field mounted boolean
|
||||
---@field buf_options table<string, any>
|
||||
---@field win_options table<string, any>
|
||||
---@field position nui_split_option_position
|
||||
---@field relative _nui_split_internal_relative
|
||||
---@field size { height?: number }|{ width?: number }
|
||||
---@field win_config _nui_split_internal_win_config
|
||||
---@field pending_quit? boolean
|
||||
---@field augroup table<'hide'|'unmount', string>
|
||||
|
||||
---@class nui_split_options
|
||||
---@field ns_id? string|integer
|
||||
---@field relative? nui_split_option_relative_type|nui_split_option_relative
|
||||
---@field position? nui_split_option_position
|
||||
---@field size? number|string|nui_split_option_size
|
||||
---@field enter? boolean
|
||||
---@field buf_options? table<string, any>
|
||||
---@field win_options? table<string, any>
|
||||
|
||||
---@class NuiSplit
|
||||
---@field private _ nui_split_internal
|
||||
---@field bufnr integer
|
||||
---@field ns_id integer
|
||||
---@field winid number
|
||||
local Split = Object("NuiSplit")
|
||||
|
||||
---@param options nui_split_options
|
||||
function Split:init(options)
|
||||
local id = u.get_next_id()
|
||||
|
||||
options = u.split.merge_default_options(options)
|
||||
options = u.split.normalize_options(options)
|
||||
|
||||
self._ = {
|
||||
id = id,
|
||||
enter = options.enter,
|
||||
buf_options = options.buf_options,
|
||||
loading = false,
|
||||
mounted = false,
|
||||
layout = {},
|
||||
position = options.position,
|
||||
size = {},
|
||||
win_options = options.win_options,
|
||||
win_config = {
|
||||
pending_changes = {},
|
||||
},
|
||||
augroup = {
|
||||
hide = string.format("%s_hide", id),
|
||||
unmount = string.format("%s_unmount", id),
|
||||
},
|
||||
}
|
||||
|
||||
self.ns_id = u.normalize_namespace_id(options.ns_id)
|
||||
|
||||
self:_buf_create()
|
||||
|
||||
self:update_layout(options)
|
||||
end
|
||||
|
||||
--luacheck: push no max line length
|
||||
|
||||
---@param config { relative?: nui_split_option_relative_type|nui_split_option_relative, position?: nui_split_option_position, size?: number|string|nui_split_option_size }
|
||||
function Split:update_layout(config)
|
||||
config = config or {}
|
||||
|
||||
u.split.update_layout_config(self._, config)
|
||||
|
||||
if self.winid then
|
||||
set_win_config(self.winid, self._.win_config)
|
||||
end
|
||||
end
|
||||
|
||||
--luacheck: pop
|
||||
|
||||
function Split:_open_window()
|
||||
if self.winid or not self.bufnr then
|
||||
return
|
||||
end
|
||||
|
||||
self.winid = vim.api.nvim_win_call(self._.relative.type == "editor" and 0 or self._.relative.win, function()
|
||||
vim.api.nvim_command(
|
||||
string.format(
|
||||
"silent noswapfile %s %ssplit",
|
||||
split_direction_command_map[self._.relative.type][self._.position],
|
||||
self._.size.width or self._.size.height or ""
|
||||
)
|
||||
)
|
||||
|
||||
return vim.api.nvim_get_current_win()
|
||||
end)
|
||||
|
||||
vim.api.nvim_win_set_buf(self.winid, self.bufnr)
|
||||
|
||||
if self._.enter then
|
||||
vim.api.nvim_set_current_win(self.winid)
|
||||
end
|
||||
|
||||
self._.win_config.pending_changes = { size = true }
|
||||
set_win_config(self.winid, self._.win_config)
|
||||
|
||||
utils._.set_win_options(self.winid, self._.win_options)
|
||||
end
|
||||
|
||||
function Split:_close_window()
|
||||
if not self.winid then
|
||||
return
|
||||
end
|
||||
|
||||
if vim.api.nvim_win_is_valid(self.winid) and not self._.pending_quit then
|
||||
vim.api.nvim_win_close(self.winid, true)
|
||||
end
|
||||
|
||||
self.winid = nil
|
||||
end
|
||||
|
||||
function Split:_buf_create()
|
||||
if not self.bufnr then
|
||||
self.bufnr = vim.api.nvim_create_buf(false, true)
|
||||
assert(self.bufnr, "failed to create buffer")
|
||||
end
|
||||
end
|
||||
|
||||
function Split:mount()
|
||||
if self._.loading or self._.mounted then
|
||||
return
|
||||
end
|
||||
|
||||
self._.loading = true
|
||||
|
||||
autocmd.create_group(self._.augroup.hide, { clear = true })
|
||||
autocmd.create_group(self._.augroup.unmount, { clear = true })
|
||||
autocmd.create("QuitPre", {
|
||||
group = self._.augroup.unmount,
|
||||
buffer = self.bufnr,
|
||||
callback = function()
|
||||
self._.pending_quit = true
|
||||
vim.schedule(function()
|
||||
self:unmount()
|
||||
self._.pending_quit = nil
|
||||
end)
|
||||
end,
|
||||
}, self.bufnr)
|
||||
autocmd.create("BufWinEnter", {
|
||||
group = self._.augroup.unmount,
|
||||
buffer = self.bufnr,
|
||||
callback = function()
|
||||
-- When two splits using the same buffer and both of them
|
||||
-- are hidden, calling `:show` for one of them fires
|
||||
-- `BufWinEnter` for both of them. And in that scenario
|
||||
-- one of them will not have `self.winid`.
|
||||
if self.winid then
|
||||
autocmd.create("WinClosed", {
|
||||
group = self._.augroup.hide,
|
||||
nested = true,
|
||||
pattern = tostring(self.winid),
|
||||
callback = function()
|
||||
self:hide()
|
||||
end,
|
||||
}, self.bufnr)
|
||||
end
|
||||
end,
|
||||
}, self.bufnr)
|
||||
|
||||
self:_buf_create()
|
||||
|
||||
utils._.set_buf_options(self.bufnr, self._.buf_options)
|
||||
|
||||
self:_open_window()
|
||||
|
||||
self._.loading = false
|
||||
self._.mounted = true
|
||||
end
|
||||
|
||||
function Split:hide()
|
||||
if self._.loading or not self._.mounted then
|
||||
return
|
||||
end
|
||||
|
||||
self._.loading = true
|
||||
|
||||
pcall(autocmd.delete_group, self._.augroup.hide)
|
||||
|
||||
self:_close_window()
|
||||
|
||||
self._.loading = false
|
||||
end
|
||||
|
||||
function Split:show()
|
||||
if self._.loading then
|
||||
return
|
||||
end
|
||||
|
||||
if not self._.mounted then
|
||||
return self:mount()
|
||||
end
|
||||
|
||||
self._.loading = true
|
||||
|
||||
autocmd.create_group(self._.augroup.hide, { clear = true })
|
||||
|
||||
self:_open_window()
|
||||
|
||||
self._.loading = false
|
||||
end
|
||||
|
||||
function Split:_buf_destroy()
|
||||
if self.bufnr then
|
||||
if vim.api.nvim_buf_is_valid(self.bufnr) then
|
||||
u.clear_namespace(self.bufnr, self.ns_id)
|
||||
|
||||
if not self._.pending_quit then
|
||||
vim.api.nvim_buf_delete(self.bufnr, { force = true })
|
||||
end
|
||||
end
|
||||
|
||||
buf_storage.cleanup(self.bufnr)
|
||||
|
||||
self.bufnr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Split:unmount()
|
||||
if self._.loading or not self._.mounted then
|
||||
return
|
||||
end
|
||||
|
||||
self._.loading = true
|
||||
|
||||
pcall(autocmd.delete_group, self._.augroup.hide)
|
||||
pcall(autocmd.delete_group, self._.augroup.unmount)
|
||||
|
||||
self:_buf_destroy()
|
||||
|
||||
self:_close_window()
|
||||
|
||||
self._.loading = false
|
||||
self._.mounted = false
|
||||
end
|
||||
|
||||
-- set keymap for this split
|
||||
---@param mode string check `:h :map-modes`
|
||||
---@param key string|string[] key for the mapping
|
||||
---@param handler string | fun(): nil handler for the mapping
|
||||
---@param opts? table<"'expr'"|"'noremap'"|"'nowait'"|"'remap'"|"'script'"|"'silent'"|"'unique'", boolean>
|
||||
---@return nil
|
||||
function Split:map(mode, key, handler, opts, ___force___)
|
||||
if not self.bufnr then
|
||||
error("split buffer not found.")
|
||||
end
|
||||
|
||||
return keymap.set(self.bufnr, mode, key, handler, opts, ___force___)
|
||||
end
|
||||
|
||||
---@param mode string check `:h :map-modes`
|
||||
---@param key string|string[] key for the mapping
|
||||
---@return nil
|
||||
function Split:unmap(mode, key)
|
||||
if not self.bufnr then
|
||||
error("split buffer not found.")
|
||||
end
|
||||
|
||||
return keymap._del(self.bufnr, mode, key)
|
||||
end
|
||||
|
||||
---@param event string | string[]
|
||||
---@param handler string | function
|
||||
---@param options? table<"'once'" | "'nested'", boolean>
|
||||
function Split:on(event, handler, options)
|
||||
if not self.bufnr then
|
||||
error("split buffer not found.")
|
||||
end
|
||||
|
||||
autocmd.buf.define(self.bufnr, event, handler, options)
|
||||
end
|
||||
|
||||
---@param event? string | string[]
|
||||
function Split:off(event)
|
||||
if not self.bufnr then
|
||||
error("split buffer not found.")
|
||||
end
|
||||
|
||||
autocmd.buf.remove(self.bufnr, nil, event)
|
||||
end
|
||||
|
||||
---@alias NuiSplit.constructor fun(options: nui_split_options): NuiSplit
|
||||
---@type NuiSplit|NuiSplit.constructor
|
||||
local NuiSplit = Split
|
||||
|
||||
return NuiSplit
|
||||
177
.config/nvim/pack/tree/start/nui.nvim/lua/nui/split/utils.lua
Normal file
177
.config/nvim/pack/tree/start/nui.nvim/lua/nui/split/utils.lua
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
local utils = require("nui.utils")
|
||||
local layout_utils = require("nui.layout.utils")
|
||||
|
||||
local u = {
|
||||
defaults = utils.defaults,
|
||||
get_editor_size = utils.get_editor_size,
|
||||
get_window_size = utils.get_window_size,
|
||||
is_type = utils.is_type,
|
||||
normalize_dimension = utils._.normalize_dimension,
|
||||
size = layout_utils.size,
|
||||
}
|
||||
|
||||
local mod = {}
|
||||
|
||||
---@param size number|string|nui_split_option_size
|
||||
---@param position nui_split_option_position
|
||||
---@return number|string size
|
||||
local function to_split_size(size, position)
|
||||
if not u.is_type("table", size) then
|
||||
---@cast size number|string
|
||||
return size
|
||||
end
|
||||
|
||||
if position == "left" or position == "right" then
|
||||
return size.width
|
||||
end
|
||||
|
||||
return size.height
|
||||
end
|
||||
|
||||
---@param options table
|
||||
---@return table options
|
||||
function mod.merge_default_options(options)
|
||||
options.relative = u.defaults(options.relative, "win")
|
||||
options.position = u.defaults(options.position, vim.go.splitbelow and "bottom" or "top")
|
||||
|
||||
options.enter = u.defaults(options.enter, true)
|
||||
|
||||
options.buf_options = u.defaults(options.buf_options, {})
|
||||
options.win_options = vim.tbl_extend("force", {
|
||||
winfixwidth = true,
|
||||
winfixheight = true,
|
||||
}, u.defaults(options.win_options, {}))
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
---@param options nui_split_options
|
||||
function mod.normalize_layout_options(options)
|
||||
if utils.is_type("string", options.relative) then
|
||||
options.relative = {
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
type = options.relative,
|
||||
}
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
---@param options nui_split_options
|
||||
function mod.normalize_options(options)
|
||||
options = mod.normalize_layout_options(options)
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
local function parse_relative(relative, fallback_winid)
|
||||
local winid = u.defaults(relative.winid, fallback_winid)
|
||||
|
||||
return {
|
||||
type = relative.type,
|
||||
win = winid,
|
||||
}
|
||||
end
|
||||
|
||||
---@param relative _nui_split_internal_relative
|
||||
---@return { size: { height: integer, width: integer }, type: 'editor'|'window' }
|
||||
local function get_container_info(relative)
|
||||
if relative.type == "editor" then
|
||||
local size = u.get_editor_size()
|
||||
|
||||
-- best effort adjustments
|
||||
size.height = size.height - vim.api.nvim_get_option("cmdheight")
|
||||
if vim.api.nvim_get_option("laststatus") >= 2 then
|
||||
size.height = size.height - 1
|
||||
end
|
||||
if vim.api.nvim_get_option("showtabline") == 2 then
|
||||
size.height = size.height - 1
|
||||
end
|
||||
|
||||
return {
|
||||
size = size,
|
||||
type = "editor",
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
size = u.get_window_size(relative.win),
|
||||
type = "window",
|
||||
}
|
||||
end
|
||||
|
||||
---@param position nui_split_option_position
|
||||
---@param size number|string
|
||||
---@param container_size { width: number, height: number }
|
||||
---@return { width?: number, height?: number }
|
||||
function mod.calculate_window_size(position, size, container_size)
|
||||
if not size then
|
||||
return {}
|
||||
end
|
||||
|
||||
if position == "left" or position == "right" then
|
||||
return {
|
||||
width = u.normalize_dimension(size, container_size.width),
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
height = u.normalize_dimension(size, container_size.height),
|
||||
}
|
||||
end
|
||||
|
||||
function mod.update_layout_config(component_internal, config)
|
||||
local internal = component_internal
|
||||
|
||||
local options = mod.normalize_layout_options({
|
||||
relative = config.relative,
|
||||
position = config.position,
|
||||
size = config.size,
|
||||
})
|
||||
|
||||
if internal.relative and internal.relative.win and not vim.api.nvim_win_is_valid(internal.relative.win) then
|
||||
internal.relative.win = vim.api.nvim_get_current_win()
|
||||
|
||||
internal.win_config.win = internal.relative.win
|
||||
|
||||
internal.win_config.pending_changes.relative = true
|
||||
end
|
||||
|
||||
if options.relative then
|
||||
local fallback_winid = internal.relative and internal.relative.win or vim.api.nvim_get_current_win()
|
||||
internal.relative = parse_relative(options.relative, fallback_winid)
|
||||
|
||||
local prev_relative = internal.win_config.relative
|
||||
local prev_win = internal.win_config.win
|
||||
|
||||
internal.win_config.relative = internal.relative.type
|
||||
internal.win_config.win = internal.relative.type == "win" and internal.relative.win or nil
|
||||
|
||||
internal.win_config.pending_changes.relative = internal.win_config.relative ~= prev_relative
|
||||
or internal.win_config.win ~= prev_win
|
||||
end
|
||||
|
||||
if options.position or internal.win_config.pending_changes.relative then
|
||||
local prev_position = internal.win_config.position
|
||||
|
||||
internal.position = options.position or internal.position
|
||||
|
||||
internal.win_config.position = internal.position
|
||||
|
||||
internal.win_config.pending_changes.position = internal.win_config.position ~= prev_position
|
||||
end
|
||||
|
||||
if options.size or internal.win_config.pending_changes.position or internal.win_config.pending_changes.relative then
|
||||
internal.layout.size = to_split_size(options.size or internal.layout.size, internal.position)
|
||||
|
||||
internal.container_info = get_container_info(internal.relative)
|
||||
internal.size = mod.calculate_window_size(internal.position, internal.layout.size, internal.container_info.size)
|
||||
|
||||
internal.win_config.width = internal.size.width
|
||||
internal.win_config.height = internal.size.height
|
||||
|
||||
internal.win_config.pending_changes.size = true
|
||||
end
|
||||
end
|
||||
|
||||
return mod
|
||||
Loading…
Add table
Add a link
Reference in a new issue