feat: layout actions (#1383)
Co-authored-by: johnybx <johnybx@users.noreply.github.com>
This commit is contained in:
140
lua/telescope/actions/layout.lua
Normal file
140
lua/telescope/actions/layout.lua
Normal file
@@ -0,0 +1,140 @@
|
||||
---@tag telescope.actions.layout
|
||||
|
||||
---@brief [[
|
||||
--- The layout actions are actions to be used to change the layout of a picker.
|
||||
---@brief ]]
|
||||
|
||||
local action_state = require "telescope.actions.state"
|
||||
local state = require "telescope.state"
|
||||
local layout_strats = require "telescope.pickers.layout_strategies"
|
||||
|
||||
local action_layout = {}
|
||||
|
||||
--- Toggle preview window.
|
||||
--- - Note: preview window can be toggled even if preview is set to false.
|
||||
---
|
||||
--- This action is not mapped by default.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
action_layout.toggle_preview = function(prompt_bufnr)
|
||||
local picker = action_state.get_current_picker(prompt_bufnr)
|
||||
local status = state.get_status(picker.prompt_bufnr)
|
||||
|
||||
if picker.previewer and status.preview_win then
|
||||
picker.hidden_previewer = picker.previewer
|
||||
picker.previewer = nil
|
||||
elseif picker.hidden_previewer and not status.preview_win then
|
||||
picker.previewer = picker.hidden_previewer
|
||||
picker.hidden_previewer = nil
|
||||
else
|
||||
return
|
||||
end
|
||||
picker:full_layout_update()
|
||||
end
|
||||
|
||||
-- TODO IMPLEMENT (mentored project available, contact @l-kershaw)
|
||||
action_layout.toggle_padding = function(prompt_bufnr)
|
||||
local picker = action_state.get_current_picker(prompt_bufnr)
|
||||
-- if padding ~= 0
|
||||
-- 1. Save `height` and `width` of picker
|
||||
-- 2. Set both to `{padding = 0}`
|
||||
-- else
|
||||
-- 1. Lookup previous `height` and `width` of picker
|
||||
-- 2. Set both to previous values
|
||||
picker:full_layout_update()
|
||||
end
|
||||
|
||||
--- Toggles the `prompt_position` option between "top" and "bottom".
|
||||
--- Checks if `prompt_position` is an option for the current layout.
|
||||
---
|
||||
--- This action is not mapped by default.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
action_layout.toggle_prompt_position = function(prompt_bufnr)
|
||||
local picker = action_state.get_current_picker(prompt_bufnr)
|
||||
picker.layout_config = picker.layout_config or {}
|
||||
picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {}
|
||||
-- flex layout is weird and needs handling separately
|
||||
if picker.layout_strategy == "flex" then
|
||||
picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {}
|
||||
picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {}
|
||||
local old_pos = picker.layout_config.flex[picker.__flex_strategy].prompt_position
|
||||
local new_pos = old_pos == "top" and "bottom" or "top"
|
||||
picker.layout_config[picker.__flex_strategy].prompt_position = new_pos
|
||||
picker.layout_config.flex[picker.__flex_strategy].prompt_position = new_pos
|
||||
picker:full_layout_update()
|
||||
elseif layout_strats._configurations[picker.layout_strategy].prompt_position then
|
||||
if picker.layout_config.prompt_position == "top" then
|
||||
picker.layout_config.prompt_position = "bottom"
|
||||
picker.layout_config[picker.layout_strategy].prompt_position = "bottom"
|
||||
else
|
||||
picker.layout_config.prompt_position = "top"
|
||||
picker.layout_config[picker.layout_strategy].prompt_position = "top"
|
||||
end
|
||||
picker:full_layout_update()
|
||||
end
|
||||
end
|
||||
|
||||
--- Toggles the `mirror` option between `true` and `false`.
|
||||
--- Checks if `mirror` is an option for the current layout.
|
||||
---
|
||||
--- This action is not mapped by default.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
action_layout.toggle_mirror = function(prompt_bufnr)
|
||||
local picker = action_state.get_current_picker(prompt_bufnr)
|
||||
-- flex layout is weird and needs handling separately
|
||||
if picker.layout_strategy == "flex" then
|
||||
picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {}
|
||||
picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {}
|
||||
local new_mirror = not picker.layout_config.flex[picker.__flex_strategy].mirror
|
||||
picker.layout_config[picker.__flex_strategy].mirror = new_mirror
|
||||
picker.layout_config.flex[picker.__flex_strategy].mirror = new_mirror
|
||||
picker:full_layout_update()
|
||||
elseif layout_strats._configurations[picker.layout_strategy].mirror then
|
||||
picker.layout_config = picker.layout_config or {}
|
||||
local new_mirror = not picker.layout_config.mirror
|
||||
picker.layout_config.mirror = new_mirror
|
||||
picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {}
|
||||
picker.layout_config[picker.layout_strategy].mirror = new_mirror
|
||||
picker:full_layout_update()
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper function for `cycle_layout_next` and `cycle_layout_prev`.
|
||||
local get_cycle_layout = function(dir)
|
||||
return function(prompt_bufnr)
|
||||
local picker = action_state.get_current_picker(prompt_bufnr)
|
||||
if picker.__layout_index then
|
||||
picker.__layout_index = ((picker.__layout_index + dir - 1) % #picker.__cycle_layout_list) + 1
|
||||
else
|
||||
picker.__layout_index = 1
|
||||
end
|
||||
local new_layout = picker.__cycle_layout_list[picker.__layout_index]
|
||||
if type(new_layout) == "string" then
|
||||
picker.layout_strategy = new_layout
|
||||
picker.layout_config = nil
|
||||
picker.previewer = picker.all_previewers and picker.all_previewers[1] or nil
|
||||
elseif type(new_layout) == "table" then
|
||||
picker.layout_strategy = new_layout.layout_strategy
|
||||
picker.layout_config = new_layout.layout_config
|
||||
picker.previewer = (new_layout.previewer == nil and picker.all_previewers[picker.current_previewer_index])
|
||||
or new_layout.previewer
|
||||
else
|
||||
error("Not a valid layout setup: " .. vim.inspect(new_layout) .. "\nShould be a string or a table")
|
||||
end
|
||||
|
||||
picker:full_layout_update()
|
||||
end
|
||||
end
|
||||
|
||||
--- Cycles to the next layout in `cycle_layout_list`.
|
||||
---
|
||||
--- This action is not mapped by default.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
action_layout.cycle_layout_next = get_cycle_layout(1)
|
||||
|
||||
--- Cycles to the previous layout in `cycle_layout_list`.
|
||||
---
|
||||
--- This action is not mapped by default.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
action_layout.cycle_layout_prev = get_cycle_layout(-1)
|
||||
|
||||
return action_layout
|
||||
@@ -189,6 +189,23 @@ append(
|
||||
|
||||
append("layout_config", layout_config_defaults, layout_config_description)
|
||||
|
||||
append(
|
||||
"cycle_layout_list",
|
||||
{ "horizontal", "vertical", { layout_strategy = "horizontal", previewer = false } },
|
||||
[[
|
||||
Determines the layouts to cycle through when using `actions.cycle_layout_next`
|
||||
and `actions.cycle_layout_prev`.
|
||||
Should be a list of "layout setups".
|
||||
Each "layout setup" can take one of two forms:
|
||||
1. string <br>
|
||||
This is interpreted as the name of a `layout_strategy`
|
||||
2. table <br>
|
||||
A table with possible keys `layout_strategy`, `layout_config` and `previewer`
|
||||
|
||||
Default: TODO
|
||||
]]
|
||||
)
|
||||
|
||||
append(
|
||||
"winblend",
|
||||
0,
|
||||
|
||||
@@ -104,6 +104,8 @@ function Picker:new(opts)
|
||||
layout_strategy = layout_strategy,
|
||||
layout_config = config.smarter_depth_2_extend(opts.layout_config or {}, config.values.layout_config or {}),
|
||||
|
||||
__cycle_layout_list = get_default(opts.cycle_layout_list, config.values.cycle_layout_list),
|
||||
|
||||
window = {
|
||||
winblend = get_default(
|
||||
opts.winblend,
|
||||
@@ -330,6 +332,7 @@ function Picker:find()
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
self.results_bufnr = results_bufnr
|
||||
@@ -572,9 +575,9 @@ function Picker:recalculate_layout()
|
||||
-- self.max_results = popup_opts.results.height
|
||||
end
|
||||
|
||||
local update_scroll = function(win, oldinfo, oldcursor, strategy, max_results)
|
||||
local update_scroll = function(win, oldinfo, oldcursor, strategy, buf_maxline)
|
||||
if strategy == "ascending" then
|
||||
vim.api.nvim_win_set_cursor(win, { max_results, 0 })
|
||||
vim.api.nvim_win_set_cursor(win, { buf_maxline, 0 })
|
||||
vim.api.nvim_win_set_cursor(win, { oldinfo.topline, 0 })
|
||||
vim.api.nvim_win_set_cursor(win, oldcursor)
|
||||
elseif strategy == "descending" then
|
||||
@@ -586,34 +589,15 @@ local update_scroll = function(win, oldinfo, oldcursor, strategy, max_results)
|
||||
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)
|
||||
|
||||
function Picker:full_layout_update()
|
||||
local oldinfo = vim.fn.getwininfo(self.results_win)[1]
|
||||
local oldcursor = vim.api.nvim_win_get_cursor(self.results_win)
|
||||
self:recalculate_layout()
|
||||
self:refresh_previewer()
|
||||
update_scroll(status.results_win, oldinfo, oldcursor, self.sorting_strategy, self.max_results)
|
||||
end
|
||||
|
||||
function Picker:toggle_padding()
|
||||
-- if padding ~= 0
|
||||
-- 1. Save `height` and `width` of picker
|
||||
-- 2. Set both to `{padding = 0}`
|
||||
-- else
|
||||
-- 1. Lookup previous `height` and `width` of picker
|
||||
-- 2. Set both to previous values
|
||||
-- update scrolled position
|
||||
local buf_maxline = #vim.api.nvim_buf_get_lines(self.results_bufnr, 0, -1, false)
|
||||
update_scroll(self.results_win, oldinfo, oldcursor, self.sorting_strategy, buf_maxline)
|
||||
end
|
||||
|
||||
-- TODO: update multi-select with the correct tag name when available
|
||||
@@ -1310,13 +1294,7 @@ function pickers.on_resize_window(prompt_bufnr)
|
||||
local status = state.get_status(prompt_bufnr)
|
||||
local picker = status.picker
|
||||
|
||||
local oldinfo = vim.fn.getwininfo(status.results_win)[1]
|
||||
local oldcursor = vim.api.nvim_win_get_cursor(status.results_win)
|
||||
picker:recalculate_layout()
|
||||
picker:refresh_previewer()
|
||||
|
||||
-- update scrolled position
|
||||
update_scroll(status.results_win, oldinfo, oldcursor, picker.sorting_strategy, picker.max_results)
|
||||
picker:full_layout_update()
|
||||
end
|
||||
|
||||
--- Get the prompt text without the prompt prefix.
|
||||
|
||||
@@ -707,8 +707,10 @@ layout_strategies.flex = make_documented_layout(
|
||||
local flip_lines = if_nil(layout_config.flip_lines, 20)
|
||||
|
||||
if max_columns < flip_columns and max_lines > flip_lines then
|
||||
self.__flex_strategy = "vertical"
|
||||
return layout_strategies.vertical(self, max_columns, max_lines, layout_config.vertical)
|
||||
else
|
||||
self.__flex_strategy = "horizontal"
|
||||
return layout_strategies.horizontal(self, max_columns, max_lines, layout_config.horizontal)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user