summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
authorTJ DeVries <devries.timothyj@gmail.com>2021-07-01 02:41:58 -0700
committerGitHub <noreply@github.com>2021-07-01 05:41:58 -0400
commit5a53ec5c2fdab10ca8775d3979b1a85e63d57953 (patch)
tree40c62f0a260a3328c3b5578c06a729b76d36d5cb /lua
parente5bd4963da81b5d044749ee4507061801aeb0f78 (diff)
feat: Consistent and sensible layout_config (#922)
* feat: Consistent and sensible layout_config * [docgen] Update doc/telescope.txt skip-checks: true * [WIP]: Thu 17 Jun 2021 03:36:44 PM EDT * [WIP]: Thu 17 Jun 2021 03:38:11 PM EDT * layout_default -> layout_defaults * remove options from bug repot * Conni2461 suggestions: part 1 * [docgen] Update doc/telescope.txt skip-checks: true * Conni2461 suggestions: part 2 * [docgen] Update doc/telescope.txt skip-checks: true * Linting * Improve deprecation checks - Move `layout_defaults` handling to `deprecated.lua` - Check for "layout keys" outside of `layout_config` on `setup` * fixup: Just add a few more words Co-authored-by: Luke Kershaw <35707277+l-kershaw@users.noreply.github.com> Co-authored-by: Github Actions <actions@github>
Diffstat (limited to 'lua')
-rw-r--r--lua/telescope/builtin/init.lua2
-rw-r--r--lua/telescope/config.lua369
-rw-r--r--lua/telescope/config/resolve.lua126
-rw-r--r--lua/telescope/deprecated.lua59
-rw-r--r--lua/telescope/make_entry.lua2
-rw-r--r--lua/telescope/pickers.lua25
-rw-r--r--lua/telescope/pickers/layout_strategies.lua532
-rw-r--r--lua/telescope/previewers/init.lua1
-rw-r--r--lua/telescope/themes.lua22
-rw-r--r--lua/tests/automated/layout_strategies_spec.lua92
-rw-r--r--lua/tests/automated/pickers/find_files_spec.lua10
11 files changed, 886 insertions, 354 deletions
diff --git a/lua/telescope/builtin/init.lua b/lua/telescope/builtin/init.lua
index 016d5ec..0588997 100644
--- a/lua/telescope/builtin/init.lua
+++ b/lua/telescope/builtin/init.lua
@@ -37,7 +37,7 @@
--- mappings = {
--- i = {
--- ["<c-d>"] = require("telescope.actions").delete_buffer,
---- -- or right hand side can also be a the name of the action as string
+--- -- or right hand side can also be the name of the action as string
--- ["<c-d>"] = "delete_buffer",
--- },
--- n = {
diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua
index da4c561..aecfcc6 100644
--- a/lua/telescope/config.lua
+++ b/lua/telescope/config.lua
@@ -1,11 +1,14 @@
-local strings = require('plenary.strings')
+local strings = require "plenary.strings"
+local deprecated = require "telescope.deprecated"
+local sorters = require "telescope.sorters"
+local if_nil = vim.F.if_nil
-- Keep the values around between reloads
_TelescopeConfigurationValues = _TelescopeConfigurationValues or {}
_TelescopeConfigurationPickers = _TelescopeConfigurationPickers or {}
local function first_non_null(...)
- local n = select('#', ...)
+ local n = select("#", ...)
for i = 1, n do
local value = select(i, ...)
@@ -15,7 +18,26 @@ local function first_non_null(...)
end
end
-local sorters = require('telescope.sorters')
+-- A function that creates an amended copy of the `base` table,
+-- by replacing keys at "level 2" that match keys in "level 1" in `priority`,
+-- and then performs a deep_extend.
+-- May give unexpected results if used with tables of "depth"
+-- greater than 2.
+local smarter_depth_2_extend = function(priority, base)
+ local result = {}
+ for key, val in pairs(base) do
+ if type(val) ~= "table" then
+ result[key] = first_non_null(priority[key], val)
+ else
+ result[key] = {}
+ for k, v in pairs(val) do
+ result[key][k] = first_non_null(priority[k], v)
+ end
+ end
+ end
+ result = vim.tbl_deep_extend("keep", priority, result)
+ return result
+end
-- TODO: Add other major configuration points here.
-- selection_strategy
@@ -27,138 +49,298 @@ config.descriptions = {}
config.pickers = _TelescopeConfigurationPickers
function config.set_pickers(pickers)
- pickers = pickers or {}
+ pickers = if_nil(pickers, {})
for k, v in pairs(pickers) do
config.pickers[k] = v
end
end
-function config.set_defaults(defaults)
- defaults = defaults or {}
-
- local function get(name, default_val)
- return first_non_null(defaults[name], config.values[name], default_val)
- end
-
- local function set(name, default_val, description)
- -- TODO(doc): Once we have descriptions for all of these, then we can add this back in.
- -- assert(description, "Config values must always have a description")
-
- config.values[name] = get(name, default_val)
- if description then
- config.descriptions[name] = strings.dedent(description)
- end
- end
-
- set("sorting_strategy", "descending", [[
+local layout_config_defaults = {
+ width = 0.8,
+ height = 0.9,
+
+ horizontal = {
+ prompt_position = "bottom",
+ preview_cutoff = 120,
+ },
+
+ vertical = {
+ preview_cutoff = 40,
+ },
+
+ center = {
+ preview_cutoff = 40,
+ },
+}
+
+local layout_config_description = string.format([[
+ Determines the default configuration values for layout strategies.
+ See |telescope.layout| for details of the configurations options for
+ each strategy.
+
+ Allows setting defaults for all strategies as top level options and
+ for overriding for specific options.
+ For example, the default values below set the default width to 80%% of
+ the screen width for all strategies except 'center', which has width
+ of 50%% of the screen width.
+
+ Default: %s
+]], vim.inspect(
+ layout_config_defaults,
+ { newline = "\n ", indent = " " }
+))
+
+-- A table of all the usual defaults for telescope.
+-- Keys will be the name of the default,
+-- values will be a list where:
+-- - first entry is the value
+-- - second entry is the description of the option
+
+local telescope_defaults = {
+
+ sorting_strategy = {
+ "descending",
+ [[
Determines the direction "better" results are sorted towards.
Available options are:
- "descending" (default)
- - "ascending"]])
+ - "ascending"]],
+ },
- set("selection_strategy", "reset", [[
+ selection_strategy = {
+ "reset",
+ [[
Determines how the cursor acts after each sort iteration.
Available options are:
- "reset" (default)
- "follow"
- - "row"]])
+ - "row"]],
+ },
- set("scroll_strategy", "cycle", [[
+ scroll_strategy = {
+ "cycle",
+ [[
Determines what happens you try to scroll past view of the picker.
Available options are:
- "cycle" (default)
- - "limit"]])
+ - "limit"]],
+ },
+
+ layout_strategy = {
+ "horizontal",
+ [[
+ Determines the default layout of Telescope pickers.
+ See |telescope.layout| for details of the available strategies.
- set("layout_strategy", "horizontal")
- set("layout_defaults", {})
+ Default: 'horizontal']],
+ },
- set("width", 0.75)
- set("winblend", 0)
- set("prompt_position", "bottom")
- set("preview_cutoff", 120)
+ layout_config = { layout_config_defaults, layout_config_description },
- set("results_height", 1)
- set("results_width", 0.8)
+ winblend = { 0 },
- set("prompt_prefix", "> ", [[
+ prompt_prefix = { "> ", [[
Will be shown in front of the prompt.
- Default: '> ']])
- set("selection_caret", "> ", [[
+ Default: '> ']]
+ },
+
+ selection_caret = { "> ", [[
Will be shown in front of the selection.
- Default: '> ']])
- set("entry_prefix", " ", [[
+ Default: '> ']]
+ },
+
+ entry_prefix = { " ", [[
Prefix in front of each result entry. Current selection not included.
- Default: ' ']])
- set("initial_mode", "insert")
+ Default: ' ']]
+ },
- set("border", {})
- set("borderchars", { '─', '│', '─', '│', '╭', '╮', '╯', '╰'})
+ initial_mode = { "insert" },
- set("get_status_text", function(self)
- local xx = (self.stats.processed or 0) - (self.stats.filtered or 0)
- local yy = self.stats.processed or 0
- if xx == 0 and yy == 0 then return "" end
+ border = { true, [[
+ Boolean defining if borders are added to Telescope windows.
- return string.format("%s / %s", xx, yy)
- end)
+ Default: true]]
+ },
- -- Builtin configuration
+ borderchars = { { "─", "│", "─", "│", "╭", "╮", "╯", "╰" } },
- -- List that will be executed.
- -- Last argument will be the search term (passed in during execution)
- set("vimgrep_arguments",
- {'rg', '--color=never', '--no-heading', '--with-filename', '--line-number', '--column', '--smart-case'}
- )
- set("use_less", true)
- set("color_devicons", true)
-
- set("set_env", nil)
-
- -- TODO: Add motions to keybindings
-
- -- To disable a keymap, put [map] = false
- -- So, to not map "<C-n>", just put
- --
- -- ...,
- -- ["<C-n>"] = false,
- -- ...,
- --
- -- Into your config.
- --
- -- Otherwise, just set the mapping to the function that you want it to be.
- --
- -- ...,
- -- ["<C-i>"] = actions.select_default
- -- ...,
- --
- set("mappings", {})
- set("default_mappings", nil)
-
- set("generic_sorter", sorters.get_generic_fuzzy_sorter)
- set("prefilter_sorter", sorters.prefilter)
- set("file_sorter", sorters.get_fuzzy_file)
-
- set("file_ignore_patterns", nil)
-
- set("dynamic_preview_title", false, [[
+ get_status_text = {
+ function(self)
+ local xx = (self.stats.processed or 0) - (self.stats.filtered or 0)
+ local yy = self.stats.processed or 0
+ if xx == 0 and yy == 0 then
+ return ""
+ end
+
+ return string.format("%s / %s", xx, yy)
+ end,
+ },
+
+ dynamic_preview_title = {
+ false,
+ [[
Will change the title of the preview window dynamically, where it
is supported. Means the preview window will for example show the
full filename.
Default: false
- ]])
+ ]],
+ },
+
+ -- Builtin configuration
+
+ -- List that will be executed.
+ -- Last argument will be the search term (passed in during execution)
+ vimgrep_arguments = {
+ { "rg", "--color=never", "--no-heading", "--with-filename", "--line-number", "--column", "--smart-case" },
+ },
+
+ use_less = { true },
+
+ color_devicons = { true },
+
+ set_env = { nil },
+
+ mappings = {
+ {}, [[
+ Your mappings to override telescope's default mappings.
+
+ Format is:
+ {
+ mode = { ..keys }
+ }
+
+ where {mode} is the one character letter for a mode
+ ('i' for insert, 'n' for normal).
+
+ For example:
+
+ mappings = {
+ i = {
+ ["<esc>"] = require('telescope.actions').close,
+ },
+ }
+
+
+ To disable a keymap, put [map] = false
+ So, to not map "<C-n>", just put
+
+ ...,
+ ["<C-n>"] = false,
+ ...,
+
+ Into your config.
+
+
+ otherwise, just set the mapping to the function that you want it to be.
+
+ ...,
+ ["<C-i>"] = require('telescope.actions').select_default,
+ ...,
+
+ If the function you want is part of `telescope.actions`, then you can simply give a string.
+ For example, the previous option is equivalent to:
+
+ ...,
+ ["<C-i>"] = "select_default",
+ ...,
+
+ You can also add other mappings using tables with `type = "command"`.
+ For example:
+
+ ...,
+ ["jj"] = { "<esc>", type = "command" },
+ ["kk"] = { "<cmd>echo \"Hello, World!\"<cr>", type = "command" },)
+ ...,
+ ]],
+ },
+
+ default_mappings = {
+ nil,
+ [[
+ Not recommended to use except for advanced users.
+
+ Will allow you to completely remove all of telescope's default maps
+ and use your own.
+ ]],
+ },
+
+ generic_sorter = { sorters.get_generic_fuzzy_sorter },
+ prefilter_sorter = { sorters.prefilter },
+ file_sorter = { sorters.get_fuzzy_file },
+
+ file_ignore_patterns = { nil },
+
+ file_previewer = {
+ function(...)
+ return require("telescope.previewers").vim_buffer_cat.new(...)
+ end,
+ },
+ grep_previewer = {
+ function(...)
+ return require("telescope.previewers").vim_buffer_vimgrep.new(...)
+ end,
+ },
+ qflist_previewer = {
+ function(...)
+ return require("telescope.previewers").vim_buffer_qflist.new(...)
+ end,
+ },
+ buffer_previewer_maker = {
+ function(...)
+ return require("telescope.previewers").buffer_previewer_maker(...)
+ end,
+ },
+}
+
+-- @param user_defaults table: a table where keys are the names of options,
+-- and values are the ones the user wants
+-- @param tele_defaults table: (optional) a table containing all of the defaults
+-- for telescope [defaults to `telescope_defaults`]
+function config.set_defaults(user_defaults, tele_defaults)
+ user_defaults = if_nil(user_defaults, {})
+ tele_defaults = if_nil(tele_defaults, telescope_defaults)
+
+ -- Check if using layout keywords outside of `layout_config`
+ deprecated.picker_window_options(user_defaults)
+
+ -- Check if using `layout_defaults` instead of `layout_config`
+ user_defaults = deprecated.layout_configuration(user_defaults)
+
+ local function get(name, default_val)
+ if name == "layout_config" then
+ return smarter_depth_2_extend(
+ if_nil(user_defaults[name], {}),
+ vim.tbl_deep_extend("keep", if_nil(config.values[name], {}), if_nil(default_val, {}))
+ )
+ end
+ return first_non_null(user_defaults[name], config.values[name], default_val)
+ end
+
+ local function set(name, default_val, description)
+ -- TODO(doc): Once we have descriptions for all of these, then we can add this back in.
+ -- assert(description, "Config values must always have a description")
+
+ config.values[name] = get(name, default_val)
+ if description then
+ config.descriptions[name] = strings.dedent(description)
+ end
+ end
- set("file_previewer", function(...) return require('telescope.previewers').vim_buffer_cat.new(...) end)
- set("grep_previewer", function(...) return require('telescope.previewers').vim_buffer_vimgrep.new(...) end)
- set("qflist_previewer", function(...) return require('telescope.previewers').vim_buffer_qflist.new(...) end)
- set("buffer_previewer_maker", function(...) return require('telescope.previewers').buffer_previewer_maker(...) end)
+ for key, info in pairs(tele_defaults) do
+ set(key, info[1], info[2])
+ end
+
+ local M = {}
+ M.get = get
+ return M
end
function config.clear_defaults()
@@ -169,5 +351,4 @@ end
config.set_defaults()
-
return config
diff --git a/lua/telescope/config/resolve.lua b/lua/telescope/config/resolve.lua
index 464bffb..6881830 100644
--- a/lua/telescope/config/resolve.lua
+++ b/lua/telescope/config/resolve.lua
@@ -1,3 +1,8 @@
+---@tag telescope.resolve
+
+---@brief [[
+--- Provides "resolver functions" to allow more customisable inputs for options.
+---@brief ]]
--[[
@@ -40,7 +45,7 @@ height =
3. function(picker, columns, lines)
-> returns one of the above options
- return max.min(110, max_rows * .5)
+ return math.min(110, max_rows * .5)
if columns > 120 then
return 110
@@ -88,46 +93,80 @@ That's the next step to scrolling.
local get_default = require('telescope.utils').get_default
local resolver = {}
+local _resolve_map = {}
-local _resolve_map = {
- -- Booleans
- [function(val) return val == false end] = function(selector, val)
- return function(...)
- return val
- end
- end,
+-- Booleans
+_resolve_map[function(val) return val == false end] = function(_, val)
+ return function(...)
+ return val
+ end
+end
- -- Percentages
- [function(val) return type(val) == 'number' and val >= 0 and val < 1 end] = function(selector, val)
- return function(...)
- local selected = select(selector, ...)
- return math.floor(val * selected)
- end
- end,
+-- Percentages
+_resolve_map[function(val) return type(val) == 'number' and val >= 0 and val < 1 end] = function(selector, val)
+ return function(...)
+ local selected = select(selector, ...)
+ return math.floor(val * selected)
+ end
+end
- -- Numbers
- [function(val) return type(val) == 'number' and val >= 1 end] = function(selector, val)
- return function(...)
- local selected = select(selector, ...)
- return math.min(val, selected)
- end
- end,
+-- Numbers
+_resolve_map[function(val) return type(val) == 'number' and val >= 1 end] = function(selector, val)
+ return function(...)
+ local selected = select(selector, ...)
+ return math.min(val, selected)
+ end
+end
+-- Tables TODO:
+-- ... {70, max}
- -- Tables TODO:
- -- ... {70, max}
+-- function:
+-- Function must have same signature as get_window_layout
+-- function(self, max_columns, max_lines): number
+--
+-- Resulting number is used for this configuration value.
+_resolve_map[function(val) return type(val) == 'function' end] = function(_, val)
+ return val
+end
+-- Add padding option
+_resolve_map[function(val) return type(val) == 'table' and val['padding'] ~= nil end] = function(selector, val)
+ local resolve_pad = function(value)
+ for k, v in pairs(_resolve_map) do
+ if k(value) then
+ return v(selector, value)
+ end
+ end
+
+ error('invalid configuration option for padding:' .. tostring(value))
+ end
+
+ return function(...)
+ local selected = select(selector, ...)
+ local padding = resolve_pad(val['padding'])
+ return math.floor(selected - 2 * padding(...))
+ end
+end
- -- function:
- -- Function must have same signature as get_window_layout
- -- function(self, max_columns, max_lines): number
- --
- -- Resulting number is used for this configuration value.
- [function(val) return type(val) == 'function' end] = function(selector, val)
- return val
- end,
-}
+--- Converts input to a function that returns the height.
+--- The input must take one of four forms:
+--- 1. 0 <= number < 1 <br>
+--- This means total height as a percentage.
+--- 2. 1 <= number <br>
+--- This means total height as a fixed number.
+--- 3. function <br>
+--- Must have signature:
+--- function(self, max_columns, max_lines): number
+--- 4. table of the form: {padding = `foo`} <br>
+--- where `foo` has one of the previous three forms. <br>
+--- The height is then set to be the remaining space after padding.
+--- For example, if the window has height 50, and the input is {padding = 5},
+--- the height returned will be `40 = 50 - 2*5`
+---
+--- The returned function will have signature:
+--- function(self, max_columns, max_lines): number
resolver.resolve_height = function(val)
for k, v in pairs(_resolve_map) do
if k(val) then
@@ -138,6 +177,23 @@ resolver.resolve_height = function(val)
error('invalid configuration option for height:' .. tostring(val))
end
+--- Converts input to a function that returns the width.
+--- The input must take one of four forms:
+--- 1. 0 <= number < 1 <br>
+--- This means total width as a percentage.
+--- 2. 1 <= number <br>
+--- This means total width as a fixed number.
+--- 3. function <br>
+--- Must have signature:
+--- function(self, max_columns, max_lines): number
+--- 4. table of the form: {padding = `foo`} <br>
+--- where `foo` has one of the previous three forms. <br>
+--- The width is then set to be the remaining space after padding.
+--- For example, if the window has width 100, and the input is {padding = 5},
+--- the width returned will be `90 = 100 - 2*5`
+---
+--- The returned function will have signature:
+--- function(self, max_columns, max_lines): number
resolver.resolve_width = function(val)
for k, v in pairs(_resolve_map) do
if k(val) then
@@ -148,8 +204,8 @@ resolver.resolve_width = function(val)
error('invalid configuration option for width:' .. tostring(val))
end
---- Win option always returns a table with preview, results, and prompt.
---- It handles many different ways. Some examples are as follows:
+-- Win option always returns a table with preview, results, and prompt.
+-- It handles many different ways. Some examples are as follows:
--
-- -- Disable
-- borderschars = false
diff --git a/lua/telescope/deprecated.lua b/lua/telescope/deprecated.lua
new file mode 100644
index 0000000..0d98c1d
--- /dev/null
+++ b/lua/telescope/deprecated.lua
@@ -0,0 +1,59 @@
+local log = require "telescope.log"
+
+local deprecated = {}
+
+deprecated.picker_window_options = function(opts)
+ local messages = {}
+
+ -- Deprecated: PR:922, 2021/06/25
+ -- Can be removed in a few weeks.
+
+ if opts.width then
+ table.insert(messages, "'opts.width' is no longer valid. Please use 'layout_config.width' instead")
+ end
+
+ if opts.height then
+ table.insert(messages, "'opts.height' is no longer valid. Please use 'layout_config.height' instead")
+ end
+
+ if opts.results_height then
+ table.insert(messages, "'opts.results_height' is no longer valid. Please see ':help telescope.changelog-922'")
+ end
+
+ if opts.results_width then
+ table.insert(messages,
+ "'opts.results_width' actually didn't do anything. Please see ':help telescope.changelog-922'"
+ )
+ end
+
+ if opts.prompt_position then
+ table.insert(messages,
+ "'opts.prompt_position' is no longer valid. Please use 'layout_config.prompt_position' instead."
+ )
+ end
+
+ if opts.preview_cutoff then
+ table.insert(messages,
+ "'opts.preview_cutoff' is no longer valid. Please use 'layout_config.preview_cutoff' instead."
+ )
+ end
+
+ if #messages > 0 then
+ table.insert(messages, 1, "Deprecated window options. Please see ':help telescope.changelog'")
+ vim.api.nvim_err_write(table.concat(messages, "\n \n ") .. "\n \nPress <Enter> to continue\n")
+ end
+end
+
+deprecated.layout_configuration = function(user_defaults)
+ if user_defaults.layout_defaults then
+ if user_defaults.layout_config == nil then
+ log.warn "Using 'layout_defaults' in setup() is deprecated. Use 'layout_config' instead."
+ user_defaults.layout_config = user_defaults.layout_defaults
+ else
+ error "Using 'layout_defaults' in setup() is deprecated. Remove this key and use 'layout_config' instead."
+ end
+ end
+ return user_defaults
+end
+
+return deprecated
diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua
index 96be07f..c451805 100644
--- a/lua/telescope/make_entry.lua
+++ b/lua/telescope/make_entry.lua
@@ -249,6 +249,8 @@ function make_entry.gen_from_git_stash()
end
function make_entry.gen_from_git_commits(opts)
+ opts = opts or {}
+
local displayer = entry_display.create {
separator = " ",
items = {
diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua
index a0e443a..28046e5 100644
--- a/lua/telescope/pickers.lua
+++ b/lua/telescope/pickers.lua
@@ -14,6 +14,7 @@ local actions = require('telescope.actions')
local action_set = require('telescope.actions.set')
local config = require('telescope.config')
local debounce = require('telescope.debounce')
+local deprecated = require('telescope.deprecated')
local log = require('telescope.log')
local mappings = require('telescope.mappings')
local state = require('telescope.state')
@@ -56,6 +57,8 @@ function Picker:new(opts)
actions._clear()
action_set._clear()
+ deprecated.picker_window_options(opts)
+
local layout_strategy = get_default(opts.layout_strategy, config.values.layout_strategy)
local obj = setmetatable({
@@ -96,33 +99,13 @@ function Picker:new(opts)
selection_strategy = get_default(opts.selection_strategy, config.values.selection_strategy),
layout_strategy = layout_strategy,
- layout_config = get_default(
- opts.layout_config,
- (config.values.layout_defaults or {})[layout_strategy]
- ) or {},
+ layout_config = vim.tbl_deep_extend("keep", opts.layout_config or {}, config.values.layout_config or {}),
window = {
- -- TODO: This won't account for different layouts...
- -- TODO: If it's between 0 and 1, it's a percetnage.
- -- TODO: If its's a single number, it's always that many columsn
- -- TODO: If it's a list, of length 2, then it's a range of min to max?
- height = get_default(opts.height, 0.8),
- width = get_default(opts.width, config.values.width),
-
- get_preview_width = get_default(opts.preview_width, config.values.get_preview_width),
-
- results_width = get_default(opts.results_width, config.values.results_width),
- results_height = get_default(opts.results_height, config.values.results_height),
-
winblend = get_default(opts.winblend, config.values.winblend),
- prompt_position = get_default(opts.prompt_position, config.values.prompt_position),
-
- -- Border config
border = get_default(opts.border, config.values.border),
borderchars = get_default(opts.borderchars, config.values.borderchars),
},
-
- preview_cutoff = get_default(opts.preview_cutoff, config.values.preview_cutoff),
}, self)
obj.get_window_options = opts.get_window_options or p_window.get_window_options
diff --git a/lua/telescope/pickers/layout_strategies.lua b/lua/telescope/pickers/layout_strategies.lua
index 21994b3..56100c3 100644
--- a/lua/telescope/pickers/layout_strategies.lua
+++ b/lua/telescope/pickers/layout_strategies.lua
@@ -7,7 +7,7 @@
--- All layout strategies are functions with the following signature:
---
--- <pre>
---- function(picker, columns, lines)
+--- function(picker, columns, lines, layout_config)
--- -- Do some calculations here...
--- return {
--- preview = preview_configuration
@@ -17,136 +17,257 @@
--- end
---
--- Parameters: ~
---- - picker : A Picker object. (docs coming soon)
---- - columns : number Columns in the vim window
---- - lines : number Lines in the vim window
+--- - picker : A Picker object. (docs coming soon)
+--- - columns : (number) Columns in the vim window
+--- - lines : (number) Lines in the vim window
+--- - layout_config : (table) The configuration values specific to the picker.
---
--- </pre>
---
---- TODO: I would like to make these link to `telescope.layout_strategies.*`,
---- but it's not yet possible.
+--- This means you can create your own layout strategy if you want! Just be aware
+--- for now that we may change some APIs or interfaces, so they may break if you create
+--- your own.
---
---- Available layout strategies include:
---- - horizontal:
---- - See |layout_strategies.horizontal|
+--- A good method for creating your own would be to copy one of the strategies that most
+--- resembles what you want from "./lua/telescope/pickers/layout_strategies.lua" in the
+--- telescope repo.
---
---- - vertical:
---- - See |layout_strategies.vertical|
----
---- - flex:
---- - See |layout_strategies.flex|
----
---- Available tweaks to the settings in layout defaults include
---- (can be applied to horizontal and vertical layouts):
---- - mirror (default is `false`):
---- - Flip the view of the current layout:
---- - If using horizontal: if `true`, swaps the location of the
---- results/prompt window and preview window
---- - If using vertical: if `true`, swaps the location of the results and
---- prompt windows
----
---- - width_padding:
---- - How many cells to pad the width of Telescope's layout window
----
---- - height_padding:
---- - How many cells to pad the height of Telescope's layout window
----
---- - preview_width:
---- - Change the width of Telescope's preview window
----
---- - scroll_speed:
---- - Change the scrolling speed of the previewer
---@brief ]]
-local config = require('telescope.config')
-local resolve = require("telescope.config.resolve")
-
+local resolve = require('telescope.config.resolve')
local p_window = require('telescope.pickers.window')
+local if_nil = vim.F.if_nil
+
+local get_border_size = function(opts)
+ if opts.window.border == false then
+ return 0
+ end
+
+ return 1
+end
+
+local layout_strategies = {}
+layout_strategies._configurations = {}
+
+--@param strategy_config table: table with keys for each option for a strategy
+--@return table: table with keys for each option (for this strategy) and with keys for each layout_strategy
+local get_valid_configuration_keys = function(strategy_config)
+ local valid_configuration_keys = {
+ -- TEMP: There are a few keys we should say are valid to start with.
+ preview_cutoff = true,
+ prompt_position = true,
+ }
+
+ for key in pairs(strategy_config) do
+ valid_configuration_keys[key] = true
+ end
+ for name in pairs(layout_strategies) do
+ valid_configuration_keys[name] = true
+ end
--- Check if there are any borders. Right now it's a little raw as
--- there are a few things that contribute to the border
-local is_borderless = function(opts)
- return opts.window.border == false
+ return valid_configuration_keys
end
+--@param strategy_name string: the name of the layout_strategy we are validating for
+--@param configuration table: table with keys for each option available
+--@param values table: table containing all of the non-default options we want to set
+--@param default_layout_config table: table with the default values to configure layouts
+--@return table: table containing the combined options (defaults and non-defaults)
+local function validate_layout_config(strategy_name, configuration, values, default_layout_config)
+ assert(strategy_name, "It is required to have a strategy name for validation.")
+ local valid_configuration_keys = get_valid_configuration_keys(configuration)
+
+ -- If no default_layout_config provided, check Telescope's config values
+ default_layout_config = if_nil(default_layout_config, require('telescope.config').values.layout_config)
+
+ local result = {}
+ local get_value = function(k)
+ -- skip "private" items
+ if string.sub(k, 1, 1) == "_" then return end
+
+ local val
+ -- Prioritise options that are specific to this strategy
+ if values[strategy_name] ~= nil and values[strategy_name][k] ~= nil then
+ val = values[strategy_name][k]
+ end
+
+ -- Handle nested layout config values
+ if layout_strategies[k]
+ and strategy_name ~= k
+ and type(val) == 'table' then
+ val = vim.tbl_deep_extend("force", default_layout_config[k], val)
+ end
+
+ if val == nil and values[k] ~= nil then
+ val = values[k]
+ end
+
+ if val == nil then
+ if default_layout_config[strategy_name] ~= nil
+ and default_layout_config[strategy_name][k] ~= nil then
+ val = default_layout_config[strategy_name][k]
+ else
+ val = default_layout_config[k]
+ end
+ end
+
+ return val
+ end
-local function validate_layout_config(options, values)
- for k, _ in pairs(options) do
- if not values[k] then
- error(string.format(
- "Unsupported layout_config key: %s\n%s",
- k,
- vim.inspect(values)
+ -- Always set the values passed first.
+ for k in pairs(values) do
+ if not valid_configuration_keys[k] then
+ -- TODO: At some point we'll move to error here,
+ -- but it's a bit annoying to just straight up crash everyone's stuff.
+ vim.api.nvim_err_writeln(string.format(
+ "Unsupported layout_config key for the %s strategy: %s\n%s",
+ strategy_name, k, vim.inspect(values)
))
end
+
+ result[k] = get_value(k)
+ end
+
+ -- And then set other valid keys via "inheritance" style extension
+ for k in pairs(valid_configuration_keys) do
+ if result[k] == nil then
+ result[k] = get_value(k)
+ end
end
- return options
+ return result
end
-local layout_strategies = {}
+-- List of options that are shared by more than one layout.
+local shared_options = {
+ width = { "How wide to make Telescope's entire layout", "See |resolver.resolve_width()|" },
+ height = { "How tall to make Telescope's entire layout", "See |resolver.resolve_height()|" },
+ mirror = "Flip the location of the results/prompt and preview windows",
+ scroll_speed = "The number of lines to scroll through the previewer",
+}
+
+-- Used for generating vim help documentation.
+layout_strategies._format = function(name)
+ local strategy_config = layout_strategies._configurations[name]
+ if vim.tbl_isempty(strategy_config) then
+ return {}
+ end
---- Horizontal previewer
+ local results = {"<pre>", "`picker.layout_config` shared options:"}
+
+ local strategy_keys = vim.tbl_keys(strategy_config)
+ table.sort(strategy_keys, function(a, b)
+ return a < b
+ end)
+
+ local add_value = function(k, val)
+ if type(val) == 'string' then
+ table.insert(results, string.format(' - %s: %s', k, val))
+ elseif type(val) == 'table' then
+ table.insert(results, string.format(' - %s:', k))
+ for _, line in ipairs(val) do
+ table.insert(results, string.format(' - %s', line))
+ end
+ else
+ error("Unknown type:" .. type(val))
+ end
+ end
+
+ for _, k in ipairs(strategy_keys) do
+ if shared_options[k] then
+ add_value(k, strategy_config[k])
+ end
+ end
+
+ table.insert(results, "")
+ table.insert(results, "`picker.layout_config` unique options:")
+
+ for _, k in ipairs(strategy_keys) do
+ if not shared_options[k] then
+ add_value(k, strategy_config[k])
+ end
+ end
+
+ table.insert(results, "</pre>")
+ return results
+end
+
+--@param name string: the name to be assigned to the layout
+--@param layout_config table: table where keys are the available options for the layout
+--@param layout function: function with signature
+-- function(self, max_columns, max_lines, layout_config): table
+-- the returned table is the sizing and location information for the parts of the picker
+--@retun function: wrapped function that inputs a validated layout_config into the `layout` function
+local function make_documented_layout(name, layout_config, layout)
+ -- Save configuration data to be used by documentation
+ layout_strategies._configurations[name] = layout_config
+
+ -- Return new function that always validates configuration
+ return function(self, max_columns, max_lines, override_layout)
+ return layout(
+ self,
+ max_columns,
+ max_lines,
+ validate_layout_config(
+ name, layout_config, vim.tbl_deep_extend("keep", if_nil(override_layout, {}), if_nil(self.layout_config, {}))
+ )
+ )
+ end
+end
+
+
+--- Horizontal layout has two columns, one for the preview
+--- and one for the prompt and results.
---
--- <pre>
---- +-------------+--------------+
---- | | |
---- | Results | |
---- | | Preview |
---- | | |
---- +-------------| |
---- | Prompt | |
---- +-------------+--------------+
+--- ┌──────────────────────────────────────────────────┐
+--- │ │
+--- │ ┌───────────────────┐┌───────────────────┐ │
+--- │ │ ││ │ │
+--- │ │ ││ │ │
+--- │ │ ││ │ │
+--- │ │ Results ││ │ │
+--- │ │ ││ Preview │ │
+--- │ │ ││ │ │
+--- │ │ ││ │ │
+--- │ └───────────────────┘│ │ │
+--- │ ┌───────────────────┐│ │ │
+--- │ │ Prompt ││ │ │
+--- │ └───────────────────┘└───────────────────┘ │
+--- │ │
+--- └──────────────────────────────────────────────────┘
--- </pre>
-layout_strategies.horizontal = function(self, max_columns, max_lines)
- local layout_config = validate_layout_config(self.layout_config or {}, {
- width_padding = "How many cells to pad the width",
- height_padding = "How many cells to pad the height",
- preview_width = "(Resolvable): Determine preview width",
- mirror = "Flip the location of the results/prompt and preview windows",
- scroll_speed = "The speed when scrolling through the previewer",
- })
-
+---@eval { ["description"] = require('telescope.pickers.layout_strategies')._format("horizontal") }
+---
+layout_strategies.horizontal = make_documented_layout('horizontal', vim.tbl_extend("error", shared_options, {
+ preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|", },
+ preview_cutoff = "When columns are less than this value, the preview will be disabled",
+ prompt_position = { "Where to place prompt window.", "Available Values: 'bottom', 'top'" },
+}), function(self, max_columns, max_lines, layout_config)
local initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview
local results = initial_options.results
local prompt = initial_options.prompt
- -- TODO: Test with 120 width terminal
- -- TODO: Test with self.width
- local width_padding = resolve.resolve_width(layout_config.width_padding or function(_, cols)
- if cols < self.preview_cutoff then
- return 2
- elseif cols < 150 then
- return 5
- else
- return 10
- end
- end)(self, max_columns, max_lines)
- local picker_width = max_columns - 2 * width_padding
+ local width_opt = layout_config.width
+ local picker_width = resolve.resolve_width(width_opt)(self, max_columns, max_lines)
+ local width_padding = math.floor((max_columns - picker_width)/2)
- local height_padding = resolve.resolve_height(layout_config.height_padding or function(_, _, lines)
- if lines < 40 then
- return 4
- else
- return math.floor(0.1 * lines)
- end
- end)(self, max_columns, max_lines)
- local picker_height = max_lines - 2 * height_padding
+ local height_opt = layout_config.height
+ local picker_height = resolve.resolve_height(height_opt)(self, max_columns, max_lines)
+ local height_padding = math.floor((max_lines - picker_height)/2)
- if self.previewer then
- preview.width = resolve.resolve_width(layout_config.preview_width or function(_, cols)
- if not self.previewer or cols < self.preview_cutoff then
- return 0
- elseif cols < 150 then
+ if self.previewer and max_columns >= layout_config.preview_cutoff then
+ preview.width = resolve.resolve_width(if_nil(layout_config.preview_width, function(_, cols)
+ if cols < 150 then
return math.floor(cols * 0.4)
elseif cols < 200 then
return 80
else
return 120
end
- end)(self, picker_width, max_lines)
+ end))(self, picker_width, max_lines)
else
preview.width = 0
end
@@ -175,14 +296,14 @@ layout_strategies.horizontal = function(self, max_columns, max_lines)
end
preview.line = height_padding
- if self.window.prompt_position == "top" then
+ if layout_config.prompt_position == "top" then
prompt.line = height_padding
results.line = prompt.line + prompt.height + 2
- elseif self.window.prompt_position == "bottom" then
+ elseif layout_config.prompt_position == "bottom" then
results.line = height_padding
prompt.line = results.line + results.height + 2
else
- error("Unknown prompt_position: " .. self.window.prompt_position)
+ error("Unknown prompt_position: " .. tostring(self.window.prompt_position) .. "\n" .. vim.inspect(layout_config))
end
return {
@@ -190,33 +311,54 @@ layout_strategies.horizontal = function(self, max_columns, max_lines)
results = results,
prompt = prompt
}
-end
+end)
---- Centered layout wih smaller default sizes (I think)
+--- Centered layout with a combined block of the prompt
+--- and results aligned to the middle of the screen.
+--- The preview window is then placed in the remaining space above.
+--- Particularly useful for creating dropdown menus
+--- (see |telescope.themes| and |themes.get_dropdown()|`).
---
--- <pre>
---- +--------------+
---- | Preview |
---- +--------------+
---- | Prompt |
---- +--------------+
---- | Result |
---- | Result |
---- | Result |
---- +--------------+
+--- ┌──────────────────────────────────────────────────┐
+--- │ ┌────────────────────────────────────────┐ │
+--- │ | Preview | │
+--- │ | Preview | │
+--- │ └────────────────────────────────────────┘ │
+--- │ ┌────────────────────────────────────────┐ │
+--- │ | Prompt | │
+--- │ ├────────────────────────────────────────┤ │
+--- │ | Result | │
+--- │ | Result | │
+--- │ └────────────────────────────────────────┘ │
+--- │ │
+--- │ │
+--- │ │
+--- │ │
+--- └──────────────────────────────────────────────────┘
--- </pre>
-layout_strategies.center = function(self, columns, lines)
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("center") }
+---
+layout_strategies.center = make_documented_layout("center", vim.tbl_extend("error", shared_options, {
+ preview_cutoff = "When lines are less than this value, the preview will be disabled",
+}), function(self, max_columns, max_lines,layout_config)
local initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview
local results = initial_options.results
local prompt = initial_options.prompt
- -- This sets the height/width for the whole layout
- local height = resolve.resolve_height(self.window.results_height)(self, columns, lines)
- local width = resolve.resolve_width(self.window.width)(self, columns, lines)
+ -- This sets the width for the whole layout
+ local width_opt = layout_config.width
+ local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines)
+
+ -- This sets the number of results displayed
+ local res_height_opt = layout_config.height
+ local res_height = resolve.resolve_height(res_height_opt)(self, max_columns, max_lines)
- local max_results = (height > lines and lines or height)
- local max_width = (width > columns and columns or width)
+ local max_results = (res_height > max_lines and max_lines or res_height)
+ local max_width = (width > max_columns and max_columns or width)
+
+ local bs = get_border_size(self)
prompt.height = 1
results.height = max_results
@@ -225,87 +367,84 @@ layout_strategies.center = function(self, columns, lines)
results.width = max_width
preview.width = max_width
- -- border size
- local bs = 1
- if is_borderless(self) then
- bs = 0
- end
-
- prompt.line = (lines / 2) - ((max_results + (bs * 2)) / 2)
+ -- Align the prompt and results so halfway up the screen is
+ -- in the middle of this combined block
+ prompt.line = (max_lines / 2) - ((max_results + (bs * 2)) / 2)
results.line = prompt.line + 1 + (bs)
preview.line = 1
- preview.height = math.floor(prompt.line - (2 + bs))
- if not self.previewer or columns < self.preview_cutoff then
+ if self.previewer and max_lines >= layout_config.preview_cutoff then
+ preview.height = math.floor(prompt.line - (2 + bs))
+ else
preview.height = 0
end
- results.col = math.ceil((columns / 2) - (width / 2) - bs)
+ results.col = math.ceil((max_columns / 2) - (width / 2) - bs)
prompt.col = results.col
preview.col = results.col
return {
- preview = self.previewer and preview.width > 0 and preview,
+ preview = self.previewer and preview.height > 0 and preview,
results = results,
prompt = prompt
}
-end
+end)
---- Vertical perviewer stacks the items on top of each other.
+--- Vertical layout stacks the items on top of each other.
+--- Particularly useful with thinner windows.
---
--- <pre>
---- +-----------------+
---- | Previewer |
---- | Previewer |
---- | Previewer |
---- +-----------------+
---- | Result |
---- | Result |
---- | Result |
---- +-----------------+
---- | Prompt |
---- +-----------------+
+--- ┌──────────────────────────────────────────────────┐
+--- │ │
+--- │ ┌────────────────────────────────────────┐ │
+--- │ | Preview | │
+--- │ | Preview | │
+--- │ | Preview | │
+--- │ └────────────────────────────────────────┘ │
+--- │ ┌────────────────────────────────────────┐ │
+--- │ | Result | │
+--- │ | Result | │
+--- │ └────────────────────────────────────────┘ │
+--- │ ┌────────────────────────────────────────┐ │
+--- │ | Prompt | │
+--- │ └────────────────────────────────────────┘ │
+--- │ │
+--- └──────────────────────────────────────────────────┘
--- </pre>
-layout_strategies.vertical = function(self, max_columns, max_lines)
- local layout_config = validate_layout_config(self.layout_config or {}, {
- width_padding = "How many cells to pad the width",
- height_padding = "How many cells to pad the height",
- preview_height = "(Resolvable): Determine preview height",
- mirror = "Flip the locations of the results and prompt windows",
- scroll_speed = "The speed when scrolling through the previewer",
- })
-
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("vertical") }
+---
+layout_strategies.vertical = make_documented_layout("vertical", vim.tbl_extend("error", shared_options, {
+ preview_cutoff = "When lines are less than this value, the preview will be disabled",
+ preview_height = { "Change the height of Telescope's preview window", "See |resolver.resolve_height()|" },
+ prompt_position = { "(unimplemented, but we plan on supporting)" },
+}), function(self, max_columns, max_lines, layout_config)
local initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview
local results = initial_options.results
local prompt = initial_options.prompt
- local width_padding = resolve.resolve_width(
- layout_config.width_padding or math.ceil((1 - self.window.width) * 0.5 * max_columns)
- )(self, max_columns, max_lines)
+ local width_opt = layout_config.width
+ local picker_width = resolve.resolve_width(width_opt)(self,max_columns,max_lines)
+ local width_padding = math.floor((max_columns - picker_width)/2)
- local width = max_columns - width_padding * 2
- if not self.previewer then
- preview.width = 0
+ local height_opt = layout_config.height
+ local picker_height = resolve.resolve_height(height_opt)(self,max_columns,max_lines)
+ local height_padding = math.floor((max_lines - picker_height)/2)
+
+ if self.previewer and max_lines >= layout_config.preview_cutoff then
+ preview.width = picker_width
else
- preview.width = width
+ preview.width = 0
end
- results.width = width
- prompt.width = width
-
- -- Height
- local height_padding = math.max(
- 1,
- resolve.resolve_height(layout_config.height_padding or 3)(self, max_columns, max_lines)
- )
- local picker_height = max_lines - 2 * height_padding
+ results.width = picker_width
+ prompt.width = picker_width
local preview_total = 0
preview.height = 0
- if self.previewer then
+ if self.previewer and max_lines >= layout_config.preview_cutoff then
preview.height = resolve.resolve_height(
- layout_config.preview_height or (max_lines - 15)
+ if_nil(layout_config.preview_height, 0.5)
)(self, max_columns, picker_height)
preview_total = preview.height + 2
@@ -332,36 +471,38 @@ layout_strategies.vertical = function(self, max_columns, max_lines)
end
return {
- preview = self.previewer and preview.width > 0 and preview,
+ preview = self.previewer and preview.height > 0 and preview,
results = results,
prompt = prompt
}
-end
+end)
---- Swap between `horizontal` and `vertical` strategies based on the window width
---- - Supports `vertical` or `horizontal` features
+--- Flex layout swaps between `horizontal` and `vertical` strategies based on the window width
+--- - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| features
---
---- Uses:
---- - flip_columns
---- - flip_lines
-layout_strategies.flex = function(self, max_columns, max_lines)
- local layout_config = self.layout_config or {}
-
- local flip_columns = layout_config.flip_columns or 100
- local flip_lines = layout_config.flip_lines or 20
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("flex") }
+---
+layout_strategies.flex = make_documented_layout('flex', vim.tbl_extend("error", shared_options, {
+ flip_columns = "The number of columns required to move to horizontal mode",
+ flip_lines = "The number of lines required to move to horizontal mode",
+ vertical = "Options to pass when switching to vertical layout",
+ horizontal = "Options to pass when switching to horizontal layout",
+}), function(self, max_columns, max_lines, layout_config)
+ local flip_columns = if_nil(layout_config.flip_columns, 100)
+ local flip_lines = if_nil(layout_config.flip_lines, 20)
if max_columns < flip_columns and max_lines > flip_lines then
- -- TODO: This feels a bit like a hack.... cause you wouldn't be able to pass this to flex easily.
- self.layout_config = (config.values.layout_defaults or {})['vertical']
- return layout_strategies.vertical(self, max_columns, max_lines)
+ return layout_strategies.vertical(self, max_columns, max_lines, layout_config.vertical)
else
- self.layout_config = (config.values.layout_defaults or {})['horizontal']
- return layout_strategies.horizontal(self, max_columns, max_lines)
+ return layout_strategies.horizontal(self, max_columns, max_lines, layout_config.horizontal)
end
-end
+end)
-layout_strategies.current_buffer = function(self, _, _)
- local initial_options = self:_get_initial_window_options()
+layout_strategies.current_buffer = make_documented_layout('current_buffer', {
+ -- No custom options.
+ -- height, width ignored
+}, function(self, _, _, _)
+ local initial_options = p_window.get_initial_window_options(self)
local window_width = vim.api.nvim_win_get_width(0)
local window_height = vim.api.nvim_win_get_height(0)
@@ -414,19 +555,20 @@ layout_strategies.current_buffer = function(self, _, _)
results = results,
prompt = prompt,
}
-end
-
-layout_strategies.bottom_pane = function(self, max_columns, max_lines)
- local layout_config = validate_layout_config(self.layout_config or {}, {
- height = "The height of the layout",
- })
+end)
+--- Bottom pane can be used to create layouts similar to "ivy".
+---
+--- For an easy ivy configuration, see |themes.get_ivy()|
+layout_strategies.bottom_pane = make_documented_layout('bottom_pane', vim.tbl_extend("error", shared_options, {
+ -- No custom options...
+}), function(self, max_columns, max_lines, layout_config)
local initial_options = p_window.get_initial_window_options(self)
local results = initial_options.results
local prompt = initial_options.prompt
local preview = initial_options.preview
- local result_height = layout_config.height or 25
+ local result_height = if_nil(resolve.resolve_height(layout_config.height)(self,max_columns,max_lines), 25)
local prompt_width = max_columns
local col = 0
@@ -477,6 +619,8 @@ layout_strategies.bottom_pane = function(self, max_columns, max_lines)
width = result_width,
}),
}
-end
+end)
+
+layout_strategies._validate_layout_config = validate_layout_config
return layout_strategies
diff --git a/lua/telescope/previewers/init.lua b/lua/telescope/previewers/init.lua
index 3f13dc3..ce73815 100644
--- a/lua/telescope/previewers/init.lua
+++ b/lua/telescope/previewers/init.lua
@@ -302,7 +302,6 @@ previewers.git_commit_message = buffer_previewer.git_commit_message
--- The run command is `git --no-pager diff $FILE`
previewers.git_file_diff = buffer_previewer.git_file_diff
-
previewers.ctags = buffer_previewer.ctags
previewers.builtin = buffer_previewer.builtin
previewers.help = buffer_previewer.help
diff --git a/lua/telescope/themes.lua b/lua/telescope/themes.lua
index f2af1a7..939f79a 100644
--- a/lua/telescope/themes.lua
+++ b/lua/telescope/themes.lua
@@ -27,16 +27,26 @@ function themes.get_dropdown(opts)
opts = opts or {}
local theme_opts = {
- -- WIP: Decide on keeping these names or not.
theme = "dropdown",
- sorting_strategy = "ascending",
- layout_strategy = "center",
results_title = false,
preview_title = "Preview",
- preview_cutoff = 1, -- Preview should always show (unless previewer = false)
- width = 80,
- results_height = 15,
+
+ sorting_strategy = "ascending",
+ layout_strategy = "center",
+ layout_config = {
+ preview_cutoff = 1, -- Preview should always show (unless previewer = false)
+
+ width = function(_, max_columns, _)
+ return math.min(max_columns - 3, 80)
+ end,
+
+ height = function(_, _, max_lines)
+ return math.min(max_lines - 4, 15)
+ end,
+ },
+
+ border = true,
borderchars = {
{ "─", "│", "─", "│", "╭", "╮", "╯", "╰"},
prompt = {"─", "│", " ", "│", "╭", "╮", "│", "│"},
diff --git a/lua/tests/automated/layout_strategies_spec.lua b/lua/tests/automated/layout_strategies_spec.lua
new file mode 100644
index 0000000..a4d2c50
--- /dev/null
+++ b/lua/tests/automated/layout_strategies_spec.lua
@@ -0,0 +1,92 @@
+-- local tester = require('telescope.pickers._test')
+local config = require('telescope.config')
+local resolve = require('telescope.config.resolve')
+local layout_strats = require('telescope.pickers.layout_strategies')
+
+local validate_layout_config = layout_strats._validate_layout_config
+
+local eq = assert.are.same
+
+describe('layout_strategies', function()
+ it('should have validator', function()
+ assert(validate_layout_config, "Has validator")
+ end)
+
+ local test_height = function(should, output, input, opts)
+ opts = opts or {}
+
+ local max_columns, max_lines = opts.max_columns or 100, opts.max_lines or 100
+ it(should, function()
+ local layout_config = validate_layout_config("horizontal", { height = true }, { height = input })
+
+ eq(output, resolve.resolve_height(layout_config.height)({}, max_columns, max_lines))
+ end)
+ end
+
+ test_height('should handle numbers', 10, 10)
+
+ test_height('should handle percentage: 100', 10, 0.1, { max_lines = 100 })
+ test_height('should handle percentage: 110', 11, 0.1, { max_lines = 110 })
+
+ test_height('should call functions: simple', 5, function() return 5 end)
+ test_height('should call functions: percentage', 15, function(_, _, lines) return 0.1 * lines end, { max_lines = 150 })
+
+ local test_defaults_key = function(should, key, strat, output, ours, theirs, override)
+ ours = ours or {}
+ theirs = theirs or {}
+ override = override or {}
+
+ it(should, function()
+ config.clear_defaults()
+ config.set_defaults({layout_config=theirs}, {layout_config={ours,'description'}})
+ local layout_config = validate_layout_config(strat, layout_strats._configurations[strat], override)
+ eq(output, layout_config[key])
+ end)
+ end
+
+ test_defaults_key("should use ours if theirs and override don't give the key",
+ 'height','horizontal',50,
+ {height=50}, {width=100}, {width=120}
+ )
+
+ test_defaults_key("should use ours if theirs and override don't give the key for this strategy",
+ 'height','horizontal',50,
+ {height=50}, {vertical={height=100}}, {vertical={height=120}}
+ )
+
+ test_defaults_key("should use theirs if override doesn't give the key",
+ 'height','horizontal',100,
+ {height=50}, {height=100}, {width=120}
+ )
+
+ test_defaults_key("should use override if key given",
+ 'height','horizontal',120,
+ {height=50}, {height=100}, {height=120}
+ )
+
+ test_defaults_key("should use override if key given for this strategy",
+ 'height','horizontal',120,
+ {height=50}, {height=100}, {horizontal={height=120}}
+ )
+
+ test_defaults_key("should use theirs if override doesn't give key (even if ours has strategy specific)",
+ 'height','horizontal',100,
+ {horizontal={height=50}}, {height=100}, {width=120}
+ )
+
+ test_defaults_key("should use override (even if ours has strategy specific)",
+ 'height','horizontal',120,
+ {horizontal={height=50}}, {height=100}, {height=120}
+ )
+
+ test_defaults_key("should use override (even if theirs has strategy specific)",
+ 'height','horizontal',120,
+ {height=50}, {horizontal={height=100}}, {height=120}
+ )
+
+ test_defaults_key("should use override (even if ours and theirs have strategy specific)",
+ 'height','horizontal',120,
+ {horizontal={height=50}}, {horizontal={height=100}}, {height=120}
+ )
+
+end)
diff --git a/lua/tests/automated/pickers/find_files_spec.lua b/lua/tests/automated/pickers/find_files_spec.lua
index b3af1d6..b01dc51 100644
--- a/lua/tests/automated/pickers/find_files_spec.lua
+++ b/lua/tests/automated/pickers/find_files_spec.lua
@@ -35,8 +35,11 @@ describe('builtin.find_files', function()
}, vim.tbl_extend("force", {
disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(),
- results_height = max_results,
layout_strategy = 'center',
+ layout_config = {
+ height = max_results,
+ width = 0.9,
+ },
}, vim.fn.json_decode([==[%s]==])))
]], vim.fn.json_encode(configuration)))
end)
@@ -57,8 +60,11 @@ describe('builtin.find_files', function()
}, vim.tbl_extend("force", {
disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(),
- results_height = 5,
layout_strategy = 'center',
+ layout_config = {
+ height = max_results,
+ width = 0.9,
+ },
}, vim.fn.json_decode([==[%s]==])))
]], expected, vim.fn.json_encode(configuration)))
end)