feat: add hide / unhide preview (#1305)

* feat: add hide / unhide preview or results and prompt

* fix edit actions while only preview window is active

* add option to start telescope without previewer -> "disable_previewer_at_startup"

* remove hide / unhide prompt and resutls, improve hide / unhide preview

* fix tests - check if popup window should be created with borders - popup.create does not support setting borderhighlight and border=false

* allow toggle preview even if preview is set to false

* reuse recalculate_layout in hide / unhide preview

* add docs to toggle preview

* check if preview window is valid window

* remove unnecessary changes

* cleanup, remove unnecessary changes

* close all previewers, refactor toggle preview code

[docgen] Update doc/telescope.txt
skip-checks: true
This commit is contained in:
johnybx
2021-11-08 18:20:17 +01:00
committed by Luke Kershaw
parent e9743620ce
commit b952c2f852
5 changed files with 134 additions and 73 deletions

View File

@@ -395,6 +395,9 @@ telescope.setup({opts}) *telescope.setup()*
Default: true Default: true
- msg_bg_fillchar: Character to fill background of unpreviewable buffers with - msg_bg_fillchar: Character to fill background of unpreviewable buffers with
Default: "" Default: ""
- hide_on_startup: Hide previewer when picker starts. Previewer can be toggled
with actions.toggle_preview.
Default: false
*telescope.defaults.vimgrep_arguments* *telescope.defaults.vimgrep_arguments*
@@ -1909,6 +1912,15 @@ actions.toggle_all({prompt_bufnr}) *actions.toggle_all()*
{prompt_bufnr} (number) The prompt bufnr {prompt_bufnr} (number) The prompt bufnr
actions.toggle_preview({prompt_bufnr}) *actions.toggle_preview()*
Toggle preview window.
- Note: preview window can be toggled even if preview is set to false.
Parameters: ~
{prompt_bufnr} (number) The prompt bufnr
actions.git_create_branch({prompt_bufnr}) *actions.git_create_branch()* actions.git_create_branch({prompt_bufnr}) *actions.git_create_branch()*
Create and checkout a new git branch if it doesn't already exist Create and checkout a new git branch if it doesn't already exist

View File

@@ -144,6 +144,13 @@ function actions.toggle_all(prompt_bufnr)
end) end)
end end
--- Toggle preview window.
--- - Note: preview window can be toggled even if preview is set to false.
---@param prompt_bufnr number: The prompt bufnr
function actions.toggle_preview(prompt_bufnr)
action_state.get_current_picker(prompt_bufnr):toggle_preview()
end
function actions.preview_scrolling_up(prompt_bufnr) function actions.preview_scrolling_up(prompt_bufnr)
action_set.scroll_previewer(prompt_bufnr, -1) action_set.scroll_previewer(prompt_bufnr, -1)
end end
@@ -240,23 +247,14 @@ end
actions._close = function(prompt_bufnr, keepinsert) actions._close = function(prompt_bufnr, keepinsert)
action_state.get_current_history():reset() action_state.get_current_history():reset()
local picker = action_state.get_current_picker(prompt_bufnr) local picker = action_state.get_current_picker(prompt_bufnr)
local prompt_win = state.get_status(prompt_bufnr).prompt_win
local original_win_id = picker.original_win_id local original_win_id = picker.original_win_id
if picker.previewer then
for _, v in ipairs(picker.all_previewers) do
v:teardown()
end
end
actions.close_pum(prompt_bufnr) actions.close_pum(prompt_bufnr)
if not keepinsert then if not keepinsert then
vim.cmd [[stopinsert]] vim.cmd [[stopinsert]]
end end
vim.api.nvim_win_close(prompt_win, true) require("telescope.pickers").on_close_prompt(prompt_bufnr)
pcall(vim.cmd, string.format([[silent bdelete! %s]], prompt_bufnr))
pcall(a.nvim_set_current_win, original_win_id) pcall(a.nvim_set_current_win, original_win_id)
end end

View File

@@ -431,6 +431,7 @@ append(
timeout = 250, timeout = 250,
treesitter = true, treesitter = true,
msg_bg_fillchar = "", msg_bg_fillchar = "",
hide_on_startup = false,
}, },
[[ [[
This field handles the global configuration for previewers. This field handles the global configuration for previewers.
@@ -511,6 +512,9 @@ append(
Default: true Default: true
- msg_bg_fillchar: Character to fill background of unpreviewable buffers with - msg_bg_fillchar: Character to fill background of unpreviewable buffers with
Default: "" Default: ""
- hide_on_startup: Hide previewer when picker starts. Previewer can be toggled
with actions.toggle_preview.
Default: false
]] ]]
) )

