diff --git a/doc/telescope.txt b/doc/telescope.txt
index 3c89161..c20f3ea 100644
--- a/doc/telescope.txt
+++ b/doc/telescope.txt
@@ -143,6 +143,20 @@ telescope.setup({opts}) *telescope.setup()*
}
+ *telescope.defaults.cycle_layout_list*
+ cycle_layout_list: ~
+ 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
+ This is interpreted as the name of a `layout_strategy`
+ 2. table
+ A table with possible keys `layout_strategy`, `layout_config` and `previewer`
+
+ Default: TODO
+
+
*telescope.defaults.winblend*
winblend: ~
Configure winblend for telescope floating windows. See |winblend| for
@@ -2238,6 +2252,65 @@ action_set.scroll_results({prompt_bufnr}, {direction}) *action_set.scroll_result
+================================================================================
+ *telescope.actions.layout*
+
+The layout actions are actions to be used to change the layout of a picker.
+
+action_layout.toggle_preview({prompt_bufnr}) *action_layout.toggle_preview()*
+ Toggle preview window.
+ - Note: preview window can be toggled even if preview is set to false.
+
+ This action is not mapped by default.
+
+
+ Parameters: ~
+ {prompt_bufnr} (number) The prompt bufnr
+
+
+action_layout.toggle_prompt_position({prompt_bufnr}) *action_layout.toggle_prompt_position()*
+ 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.
+
+
+ Parameters: ~
+ {prompt_bufnr} (number) The prompt bufnr
+
+
+action_layout.toggle_mirror({prompt_bufnr}) *action_layout.toggle_mirror()*
+ 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.
+
+
+ Parameters: ~
+ {prompt_bufnr} (number) The prompt bufnr
+
+
+action_layout.cycle_layout_next({prompt_bufnr}) *action_layout.cycle_layout_next()*
+ Cycles to the next layout in `cycle_layout_list`.
+
+ This action is not mapped by default.
+
+
+ Parameters: ~
+ {prompt_bufnr} (number) The prompt bufnr
+
+
+action_layout.cycle_layout_prev({prompt_bufnr}) *action_layout.cycle_layout_prev()*
+ Cycles to the previous layout in `cycle_layout_list`.
+
+ This action is not mapped by default.
+
+
+ Parameters: ~
+ {prompt_bufnr} (number) The prompt bufnr
+
+
+
================================================================================
*telescope.actions.utils*
diff --git a/lua/telescope/actions/layout.lua b/lua/telescope/actions/layout.lua
new file mode 100644
index 0000000..f40151f
--- /dev/null
+++ b/lua/telescope/actions/layout.lua
@@ -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
diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua
index 127307b..a96bc0b 100644
--- a/lua/telescope/config.lua
+++ b/lua/telescope/config.lua
@@ -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
+ This is interpreted as the name of a `layout_strategy`
+ 2. table
+ A table with possible keys `layout_strategy`, `layout_config` and `previewer`
+
+ Default: TODO
+ ]]
+)
+
append(
"winblend",
0,
diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua
index 76e6136..b626c46 100644
--- a/lua/telescope/pickers.lua
+++ b/lua/telescope/pickers.lua
@@ -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.
diff --git a/lua/telescope/pickers/layout_strategies.lua b/lua/telescope/pickers/layout_strategies.lua
index 411b7fc..271fc72 100644
--- a/lua/telescope/pickers/layout_strategies.lua
+++ b/lua/telescope/pickers/layout_strategies.lua
@@ -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
diff --git a/scripts/gendocs.lua b/scripts/gendocs.lua
index 025e077..95f6ea6 100644
--- a/scripts/gendocs.lua
+++ b/scripts/gendocs.lua
@@ -18,6 +18,7 @@ docs.test = function()
"./lua/telescope/actions/init.lua",
"./lua/telescope/actions/state.lua",
"./lua/telescope/actions/set.lua",
+ "./lua/telescope/actions/layout.lua",
"./lua/telescope/actions/utils.lua",
"./lua/telescope/actions/generate.lua",
"./lua/telescope/previewers/init.lua",