feat: support preview from floating window (#403)

* implement floating window

* reset width on closing window

* use gap from new config parameter

* use minimal style for preview in floating

* lower z-index

* add configuration of preview position in floating window

* fix in verions earlier than nvim 0.10

* close preview on opening floating window

Close the any existing preview because otherwise strange errors happen when the preview is open and the floating window is opened at the same time.

* reset formatting changes

* remove empty line

* change z-index of preview window to floating window z-index

* add configurations to oil.txt

* formatting

* add auto configuration

* update oil doc

* refactor: move logic into layout.lua and eliminate flicker

* fix: floating preview window title is file name

* doc: clarify default_file_explorer

* refactor: don't need a preview_gap option

* refactor: only find preview win in current tabpage

---------

Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
This commit is contained in:
Philipp Oeschger 2024-06-20 03:23:30 +02:00 committed by GitHub
parent 0883b109a7
commit 59b3dab6f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 162 additions and 47 deletions

View file

@ -127,7 +127,7 @@ You can open a directory with `:edit <path>` or `:Oil <path>`. To open oil in a
```lua ```lua
require("oil").setup({ require("oil").setup({
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`) -- Oil will take over directory buffers (e.g. `vim .` or `:e src/`)
-- Set to false if you still want to use netrw. -- Set to false if you want some other plugin (e.g. netrw) to open when you edit directories.
default_file_explorer = true, default_file_explorer = true,
-- Id is automatically added at the beginning, and name at the end -- Id is automatically added at the beginning, and name at the end
-- See :help oil-columns -- See :help oil-columns
@ -248,6 +248,8 @@ require("oil").setup({
win_options = { win_options = {
winblend = 0, winblend = 0,
}, },
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
preview_split = "auto",
-- This is the config that will be passed to nvim_open_win. -- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout -- Change values here to customize the layout
override = function(conf) override = function(conf)

View file

@ -17,7 +17,7 @@ CONFIG *oil-confi
>lua >lua
require("oil").setup({ require("oil").setup({
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`) -- Oil will take over directory buffers (e.g. `vim .` or `:e src/`)
-- Set to false if you still want to use netrw. -- Set to false if you want some other plugin (e.g. netrw) to open when you edit directories.
default_file_explorer = true, default_file_explorer = true,
-- Id is automatically added at the beginning, and name at the end -- Id is automatically added at the beginning, and name at the end
-- See :help oil-columns -- See :help oil-columns
@ -138,6 +138,8 @@ CONFIG *oil-confi
win_options = { win_options = {
winblend = 0, winblend = 0,
}, },
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
preview_split = "auto",
-- This is the config that will be passed to nvim_open_win. -- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout -- Change values here to customize the layout
override = function(conf) override = function(conf)

View file

@ -80,6 +80,11 @@ M.preview = {
local cur_id = vim.w[winid].oil_entry_id local cur_id = vim.w[winid].oil_entry_id
if entry.id == cur_id then if entry.id == cur_id then
vim.api.nvim_win_close(winid, true) vim.api.nvim_win_close(winid, true)
if util.is_floating_win() then
local layout = require("oil.layout")
local win_opts = layout.get_fullscreen_win_opts()
vim.api.nvim_win_set_config(0, win_opts)
end
return return
end end
end end

View file

@ -2,7 +2,7 @@
local default_config = { local default_config = {
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`) -- Oil will take over directory buffers (e.g. `vim .` or `:e src/`)
-- Set to false if you still want to use netrw. -- Set to false if you want some other plugin (e.g. netrw) to open when you edit directories.
default_file_explorer = true, default_file_explorer = true,
-- Id is automatically added at the beginning, and name at the end -- Id is automatically added at the beginning, and name at the end
-- See :help oil-columns -- See :help oil-columns
@ -123,6 +123,8 @@ local default_config = {
win_options = { win_options = {
winblend = 0, winblend = 0,
}, },
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
preview_split = "auto",
-- This is the config that will be passed to nvim_open_win. -- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout -- Change values here to customize the layout
override = function(conf) override = function(conf)

View file

@ -244,6 +244,7 @@ M.open_float = function(dir)
local layout = require("oil.layout") local layout = require("oil.layout")
local util = require("oil.util") local util = require("oil.util")
local view = require("oil.view") local view = require("oil.view")
local parent_url, basename = M.get_url_for_path(dir) local parent_url, basename = M.get_url_for_path(dir)
if not parent_url then if not parent_url then
return return
@ -254,31 +255,7 @@ M.open_float = function(dir)
local bufnr = vim.api.nvim_create_buf(false, true) local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe" vim.bo[bufnr].bufhidden = "wipe"
local total_width = vim.o.columns local win_opts = layout.get_fullscreen_win_opts()
local total_height = layout.get_editor_height()
local width = total_width - 2 * config.float.padding
if config.float.border ~= "none" then
width = width - 2 -- The border consumes 1 col on each side
end
if config.float.max_width > 0 then
width = math.min(width, config.float.max_width)
end
local height = total_height - 2 * config.float.padding
if config.float.max_height > 0 then
height = math.min(height, config.float.max_height)
end
local row = math.floor((total_height - height) / 2)
local col = math.floor((total_width - width) / 2) - 1 -- adjust for border width
local win_opts = {
relative = "editor",
width = width,
height = height,
row = row,
col = col,
border = config.float.border,
zindex = 45,
}
win_opts = config.float.override(win_opts) or win_opts
local original_winid = vim.api.nvim_get_current_win() local original_winid = vim.api.nvim_get_current_win()
local winid = vim.api.nvim_open_win(bufnr, true, win_opts) local winid = vim.api.nvim_open_win(bufnr, true, win_opts)
@ -332,12 +309,13 @@ M.open_float = function(dir)
if not vim.api.nvim_win_is_valid(winid) or vim.api.nvim_win_get_buf(winid) ~= winbuf then if not vim.api.nvim_win_is_valid(winid) or vim.api.nvim_win_get_buf(winid) ~= winbuf then
return return
end end
local cur_win_opts = vim.api.nvim_win_get_config(winid)
vim.api.nvim_win_set_config(winid, { vim.api.nvim_win_set_config(winid, {
relative = "editor", relative = "editor",
row = win_opts.row, row = cur_win_opts.row,
col = win_opts.col, col = cur_win_opts.col,
width = win_opts.width, width = cur_win_opts.width,
height = win_opts.height, height = cur_win_opts.height,
title = get_title(), title = get_title(),
}) })
end, end,
@ -444,6 +422,8 @@ end
--- split "aboveleft"|"belowright"|"topleft"|"botright" Split modifier --- split "aboveleft"|"belowright"|"topleft"|"botright" Split modifier
M.open_preview = function(opts, callback) M.open_preview = function(opts, callback)
opts = opts or {} opts = opts or {}
local config = require("oil.config")
local layout = require("oil.layout")
local util = require("oil.util") local util = require("oil.util")
local function finish(err) local function finish(err)
@ -465,18 +445,59 @@ M.open_preview = function(opts, callback)
opts.split = vim.o.splitright and "belowright" or "aboveleft" opts.split = vim.o.splitright and "belowright" or "aboveleft"
end end
end end
if util.is_floating_win() then
return finish("oil preview doesn't work in a floating window") local preview_win = util.get_preview_win()
end local prev_win = vim.api.nvim_get_current_win()
local bufnr = vim.api.nvim_get_current_buf()
local entry = M.get_cursor_entry() local entry = M.get_cursor_entry()
if not entry then if not entry then
return finish("Could not find entry under cursor") return finish("Could not find entry under cursor")
end end
local entry_title = entry.name
if entry.type == "directory" then
entry_title = entry_title .. "/"
end
local preview_win = util.get_preview_win() if util.is_floating_win() then
local prev_win = vim.api.nvim_get_current_win() if preview_win == nil then
local bufnr = vim.api.nvim_get_current_buf() local root_win_opts, preview_win_opts =
layout.split_window(0, config.float.preview_split, config.float.padding)
local win_opts_oil = {
relative = "editor",
width = root_win_opts.width,
height = root_win_opts.height,
row = root_win_opts.row,
col = root_win_opts.col,
border = config.float.border,
zindex = 45,
}
vim.api.nvim_win_set_config(0, win_opts_oil)
local win_opts = {
relative = "editor",
width = preview_win_opts.width,
height = preview_win_opts.height,
row = preview_win_opts.row,
col = preview_win_opts.col,
border = config.float.border,
zindex = 45,
focusable = false,
noautocmd = true,
style = "minimal",
}
if vim.fn.has("nvim-0.9") == 1 then
win_opts.title = entry_title
end
preview_win = vim.api.nvim_open_win(bufnr, true, win_opts)
vim.api.nvim_set_option_value("previewwindow", true, { scope = "local", win = preview_win })
vim.api.nvim_set_current_win(prev_win)
elseif vim.fn.has("nvim-0.9") == 1 then
vim.api.nvim_win_set_config(preview_win, { title = entry_title })
end
end
local cmd = preview_win and "buffer" or "sbuffer" local cmd = preview_win and "buffer" or "sbuffer"
local mods = { local mods = {
@ -485,7 +506,6 @@ M.open_preview = function(opts, callback)
split = opts.split, split = opts.split,
} }
local is_visual_mode = util.is_visual_mode()
-- HACK Switching windows takes us out of visual mode. -- HACK Switching windows takes us out of visual mode.
-- Switching with nvim_set_current_win causes the previous visual selection (as used by `gv`) to -- Switching with nvim_set_current_win causes the previous visual selection (as used by `gv`) to
-- not get set properly. So we have to switch windows this way instead. -- not get set properly. So we have to switch windows this way instead.
@ -494,6 +514,8 @@ M.open_preview = function(opts, callback)
vim.cmd.wincmd({ args = { "w" }, count = winnr }) vim.cmd.wincmd({ args = { "w" }, count = winnr })
end end
util.get_edit_path(bufnr, entry, function(normalized_url)
local is_visual_mode = util.is_visual_mode()
if preview_win then if preview_win then
if is_visual_mode then if is_visual_mode then
hack_set_win(preview_win) hack_set_win(preview_win)
@ -502,7 +524,6 @@ M.open_preview = function(opts, callback)
end end
end end
util.get_edit_path(bufnr, entry, function(normalized_url)
local filebufnr = vim.fn.bufadd(normalized_url) local filebufnr = vim.fn.bufadd(normalized_url)
local entry_is_file = not vim.endswith(normalized_url, "/") local entry_is_file = not vim.endswith(normalized_url, "/")

View file

@ -93,6 +93,89 @@ M.calculate_height = function(desired_height, opts)
) )
end end
---@class (exact) conform.WinLayout
---@field width integer
---@field height integer
---@field row integer
---@field col integer
---@return vim.api.keyset.win_config
M.get_fullscreen_win_opts = function()
local config = require("oil.config")
local total_width = M.get_editor_width()
local total_height = M.get_editor_height()
local width = total_width - 2 * config.float.padding
if config.float.border ~= "none" then
width = width - 2 -- The border consumes 1 col on each side
end
if config.float.max_width > 0 then
width = math.min(width, config.float.max_width)
end
local height = total_height - 2 * config.float.padding
if config.float.max_height > 0 then
height = math.min(height, config.float.max_height)
end
local row = math.floor((total_height - height) / 2)
local col = math.floor((total_width - width) / 2) - 1 -- adjust for border width
local win_opts = {
relative = "editor",
width = width,
height = height,
row = row,
col = col,
border = config.float.border,
zindex = 45,
}
return config.float.override(win_opts) or win_opts
end
---@param winid integer
---@param direction "above"|"below"|"left"|"right"|"auto"
---@param gap integer
---@return conform.WinLayout root_dim New dimensions of the original window
---@return conform.WinLayout new_dim New dimensions of the new window
M.split_window = function(winid, direction, gap)
if direction == "auto" then
direction = vim.o.splitright and "right" or "left"
end
local float_config = vim.api.nvim_win_get_config(winid)
local dim_root = {
width = float_config.width,
height = float_config.height,
col = float_config.col,
row = float_config.row,
}
if vim.fn.has("nvim-0.10") == 0 then
-- read https://github.com/neovim/neovim/issues/24430 for more infos.
dim_root.col = float_config.col[vim.val_idx]
dim_root.row = float_config.row[vim.val_idx]
end
local dim_new = vim.deepcopy(dim_root)
if direction == "left" or direction == "right" then
dim_new.width = math.floor(float_config.width / 2) - math.ceil(gap / 2)
dim_root.width = dim_new.width
else
dim_new.height = math.floor(float_config.height / 2) - math.ceil(gap / 2)
dim_root.height = dim_new.height
end
if direction == "left" then
dim_root.col = dim_root.col + dim_root.width + gap
elseif direction == "right" then
dim_new.col = dim_new.col + dim_new.width + gap
elseif direction == "above" then
dim_root.row = dim_root.row + dim_root.height + gap
elseif direction == "below" then
dim_new.row = dim_new.row + dim_new.height + gap
end
return dim_root, dim_new
end
M.calculate_dims = function(desired_width, desired_height, opts) M.calculate_dims = function(desired_width, desired_height, opts)
local width = M.calculate_width(desired_width, opts) local width = M.calculate_width(desired_width, opts)
local height = M.calculate_height(desired_height, opts) local height = M.calculate_height(desired_height, opts)