View File

@@ -135,6 +135,14 @@ function Picker:new(opts)
obj.previewer = false obj.previewer = false
end end
local __hide_previewer = opts.__hide_previewer
if __hide_previewer then
obj.hidden_previewer = obj.previewer
obj.previewer = nil
else
obj.hidden_previewer = nil
end
-- TODO: It's annoying that this is create and everything else is "new" -- TODO: It's annoying that this is create and everything else is "new"
obj.scroller = p_scroller.create(obj.scroll_strategy, obj.sorting_strategy) obj.scroller = p_scroller.create(obj.scroll_strategy, obj.sorting_strategy)
@@ -297,6 +305,7 @@ function Picker:find()
-- 1. Prompt window -- 1. Prompt window
-- 2. Options window -- 2. Options window
-- 3. Preview window -- 3. Preview window
local line_count = vim.o.lines - vim.o.cmdheight local line_count = vim.o.lines - vim.o.cmdheight
if vim.o.laststatus ~= 0 then if vim.o.laststatus ~= 0 then
line_count = line_count - 1 line_count = line_count - 1
@@ -315,12 +324,11 @@ function Picker:find()
popup_opts.prompt.titlehighlight = "TelescopePromptTitle" popup_opts.prompt.titlehighlight = "TelescopePromptTitle"
if popup_opts.preview then if popup_opts.preview then
popup_opts.preview.minheight = popup_opts.preview.height popup_opts.preview.minheight = popup_opts.preview.height
popup_opts.preview.highlight = "TelescopeNormal" popup_opts.preview.highlight = "TelescopePreviewNormal"
popup_opts.preview.borderhighlight = "TelescopePreviewBorder" popup_opts.preview.borderhighlight = "TelescopePreviewBorder"
popup_opts.preview.titlehighlight = "TelescopePreviewTitle" popup_opts.preview.titlehighlight = "TelescopePreviewTitle"
end end
-- local results_win, results_opts = popup.create("", popup_opts.results)
local results_win, results_opts, results_border_win = self:_create_window("", popup_opts.results, true) local results_win, results_opts, results_border_win = self:_create_window("", popup_opts.results, true)
local results_bufnr = a.nvim_win_get_buf(results_win) local results_bufnr = a.nvim_win_get_buf(results_win)
@@ -457,12 +465,14 @@ function Picker:find()
-- TODO: Use WinLeave as well? -- TODO: Use WinLeave as well?
local on_buf_leave = string.format( local on_buf_leave = string.format(
[[ autocmd BufLeave <buffer> ++nested ++once :silent lua require('telescope.pickers').on_close_prompt(%s)]], [[ autocmd BufLeave <buffer=%s> ++nested ++once :silent lua require('telescope.pickers').on_close_prompt(%s)]],
prompt_bufnr,
prompt_bufnr prompt_bufnr
) )
local on_vim_resize = string.format( local on_vim_resize = string.format(
[[ autocmd VimResized <buffer> ++nested :lua require('telescope.pickers').on_resize_window(%s)]], [[ autocmd VimResized <buffer=%s> ++nested :lua require('telescope.pickers').on_resize_window(%s)]],
prompt_bufnr,
prompt_bufnr prompt_bufnr
) )
@@ -527,11 +537,16 @@ function Picker:recalculate_layout()
if preview_win ~= nil then if preview_win ~= nil then
popup.move(preview_win, popup_opts.preview) popup.move(preview_win, popup_opts.preview)
else else
popup_opts.preview.highlight = "TelescopeNormal" popup_opts.preview.highlight = "TelescopePreviewNormal"
popup_opts.preview.borderhighlight = "TelescopePreviewBorder" popup_opts.preview.borderhighlight = "TelescopePreviewBorder"
popup_opts.preview.titlehighlight = "TelescopePreviewTitle" popup_opts.preview.titlehighlight = "TelescopePreviewTitle"
preview_win, preview_opts, preview_border_win = self:_create_window("", popup_opts.preview) local preview_bufnr = vim.api.nvim_buf_is_valid(status.preview_bufnr) and status.preview_bufnr or ""
preview_win, preview_opts, preview_border_win = self:_create_window(preview_bufnr, popup_opts.preview)
if preview_bufnr == "" then
preview_bufnr = a.nvim_win_get_buf(preview_win)
end
status.preview_win = preview_win status.preview_win = preview_win
status.preview_bufnr = preview_bufnr
status.preview_border_win = preview_border_win status.preview_border_win = preview_border_win
state.set_status(prompt_win, status) state.set_status(prompt_win, status)
self.preview_win = preview_win self.preview_win = preview_win
@@ -539,10 +554,8 @@ function Picker:recalculate_layout()
self.preview_border = preview_opts and preview_opts.border self.preview_border = preview_opts and preview_opts.border
end end
elseif preview_win ~= nil then elseif preview_win ~= nil then
vim.api.nvim_win_close(preview_win, false) utils.win_delete("preview_win", preview_win, true)
if status.preview_border_win then utils.win_delete("preview_win", status.preview_border_win, true)
vim.api.nvim_win_close(status.preview_border_win, false)
end
status.preview_win = nil status.preview_win = nil
status.preview_border_win = nil status.preview_border_win = nil
state.set_status(prompt_win, status) state.set_status(prompt_win, status)
@@ -559,9 +572,39 @@ function Picker:recalculate_layout()
-- self.max_results = popup_opts.results.height -- self.max_results = popup_opts.results.height
end end
function Picker:hide_preview() local update_scroll = function(win, oldinfo, oldcursor, strategy, max_results)
-- 1. Hide the window (and border) if strategy == "ascending" then
-- 2. Resize prompt & results windows accordingly vim.api.nvim_win_set_cursor(win, { max_results, 0 })
vim.api.nvim_win_set_cursor(win, { oldinfo.topline, 0 })
vim.api.nvim_win_set_cursor(win, oldcursor)
elseif strategy == "descending" then
vim.api.nvim_win_set_cursor(win, { 1, 0 })
vim.api.nvim_win_set_cursor(win, { oldinfo.botline, 0 })
vim.api.nvim_win_set_cursor(win, oldcursor)
else
error(debug.traceback("Unknown sorting strategy: " .. (strategy or "")))
end
end
function Picker:toggle_preview()
local status = state.get_status(self.prompt_bufnr)
if self.previewer and status.preview_win then
self.hidden_previewer = self.previewer
self.previewer = nil
elseif self.hidden_previewer and not status.preview_win then
self.previewer = self.hidden_previewer
self.hidden_previewer = nil
else
return
end
local oldinfo = vim.fn.getwininfo(status.results_win)[1]
local oldcursor = vim.api.nvim_win_get_cursor(status.results_win)
self:recalculate_layout()
self:refresh_previewer()
update_scroll(status.results_win, oldinfo, oldcursor, self.sorting_strategy, self.max_results)
end end
function Picker:toggle_padding() function Picker:toggle_padding()
@@ -638,6 +681,8 @@ function Picker:set_prompt(str)
end end
function Picker.close_windows(status) function Picker.close_windows(status)
-- make sure we don't have BufLeave autocmd.
vim.cmd(string.format([[ autocmd! PickerInsert BufLeave <buffer=%s> ]], status.prompt_bufnr))
local prompt_win = status.prompt_win local prompt_win = status.prompt_win
local results_win = status.results_win local results_win = status.results_win
local preview_win = status.preview_win local preview_win = status.preview_win
@@ -646,42 +691,22 @@ function Picker.close_windows(status)
local results_border_win = status.results_border_win local results_border_win = status.results_border_win
local preview_border_win = status.preview_border_win local preview_border_win = status.preview_border_win
local function del_win(name, win_id, force, bdelete) utils.win_delete("prompt_win", prompt_win, true, true)
if win_id == nil or not vim.api.nvim_win_is_valid(win_id) then utils.win_delete("results_win", results_win, true, true)
return utils.win_delete("preview_win", preview_win, true, true)
end
local bufnr = vim.api.nvim_win_get_buf(win_id) utils.win_delete("prompt_border_win", prompt_border_win, true, true)
if bdelete and vim.api.nvim_buf_is_valid(bufnr) and not vim.api.nvim_buf_get_option(bufnr, "buflisted") then utils.win_delete("results_border_win", results_border_win, true, true)
vim.cmd(string.format("silent! bdelete! %s", bufnr)) utils.win_delete("preview_border_win", preview_border_win, true, true)
end
if not vim.api.nvim_win_is_valid(win_id) then -- Buffers should be deleted but it may be also the case that buffer was swapped in window
return -- so make sure that buffers created in Picker are deleted.
end utils.buf_delete(status.prompt_bufnr)
utils.buf_delete(status.results_bufnr)
if not pcall(vim.api.nvim_win_close, win_id, force) then utils.buf_delete(status.preview_bufnr)
log.trace("Unable to close window: ", name, "/", win_id)
end
end
del_win("prompt_win", prompt_win, true)
del_win("results_win", results_win, true, true)
del_win("preview_win", preview_win, true, true)
del_win("prompt_border_win", prompt_border_win, true, true)
del_win("results_border_win", results_border_win, true, true)
del_win("preview_border_win", preview_border_win, true, true)
-- vim.cmd(string.format("bdelete! %s", status.prompt_bufnr)) -- vim.cmd(string.format("bdelete! %s", status.prompt_bufnr))
-- Major hack?? Why do I have to od this.
-- Probably because we're currently IN the buffer.
-- Should wait to do this until after we're done.
vim.defer_fn(function()
del_win("prompt_win", prompt_win, true)
end, 10)
state.clear_status(status.prompt_bufnr) state.clear_status(status.prompt_bufnr)
end end
@@ -907,7 +932,11 @@ end
function Picker:refresh_previewer() function Picker:refresh_previewer()
local status = state.get_status(self.prompt_bufnr) local status = state.get_status(self.prompt_bufnr)
if status.preview_win and self.previewer then if not self._selection_entry then
-- if selection_entry is nil there is nothing to be previewed
return
end
if self.previewer and status.preview_win and a.nvim_win_is_valid(status.preview_win) then
self:_increment "previewed" self:_increment "previewed"
self.previewer:preview(self._selection_entry, status) self.previewer:preview(self._selection_entry, status)
@@ -938,8 +967,12 @@ function Picker:cycle_previewers(next)
self.current_previewer_index = size self.current_previewer_index = size
end end
if self.previewer then
self.previewer = self.all_previewers[self.current_previewer_index] self.previewer = self.all_previewers[self.current_previewer_index]
self:refresh_previewer() self:refresh_previewer()
elseif self.hidden_previewer then
self.hidden_previewer = self.all_previewers[self.current_previewer_index]
end
end end
function Picker:entry_adder(index, entry, _, insert) function Picker:entry_adder(index, entry, _, insert)
@@ -1207,6 +1240,13 @@ pickers.new = function(opts, defaults)
end end
end end
if result["previewer"] == false then
result["previewer"] = defaults["previewer"]
result["__hide_previewer"] = true
elseif type(opts["preview"]) == "table" and opts["preview"]["hide_on_startup"] then
result["__hide_previewer"] = true
end
return Picker:new(result) return Picker:new(result)
end end
@@ -1253,8 +1293,10 @@ function pickers.on_close_prompt(prompt_bufnr)
picker.sorter:_destroy() picker.sorter:_destroy()
end end
if picker.previewer then if picker.all_previewers then
picker.previewer:teardown() for _, v in ipairs(picker.all_previewers) do
v:teardown()
end
end end
if picker.finder then if picker.finder then
@@ -1264,20 +1306,6 @@ function pickers.on_close_prompt(prompt_bufnr)
picker.close_windows(status) picker.close_windows(status)
end end
local update_scroll = function(win, oldinfo, oldcursor, strategy, max_results)
if strategy == "ascending" then
vim.api.nvim_win_set_cursor(win, { max_results, 0 })
vim.api.nvim_win_set_cursor(win, { oldinfo.topline, 0 })
vim.api.nvim_win_set_cursor(win, oldcursor)
elseif strategy == "descending" then
vim.api.nvim_win_set_cursor(win, { 1, 0 })
vim.api.nvim_win_set_cursor(win, { oldinfo.botline, 0 })
vim.api.nvim_win_set_cursor(win, oldcursor)
else
error(debug.traceback("Unknown sorting strategy: " .. (strategy or "")))
end
end
function pickers.on_resize_window(prompt_bufnr) function pickers.on_resize_window(prompt_bufnr)
local status = state.get_status(prompt_bufnr) local status = state.get_status(prompt_bufnr)
local picker = status.picker local picker = status.picker

View File

@@ -438,6 +438,25 @@ function utils.buf_delete(bufnr)
end end
end end
function utils.win_delete(name, win_id, force, bdelete)
if win_id == nil or not vim.api.nvim_win_is_valid(win_id) then
return
end
local bufnr = vim.api.nvim_win_get_buf(win_id)
if bdelete then
utils.buf_delete(bufnr)
end
if not vim.api.nvim_win_is_valid(win_id) then
return
end
if not pcall(vim.api.nvim_win_close, win_id, force) then
log.trace("Unable to close window: ", name, "/", win_id)
end
end
function utils.max_split(s, pattern, maxsplit) function utils.max_split(s, pattern, maxsplit)
pattern = pattern or " " pattern = pattern or " "
maxsplit = maxsplit or -1 maxsplit = maxsplit or -1