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>
This commit is contained in:
TJ DeVries
2021-07-01 02:41:58 -07:00
committed by GitHub
parent e5bd4963da
commit 5a53ec5c2f
16 changed files with 1300 additions and 529 deletions

View File

@@ -88,37 +88,5 @@ Plug 'nvim-telescope/telescope.nvim'
call plug#end() call plug#end()
autocmd VimEnter * PlugClean! | PlugUpdate --sync | close autocmd VimEnter * PlugClean! | PlugUpdate --sync | close
lua << EOF lua require('telescope').setup()
require('telescope').setup{
defaults = {
vimgrep_arguments = {
'rg',
'--color=never',
'--no-heading',
'--with-filename',
'--line-number',
'--column',
'--smart-case'
},
prompt_position = "bottom",
prompt_prefix = ">",
selection_strategy = "reset",
sorting_strategy = "descending",
layout_strategy = "horizontal",
layout_defaults = {},
file_ignore_patterns = {},
shorten_path = true,
winblend = 0,
width = 0.75,
preview_cutoff = 120,
results_height = 1,
results_width = 0.8,
border = {},
borderchars = { '─', '│', '─', '│', '╭', '╮', '╯', '╰'},
color_devicons = true,
use_less = true,
}
}
EOF
``` ```

View File

@@ -138,7 +138,6 @@ require('telescope').setup{
'--column', '--column',
'--smart-case' '--smart-case'
}, },
prompt_position = "bottom",
prompt_prefix = "> ", prompt_prefix = "> ",
selection_caret = "> ", selection_caret = "> ",
entry_prefix = " ", entry_prefix = " ",
@@ -146,7 +145,7 @@ require('telescope').setup{
selection_strategy = "reset", selection_strategy = "reset",
sorting_strategy = "descending", sorting_strategy = "descending",
layout_strategy = "horizontal", layout_strategy = "horizontal",
layout_defaults = { layout_config = {
horizontal = { horizontal = {
mirror = false, mirror = false,
}, },
@@ -159,10 +158,6 @@ require('telescope').setup{
generic_sorter = require'telescope.sorters'.get_generic_fuzzy_sorter, generic_sorter = require'telescope.sorters'.get_generic_fuzzy_sorter,
shorten_path = true, shorten_path = true,
winblend = 0, winblend = 0,
width = 0.75,
preview_cutoff = 120,
results_height = 1,
results_width = 0.8,
border = {}, border = {},
borderchars = { '─', '│', '─', '│', '╭', '╮', '╯', '╰' }, borderchars = { '─', '│', '─', '│', '╭', '╮', '╯', '╰' },
color_devicons = true, color_devicons = true,
@@ -196,24 +191,19 @@ EOF
| Keys | Description | Options | | Keys | Description | Options |
|------------------------|-------------------------------------------------------|----------------------------| |------------------------|-------------------------------------------------------|----------------------------|
| `prompt_position` | Where the prompt should be located. | top/bottom |
| `prompt_prefix` | What should the prompt prefix be. | string | | `prompt_prefix` | What should the prompt prefix be. | string |
| `selection_caret` | What should the selection caret be. | string | | `selection_caret` | What should the selection caret be. | string |
| `entry_prefix` | What should be shown in front of every entry. (current selection excluded) | string| | `entry_prefix` | What should be shown in front of every entry. (current selection excluded) | string|
| `initial_mode` | The initial mode when a prompt is opened. | insert/normal | | `initial_mode` | The initial mode when a prompt is opened. | insert/normal |
| `sorting_strategy` | Where first selection should be located. | descending/ascending |
| `layout_strategy` | How the telescope is drawn. | [supported layouts](https://github.com/nvim-telescope/telescope.nvim/wiki/Layouts) | | `layout_strategy` | How the telescope is drawn. | [supported layouts](https://github.com/nvim-telescope/telescope.nvim/wiki/Layouts) |
| `winblend` | How transparent is the telescope window should be. | NUM | | `layout_config` | Extra settings for fine-tuning how your layout looks | [supported settings](https://github.com/nvim-telescope/telescope.nvim/wiki/Layouts#layout-defaults) |
| `layout_defaults` | Extra settings for fine-tuning how your layout looks | [supported settings](https://github.com/nvim-telescope/telescope.nvim/wiki/Layouts#layout-defaults) | | `sorting_strategy` | Where first selection should be located. | descending/ascending |
| `width` | TODO | NUM | | `scroll_strategy` | How to behave when the when there are no more item next/prev | cycle, nil |
| `preview_cutoff` | TODO | NUM | | `winblend` | How transparent is the telescope window should be. | number |
| `results_height` | TODO | NUM |
| `results_width` | TODO | NUM |
| `borderchars` | The border chars, it gives border telescope window | dict | | `borderchars` | The border chars, it gives border telescope window | dict |
| `color_devicons` | Whether to color devicons or not | boolean | | `color_devicons` | Whether to color devicons or not | boolean |
| `use_less` | Whether to use less with bat or less/cat if bat not installed | boolean | | `use_less` | Whether to use less with bat or less/cat if bat not installed | boolean |
| `set_env` | Set environment variables for previewer | dict | | `set_env` | Set environment variables for previewer | dict |
| `scroll_strategy` | How to behave when the when there are no more item next/prev | cycle, nil |
| `file_previewer` | What telescope previewer to use for files. | [Previewers](#previewers) | | `file_previewer` | What telescope previewer to use for files. | [Previewers](#previewers) |
| `grep_previewer` | What telescope previewer to use for grep and similar | [Previewers](#previewers) | | `grep_previewer` | What telescope previewer to use for grep and similar | [Previewers](#previewers) |
| `qflist_previewer` | What telescope previewer to use for qflist | [Previewers](#previewers) | | `qflist_previewer` | What telescope previewer to use for qflist | [Previewers](#previewers) |
@@ -446,7 +436,7 @@ Built-in functions. Ready to be bound to any key you like. :smile:
| `builtin.current_buffer_fuzzy_find` | Live fuzzy search inside of the currently open buffer | | `builtin.current_buffer_fuzzy_find` | Live fuzzy search inside of the currently open buffer |
| `builtin.current_buffer_tags` | Lists all of the tags for the currently open buffer, with a preview | | `builtin.current_buffer_tags` | Lists all of the tags for the currently open buffer, with a preview |
### LSP Pickers ### Neovim LSP Pickers
| Functions | Description | | Functions | Description |
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------| |---------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
@@ -644,7 +634,6 @@ Picker:new{
selection_strategy = "reset", -- follow, reset, row selection_strategy = "reset", -- follow, reset, row
border = {}, border = {},
borderchars = {"─", "│", "─", "│", "┌", "┐", "┘", "└"}, borderchars = {"─", "│", "─", "│", "┌", "┐", "┘", "└"},
preview_cutoff = 120,
default_selection_index = 1, -- Change the index of the initial selection row default_selection_index = 1, -- Change the index of the initial selection row
} }
``` ```
@@ -677,24 +666,37 @@ end
### Layout (display) ### Layout (display)
<!-- TODO need some work --> <!-- TODO need some work -->
`Resolvable`: Layout can be configured by choosing a specific `layout_strategy` and
1. 0 <= number < 1: specifying a particular `layout_config` for that strategy.
- This means total height as a percentage For more details on available strategies and configuration options,
2. 1 <= number: see `:help telescope.layout`.
- This means total height as a fixed number
3. function(picker, columns, lines):
- returns one of the above options
- `return max.min(110, max_rows * .5)`
```lua Some options for configuring sizes in layouts are "resolvable".
layout_strategies.horizontal = function(self, max_columns, max_lines) This means that they can take different forms, and will be interpreted differently according to which form they take.
local layout_config = validate_layout_config(self.layout_config or {}, { For example, if we wanted to set the `width` of a picker using the `vertical`
width_padding = "How many cells to pad the width", layout strategy to 50% of the screen width, we would specify that width
height_padding = "How many cells to pad the height", as `0.5`, but if we wanted to specify the `width` to be exactly 80
preview_width = "(Resolvable): Determine preview width", characters wide, we would specify it as `80`.
For more details on resolving sizes, see `:help telescope.resolve`.
As an example, if we wanted to specify the layout strategy and width,
but only for this instance, we could do something like:
```
:lua require('telescope.builtin').find_files({layout_strategy='vertical',layout_config={width=0.5}})
```
or if we wanted to change the width for every time we use the `vertical`
layout strategy, we could add the following to our `setup()` call:
```
require('telescope').setup({
defaults = {
layout_config = {
vertical = { width = 0.5 }
-- other layout configuration here
},
-- other defaults configuration here
},
-- other configuration values here
}) })
...
end
``` ```
## Vim Commands ## Vim Commands

View File

@@ -19,6 +19,20 @@ telescope.setup({opts}) *telescope.setup()*
Valid keys for {opts.defaults} Valid keys for {opts.defaults}
*telescope.defaults.border*
border: ~
Boolean defining if borders are added to Telescope windows.
Default: true
*telescope.defaults.default_mappings*
default_mappings: ~
Not recommended to use except for advanced users.
Will allow you to completely remove all of telescope's default maps
and use your own.
*telescope.defaults.dynamic_preview_title* *telescope.defaults.dynamic_preview_title*
dynamic_preview_title: ~ dynamic_preview_title: ~
Will change the title of the preview window dynamically, where it Will change the title of the preview window dynamically, where it
@@ -34,6 +48,94 @@ telescope.setup({opts}) *telescope.setup()*
Default: ' ' Default: ' '
*telescope.defaults.layout_config*
layout_config: ~
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: {
center = {
preview_cutoff = 40
},
height = 0.9,
horizontal = {
preview_cutoff = 120,
prompt_position = "bottom"
},
vertical = {
preview_cutoff = 40
},
width = 0.8
}
*telescope.defaults.layout_strategy*
layout_strategy: ~
Determines the default layout of Telescope pickers.
See |telescope.layout| for details of the available strategies.
Default: 'horizontal'
*telescope.defaults.mappings*
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" },)
...,
*telescope.defaults.prompt_prefix* *telescope.defaults.prompt_prefix*
prompt_prefix: ~ prompt_prefix: ~
Will be shown in front of the prompt. Will be shown in front of the prompt.
@@ -478,7 +580,7 @@ require("telescope").setup {
mappings = { mappings = {
i = { i = {
["<c-d>"] = require("telescope.actions").delete_buffer, ["<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", ["<c-d>"] = "delete_buffer",
}, },
n = { n = {
@@ -1118,6 +1220,52 @@ action_state.get_current_picker({prompt_bufnr})*action_state.get_current_picker(
================================================================================
*telescope.resolve*
Provides "resolver functions" to allow more customisable inputs for options.
resolver.resolve_height() *resolver.resolve_height()*
Converts input to a function that returns the height. The input must take
one of four forms:
1. 0 <= number < 1
This means total height as a percentage.
2. 1 <= number
This means total height as a fixed number.
3. function
Must have signature: function(self, max_columns, max_lines): number
4. table of the form: {padding = `foo`}
where `foo` has one of the previous three forms.
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_width() *resolver.resolve_width()*
Converts input to a function that returns the width. The input must take
one of four forms:
1. 0 <= number < 1
This means total width as a percentage.
2. 1 <= number
This means total width as a fixed number.
3. function
Must have signature: function(self, max_columns, max_lines): number
4. table of the form: {padding = `foo`}
where `foo` has one of the previous three forms.
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
================================================================================ ================================================================================
*telescope.previewers* *telescope.previewers*
@@ -1450,7 +1598,7 @@ Layout strategies are different functions to position telescope.
All layout strategies are functions with the following signature: All layout strategies are functions with the following signature:
function(picker, columns, lines) function(picker, columns, lines, layout_config)
-- Do some calculations here... -- Do some calculations here...
return { return {
preview = preview_configuration preview = preview_configuration
@@ -1461,98 +1609,166 @@ All layout strategies are functions with the following signature:
Parameters: ~ Parameters: ~
- picker : A Picker object. (docs coming soon) - picker : A Picker object. (docs coming soon)
- columns : number Columns in the vim window - columns : (number) Columns in the vim window
- lines : number Lines in the vim window - lines : (number) Lines in the vim window
- layout_config : (table) The configuration values specific to the picker.
TODO: I would like to make these link to `telescope.layout_strategies.*`, but This means you can create your own layout strategy if you want! Just be aware
it's not yet possible. for now that we may change some APIs or interfaces, so they may break if you
create your own.
Available layout strategies include: A good method for creating your own would be to copy one of the strategies that
- horizontal: most resembles what you want from
- See |layout_strategies.horizontal| "./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
layout_strategies.horizontal() *layout_strategies.horizontal()* layout_strategies.horizontal() *layout_strategies.horizontal()*
Horizontal previewer Horizontal layout has two columns, one for the preview and one for the
prompt and results.
+-------------+--------------+ ┌──────────────────────────────────────────────────┐
| | |
| Results | | │ ┌───────────────────┐┌───────────────────┐
| | Preview | ││ │ │
| | | │ │ ││ │ │
+-------------| | │ ││ │ │
| Prompt | | │ Results ││ │ │
+-------------+--------------+ │ ││ Preview │ │
│ │ ││ │ │
│ │ ││ │ │
│ └───────────────────┘│ │ │
│ ┌───────────────────┐│ │ │
│ │ Prompt ││ │ │
│ └───────────────────┘└───────────────────┘ │
│ │
└──────────────────────────────────────────────────┘
`picker.layout_config` shared options:
- 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
- width:
- How wide to make Telescope's entire layout
- See |resolver.resolve_width()|
`picker.layout_config` unique options:
- preview_cutoff: When columns are less than this value, the preview will be disabled
- preview_width:
- Change the width of Telescope's preview window
- See |resolver.resolve_width()|
- prompt_position:
- Where to place prompt window.
- Available Values: 'bottom', 'top'
layout_strategies.center() *layout_strategies.center()* layout_strategies.center() *layout_strategies.center()*
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()|`).
+--------------+ ┌──────────────────────────────────────────────────┐
| Preview | ┌────────────────────────────────────────┐ │
+--------------+ │ | Preview | │
| Prompt | | Preview | │
+--------------+ └────────────────────────────────────────┘ │
| Result | ┌────────────────────────────────────────┐ │
| Result | | Prompt | │
| Result | ├────────────────────────────────────────┤ │
+--------------+ │ | Result | │
│ | Result | │
│ └────────────────────────────────────────┘ │
│ │
│ │
│ │
│ │
└──────────────────────────────────────────────────┘
`picker.layout_config` shared options:
- 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
- width:
- How wide to make Telescope's entire layout
- See |resolver.resolve_width()|
`picker.layout_config` unique options:
- preview_cutoff: When lines are less than this value, the preview will be disabled
layout_strategies.vertical() *layout_strategies.vertical()* layout_strategies.vertical() *layout_strategies.vertical()*
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.
+-----------------+ ┌──────────────────────────────────────────────────┐
| Previewer |
| Previewer | ┌────────────────────────────────────────┐
| Previewer | | Preview | │
+-----------------+ | Preview | │
| Result | | Preview | │
| Result | └────────────────────────────────────────┘
| Result | ┌────────────────────────────────────────┐
+-----------------+ | Result | │
| Prompt | | Result | │
+-----------------+ └────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────┐ │
│ | Prompt | │
│ └────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────┘
`picker.layout_config` shared options:
- 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
- width:
- How wide to make Telescope's entire layout
- See |resolver.resolve_width()|
`picker.layout_config` unique 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)
layout_strategies.flex() *layout_strategies.flex()* layout_strategies.flex() *layout_strategies.flex()*
Swap between `horizontal` and `vertical` strategies based on the window Flex layout swaps between `horizontal` and `vertical` strategies based on
width the window width
- Supports `vertical` or `horizontal` features - Supports |layout_strategies.vertical| or |layout_strategies.horizontal|
features
Uses:
- flip_columns `picker.layout_config` shared options:
- flip_lines - 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
- width:
- How wide to make Telescope's entire layout
- See |resolver.resolve_width()|
`picker.layout_config` unique 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
- horizontal: Options to pass when switching to horizontal layout
- vertical: Options to pass when switching to vertical layout
layout_strategies.bottom_pane() *layout_strategies.bottom_pane()*
Bottom pane can be used to create layouts similar to "ivy".
For an easy ivy configuration, see |themes.get_ivy()|

View File

@@ -0,0 +1,51 @@
================================================================================
*telescope.changelog*
# Changelog
*telescope.changelog-922*
Date: May 17, 2021
PR: https://github.com/nvim-telescope/telescope.nvim/pull/922
This is one of our largest breaking changes thus far, so I (TJ) am adding some
information here so that you can more easily update (without having to track
down the commit, etc.).
The goal of these breaking changes is to greatly simplify the way
configuration for layouts happen. This should make it much easier to configure
each picker, layout_strategy, and more. Please report any bugs or behavior
that is broken / confusing upstream and we can try and make the configuration
better.
|telescope.setup()| has changed `layout_defaults` -> `layout_config`.
This makes it so that the setup and the pickers share the same key,
otherwise it is too confusing which key is for which.
`picker:find()` now has different values available for configuring the UI.
All configuration for the layout must be passed in the key:
`layout_config`.
Previously, these keys were passed via `picker:find(opts)`, but should be
passed via `opts.layout_config` now.
- {height}
- {width}
- {prompt_position}
- {preview_cutoff}
These keys are removed:
- {results_height}: This key is no longer valid. Instead, use `height`
and the corresponding `preview_*` options for the layout strategy to
get the correct results height. This simplifies the configuration
for many of the existing strategies.
- {results_width}: This key actually never did anything. It was
leftover from some hacking that I had attempted before. Instead you
should be using something like the `preview_width` configuration
option for |layout_strategies.horizontal()|
You should get error messages when you try and use any of the above keys now.
vim:tw=78:ts=8:ft=help:norl:

View File

@@ -37,7 +37,7 @@
--- mappings = { --- mappings = {
--- i = { --- i = {
--- ["<c-d>"] = require("telescope.actions").delete_buffer, --- ["<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", --- ["<c-d>"] = "delete_buffer",
--- }, --- },
--- n = { --- n = {

View File

@@ -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 -- Keep the values around between reloads
_TelescopeConfigurationValues = _TelescopeConfigurationValues or {} _TelescopeConfigurationValues = _TelescopeConfigurationValues or {}
_TelescopeConfigurationPickers = _TelescopeConfigurationPickers or {} _TelescopeConfigurationPickers = _TelescopeConfigurationPickers or {}
local function first_non_null(...) local function first_non_null(...)
local n = select('#', ...) local n = select("#", ...)
for i = 1, n do for i = 1, n do
local value = select(i, ...) local value = select(i, ...)
@@ -15,7 +18,26 @@ local function first_non_null(...)
end end
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. -- TODO: Add other major configuration points here.
-- selection_strategy -- selection_strategy
@@ -27,18 +49,279 @@ config.descriptions = {}
config.pickers = _TelescopeConfigurationPickers config.pickers = _TelescopeConfigurationPickers
function config.set_pickers(pickers) function config.set_pickers(pickers)
pickers = pickers or {} pickers = if_nil(pickers, {})
for k, v in pairs(pickers) do for k, v in pairs(pickers) do
config.pickers[k] = v config.pickers[k] = v
end end
end end
function config.set_defaults(defaults) local layout_config_defaults = {
defaults = defaults or {} 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"]],
},
selection_strategy = {
"reset",
[[
Determines how the cursor acts after each sort iteration.
Available options are:
- "reset" (default)
- "follow"
- "row"]],
},
scroll_strategy = {
"cycle",
[[
Determines what happens you try to scroll past view of the picker.
Available options are:
- "cycle" (default)
- "limit"]],
},
layout_strategy = {
"horizontal",
[[
Determines the default layout of Telescope pickers.
See |telescope.layout| for details of the available strategies.
Default: 'horizontal']],
},
layout_config = { layout_config_defaults, layout_config_description },
winblend = { 0 },
prompt_prefix = { "> ", [[
Will be shown in front of the prompt.
Default: '> ']]
},
selection_caret = { "> ", [[
Will be shown in front of the selection.
Default: '> ']]
},
entry_prefix = { " ", [[
Prefix in front of each result entry. Current selection not included.
Default: ' ']]
},
initial_mode = { "insert" },
border = { true, [[
Boolean defining if borders are added to Telescope windows.
Default: true]]
},
borderchars = { { "", "", "", "", "", "", "", "" } },
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) local function get(name, default_val)
return first_non_null(defaults[name], config.values[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 end
local function set(name, default_val, description) local function set(name, default_val, description)
@@ -51,114 +334,13 @@ function config.set_defaults(defaults)
end end
end end
set("sorting_strategy", "descending", [[ for key, info in pairs(tele_defaults) do
Determines the direction "better" results are sorted towards. set(key, info[1], info[2])
end
Available options are: local M = {}
- "descending" (default) M.get = get
- "ascending"]]) return M
set("selection_strategy", "reset", [[
Determines how the cursor acts after each sort iteration.
Available options are:
- "reset" (default)
- "follow"
- "row"]])
set("scroll_strategy", "cycle", [[
Determines what happens you try to scroll past view of the picker.
Available options are:
- "cycle" (default)
- "limit"]])
set("layout_strategy", "horizontal")
set("layout_defaults", {})
set("width", 0.75)
set("winblend", 0)
set("prompt_position", "bottom")
set("preview_cutoff", 120)
set("results_height", 1)
set("results_width", 0.8)
set("prompt_prefix", "> ", [[
Will be shown in front of the prompt.
Default: '> ']])
set("selection_caret", "> ", [[
Will be shown in front of the selection.
Default: '> ']])
set("entry_prefix", " ", [[
Prefix in front of each result entry. Current selection not included.
Default: ' ']])
set("initial_mode", "insert")
set("border", {})
set("borderchars", { '', '', '', '', '', '', '', ''})
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
return string.format("%s / %s", xx, yy)
end)
-- Builtin configuration
-- 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, [[
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
]])
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)
end end
function config.clear_defaults() function config.clear_defaults()
@@ -169,5 +351,4 @@ end
config.set_defaults() config.set_defaults()
return config return config

View File

@@ -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) 3. function(picker, columns, lines)
-> returns one of the above options -> returns one of the above options
return max.min(110, max_rows * .5) return math.min(110, max_rows * .5)
if columns > 120 then if columns > 120 then
return 110 return 110
@@ -88,46 +93,80 @@ That's the next step to scrolling.
local get_default = require('telescope.utils').get_default local get_default = require('telescope.utils').get_default
local resolver = {} local resolver = {}
local _resolve_map = {}
local _resolve_map = {
-- Booleans -- Booleans
[function(val) return val == false end] = function(selector, val) _resolve_map[function(val) return val == false end] = function(_, val)
return function(...) return function(...)
return val return val
end end
end, end
-- Percentages -- Percentages
[function(val) return type(val) == 'number' and val >= 0 and val < 1 end] = function(selector, val) _resolve_map[function(val) return type(val) == 'number' and val >= 0 and val < 1 end] = function(selector, val)
return function(...) return function(...)
local selected = select(selector, ...) local selected = select(selector, ...)
return math.floor(val * selected) return math.floor(val * selected)
end end
end, end
-- Numbers -- Numbers
[function(val) return type(val) == 'number' and val >= 1 end] = function(selector, val) _resolve_map[function(val) return type(val) == 'number' and val >= 1 end] = function(selector, val)
return function(...) return function(...)
local selected = select(selector, ...) local selected = select(selector, ...)
return math.min(val, selected) return math.min(val, selected)
end end
end, end
-- Tables TODO: -- Tables TODO:
-- ... {70, max} -- ... {70, max}
-- function: -- function:
-- Function must have same signature as get_window_layout -- Function must have same signature as get_window_layout
-- function(self, max_columns, max_lines): number -- function(self, max_columns, max_lines): number
-- --
-- Resulting number is used for this configuration value. -- Resulting number is used for this configuration value.
[function(val) return type(val) == 'function' end] = function(selector, val) _resolve_map[function(val) return type(val) == 'function' end] = function(_, val)
return val return val
end, 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
--- 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) resolver.resolve_height = function(val)
for k, v in pairs(_resolve_map) do for k, v in pairs(_resolve_map) do
if k(val) then if k(val) then
@@ -138,6 +177,23 @@ resolver.resolve_height = function(val)
error('invalid configuration option for height:' .. tostring(val)) error('invalid configuration option for height:' .. tostring(val))
end 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) resolver.resolve_width = function(val)
for k, v in pairs(_resolve_map) do for k, v in pairs(_resolve_map) do
if k(val) then if k(val) then
@@ -148,8 +204,8 @@ resolver.resolve_width = function(val)
error('invalid configuration option for width:' .. tostring(val)) error('invalid configuration option for width:' .. tostring(val))
end end
--- Win option always returns a table with preview, results, and prompt. -- Win option always returns a table with preview, results, and prompt.
--- It handles many different ways. Some examples are as follows: -- It handles many different ways. Some examples are as follows:
-- --
-- -- Disable -- -- Disable
-- borderschars = false -- borderschars = false

View File

@@ -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

View File

@@ -249,6 +249,8 @@ function make_entry.gen_from_git_stash()
end end
function make_entry.gen_from_git_commits(opts) function make_entry.gen_from_git_commits(opts)
opts = opts or {}
local displayer = entry_display.create { local displayer = entry_display.create {
separator = " ", separator = " ",
items = { items = {

View File

@@ -14,6 +14,7 @@ local actions = require('telescope.actions')
local action_set = require('telescope.actions.set') local action_set = require('telescope.actions.set')
local config = require('telescope.config') local config = require('telescope.config')
local debounce = require('telescope.debounce') local debounce = require('telescope.debounce')
local deprecated = require('telescope.deprecated')
local log = require('telescope.log') local log = require('telescope.log')
local mappings = require('telescope.mappings') local mappings = require('telescope.mappings')
local state = require('telescope.state') local state = require('telescope.state')
@@ -56,6 +57,8 @@ function Picker:new(opts)
actions._clear() actions._clear()
action_set._clear() action_set._clear()
deprecated.picker_window_options(opts)
local layout_strategy = get_default(opts.layout_strategy, config.values.layout_strategy) local layout_strategy = get_default(opts.layout_strategy, config.values.layout_strategy)
local obj = setmetatable({ local obj = setmetatable({
@@ -96,33 +99,13 @@ function Picker:new(opts)
selection_strategy = get_default(opts.selection_strategy, config.values.selection_strategy), selection_strategy = get_default(opts.selection_strategy, config.values.selection_strategy),
layout_strategy = layout_strategy, layout_strategy = layout_strategy,
layout_config = get_default( layout_config = vim.tbl_deep_extend("keep", opts.layout_config or {}, config.values.layout_config or {}),
opts.layout_config,
(config.values.layout_defaults or {})[layout_strategy]
) or {},
window = { 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), 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), border = get_default(opts.border, config.values.border),
borderchars = get_default(opts.borderchars, config.values.borderchars), borderchars = get_default(opts.borderchars, config.values.borderchars),
}, },
preview_cutoff = get_default(opts.preview_cutoff, config.values.preview_cutoff),
}, self) }, self)
obj.get_window_options = opts.get_window_options or p_window.get_window_options obj.get_window_options = opts.get_window_options or p_window.get_window_options

View File

@@ -7,7 +7,7 @@
--- All layout strategies are functions with the following signature: --- All layout strategies are functions with the following signature:
--- ---
--- <pre> --- <pre>
--- function(picker, columns, lines) --- function(picker, columns, lines, layout_config)
--- -- Do some calculations here... --- -- Do some calculations here...
--- return { --- return {
--- preview = preview_configuration --- preview = preview_configuration
@@ -18,135 +18,256 @@
--- ---
--- Parameters: ~ --- Parameters: ~
--- - picker : A Picker object. (docs coming soon) --- - picker : A Picker object. (docs coming soon)
--- - columns : number Columns in the vim window --- - columns : (number) Columns in the vim window
--- - lines : number Lines in the vim window --- - lines : (number) Lines in the vim window
--- - layout_config : (table) The configuration values specific to the picker.
--- ---
--- </pre> --- </pre>
--- ---
--- TODO: I would like to make these link to `telescope.layout_strategies.*`, --- This means you can create your own layout strategy if you want! Just be aware
--- but it's not yet possible. --- for now that we may change some APIs or interfaces, so they may break if you create
--- your own.
--- ---
--- Available layout strategies include: --- A good method for creating your own would be to copy one of the strategies that most
--- - horizontal: --- resembles what you want from "./lua/telescope/pickers/layout_strategies.lua" in the
--- - See |layout_strategies.horizontal| --- 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 ]] ---@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 p_window = require('telescope.pickers.window')
local if_nil = vim.F.if_nil
local get_border_size = function(opts)
-- Check if there are any borders. Right now it's a little raw as if opts.window.border == false then
-- there are a few things that contribute to the border return 0
local is_borderless = function(opts)
return opts.window.border == false
end end
return 1
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)
))
end
end
return options
end end
local layout_strategies = {} local layout_strategies = {}
layout_strategies._configurations = {}
--- Horizontal previewer --@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
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
-- 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 result
end
-- 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
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> --- <pre>
--- +-------------+--------------+ --- ┌──────────────────────────────────────────────────┐
--- | | | ---
--- | Results | | --- │ ┌───────────────────┐┌───────────────────┐
--- | | Preview | --- ││ │ │
--- | | | --- │ │ ││ │ │
--- +-------------| | --- │ ││ │ │
--- | Prompt | | --- │ Results ││ │ │
--- +-------------+--------------+ --- │ ││ Preview │ │
--- │ │ ││ │ │
--- │ │ ││ │ │
--- │ └───────────────────┘│ │ │
--- │ ┌───────────────────┐│ │ │
--- │ │ Prompt ││ │ │
--- │ └───────────────────┘└───────────────────┘ │
--- │ │
--- └──────────────────────────────────────────────────┘
--- </pre> --- </pre>
layout_strategies.horizontal = function(self, max_columns, max_lines) ---@eval { ["description"] = require('telescope.pickers.layout_strategies')._format("horizontal") }
local layout_config = validate_layout_config(self.layout_config or {}, { ---
width_padding = "How many cells to pad the width", layout_strategies.horizontal = make_documented_layout('horizontal', vim.tbl_extend("error", shared_options, {
height_padding = "How many cells to pad the height", preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|", },
preview_width = "(Resolvable): Determine preview width", preview_cutoff = "When columns are less than this value, the preview will be disabled",
mirror = "Flip the location of the results/prompt and preview windows", prompt_position = { "Where to place prompt window.", "Available Values: 'bottom', 'top'" },
scroll_speed = "The speed when scrolling through the previewer", }), function(self, max_columns, max_lines, layout_config)
})
local initial_options = p_window.get_initial_window_options(self) local initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview local preview = initial_options.preview
local results = initial_options.results local results = initial_options.results
local prompt = initial_options.prompt local prompt = initial_options.prompt
-- TODO: Test with 120 width terminal local width_opt = layout_config.width
-- TODO: Test with self.width local picker_width = resolve.resolve_width(width_opt)(self, max_columns, max_lines)
local width_padding = resolve.resolve_width(layout_config.width_padding or function(_, cols) local width_padding = math.floor((max_columns - picker_width)/2)
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 height_padding = resolve.resolve_height(layout_config.height_padding or function(_, _, lines) local height_opt = layout_config.height
if lines < 40 then local picker_height = resolve.resolve_height(height_opt)(self, max_columns, max_lines)
return 4 local height_padding = math.floor((max_lines - picker_height)/2)
else
return math.floor(0.1 * lines)
end
end)(self, max_columns, max_lines)
local picker_height = max_lines - 2 * height_padding
if self.previewer then if self.previewer and max_columns >= layout_config.preview_cutoff then
preview.width = resolve.resolve_width(layout_config.preview_width or function(_, cols) preview.width = resolve.resolve_width(if_nil(layout_config.preview_width, function(_, cols)
if not self.previewer or cols < self.preview_cutoff then if cols < 150 then
return 0
elseif cols < 150 then
return math.floor(cols * 0.4) return math.floor(cols * 0.4)
elseif cols < 200 then elseif cols < 200 then
return 80 return 80
else else
return 120 return 120
end end
end)(self, picker_width, max_lines) end))(self, picker_width, max_lines)
else else
preview.width = 0 preview.width = 0
end end
@@ -175,14 +296,14 @@ layout_strategies.horizontal = function(self, max_columns, max_lines)
end end
preview.line = height_padding preview.line = height_padding
if self.window.prompt_position == "top" then if layout_config.prompt_position == "top" then
prompt.line = height_padding prompt.line = height_padding
results.line = prompt.line + prompt.height + 2 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 results.line = height_padding
prompt.line = results.line + results.height + 2 prompt.line = results.line + results.height + 2
else else
error("Unknown prompt_position: " .. self.window.prompt_position) error("Unknown prompt_position: " .. tostring(self.window.prompt_position) .. "\n" .. vim.inspect(layout_config))
end end
return { return {
@@ -190,33 +311,54 @@ layout_strategies.horizontal = function(self, max_columns, max_lines)
results = results, results = results,
prompt = prompt 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> --- <pre>
--- +--------------+ --- ┌──────────────────────────────────────────────────┐
--- | Preview | --- ┌────────────────────────────────────────┐ │
--- +--------------+ --- │ | Preview | │
--- | Prompt | --- | Preview | │
--- +--------------+ --- └────────────────────────────────────────┘ │
--- | Result | --- ┌────────────────────────────────────────┐ │
--- | Result | --- | Prompt | │
--- | Result | --- ├────────────────────────────────────────┤ │
--- +--------------+ --- │ | Result | │
--- │ | Result | │
--- │ └────────────────────────────────────────┘ │
--- │ │
--- │ │
--- │ │
--- │ │
--- └──────────────────────────────────────────────────┘
--- </pre> --- </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 initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview local preview = initial_options.preview
local results = initial_options.results local results = initial_options.results
local prompt = initial_options.prompt local prompt = initial_options.prompt
-- This sets the height/width for the whole layout -- This sets the width for the whole layout
local height = resolve.resolve_height(self.window.results_height)(self, columns, lines) local width_opt = layout_config.width
local width = resolve.resolve_width(self.window.width)(self, columns, lines) local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines)
local max_results = (height > lines and lines or height) -- This sets the number of results displayed
local max_width = (width > columns and columns or width) local res_height_opt = layout_config.height
local res_height = resolve.resolve_height(res_height_opt)(self, max_columns, max_lines)
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 prompt.height = 1
results.height = max_results results.height = max_results
@@ -225,87 +367,84 @@ layout_strategies.center = function(self, columns, lines)
results.width = max_width results.width = max_width
preview.width = max_width preview.width = max_width
-- border size -- Align the prompt and results so halfway up the screen is
local bs = 1 -- in the middle of this combined block
if is_borderless(self) then prompt.line = (max_lines / 2) - ((max_results + (bs * 2)) / 2)
bs = 0
end
prompt.line = (lines / 2) - ((max_results + (bs * 2)) / 2)
results.line = prompt.line + 1 + (bs) results.line = prompt.line + 1 + (bs)
preview.line = 1 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 preview.height = 0
end end
results.col = math.ceil((columns / 2) - (width / 2) - bs) results.col = math.ceil((max_columns / 2) - (width / 2) - bs)
prompt.col = results.col prompt.col = results.col
preview.col = results.col preview.col = results.col
return { return {
preview = self.previewer and preview.width > 0 and preview, preview = self.previewer and preview.height > 0 and preview,
results = results, results = results,
prompt = prompt 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> --- <pre>
--- +-----------------+ --- ┌──────────────────────────────────────────────────┐
--- | Previewer | ---
--- | Previewer | --- ┌────────────────────────────────────────┐
--- | Previewer | --- | Preview | │
--- +-----------------+ --- | Preview | │
--- | Result | --- | Preview | │
--- | Result | --- └────────────────────────────────────────┘
--- | Result | --- ┌────────────────────────────────────────┐
--- +-----------------+ --- | Result | │
--- | Prompt | --- | Result | │
--- +-----------------+ --- └────────────────────────────────────────┘ │
--- │ ┌────────────────────────────────────────┐ │
--- │ | Prompt | │
--- │ └────────────────────────────────────────┘ │
--- │ │
--- └──────────────────────────────────────────────────┘
--- </pre> --- </pre>
layout_strategies.vertical = function(self, max_columns, max_lines) ---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("vertical") }
local layout_config = validate_layout_config(self.layout_config or {}, { ---
width_padding = "How many cells to pad the width", layout_strategies.vertical = make_documented_layout("vertical", vim.tbl_extend("error", shared_options, {
height_padding = "How many cells to pad the height", preview_cutoff = "When lines are less than this value, the preview will be disabled",
preview_height = "(Resolvable): Determine preview height", preview_height = { "Change the height of Telescope's preview window", "See |resolver.resolve_height()|" },
mirror = "Flip the locations of the results and prompt windows", prompt_position = { "(unimplemented, but we plan on supporting)" },
scroll_speed = "The speed when scrolling through the previewer", }), function(self, max_columns, max_lines, layout_config)
})
local initial_options = p_window.get_initial_window_options(self) local initial_options = p_window.get_initial_window_options(self)
local preview = initial_options.preview local preview = initial_options.preview
local results = initial_options.results local results = initial_options.results
local prompt = initial_options.prompt local prompt = initial_options.prompt
local width_padding = resolve.resolve_width( local width_opt = layout_config.width
layout_config.width_padding or math.ceil((1 - self.window.width) * 0.5 * max_columns) local picker_width = resolve.resolve_width(width_opt)(self,max_columns,max_lines)
)(self, max_columns, max_lines) local width_padding = math.floor((max_columns - picker_width)/2)
local width = max_columns - width_padding * 2 local height_opt = layout_config.height
if not self.previewer then local picker_height = resolve.resolve_height(height_opt)(self,max_columns,max_lines)
preview.width = 0 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 else
preview.width = width preview.width = 0
end end
results.width = width results.width = picker_width
prompt.width = width prompt.width = picker_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
local preview_total = 0 local preview_total = 0
preview.height = 0 preview.height = 0
if self.previewer then if self.previewer and max_lines >= layout_config.preview_cutoff then
preview.height = resolve.resolve_height( 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) )(self, max_columns, picker_height)
preview_total = preview.height + 2 preview_total = preview.height + 2
@@ -332,36 +471,38 @@ layout_strategies.vertical = function(self, max_columns, max_lines)
end end
return { return {
preview = self.previewer and preview.width > 0 and preview, preview = self.previewer and preview.height > 0 and preview,
results = results, results = results,
prompt = prompt prompt = prompt
} }
end end)
--- Swap between `horizontal` and `vertical` strategies based on the window width --- Flex layout swaps between `horizontal` and `vertical` strategies based on the window width
--- - Supports `vertical` or `horizontal` features --- - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| features
--- ---
--- Uses: ---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("flex") }
--- - flip_columns ---
--- - flip_lines layout_strategies.flex = make_documented_layout('flex', vim.tbl_extend("error", shared_options, {
layout_strategies.flex = function(self, max_columns, max_lines) flip_columns = "The number of columns required to move to horizontal mode",
local layout_config = self.layout_config or {} flip_lines = "The number of lines required to move to horizontal mode",
vertical = "Options to pass when switching to vertical layout",
local flip_columns = layout_config.flip_columns or 100 horizontal = "Options to pass when switching to horizontal layout",
local flip_lines = layout_config.flip_lines or 20 }), 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 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. return layout_strategies.vertical(self, max_columns, max_lines, layout_config.vertical)
self.layout_config = (config.values.layout_defaults or {})['vertical']
return layout_strategies.vertical(self, max_columns, max_lines)
else else
self.layout_config = (config.values.layout_defaults or {})['horizontal'] return layout_strategies.horizontal(self, max_columns, max_lines, layout_config.horizontal)
return layout_strategies.horizontal(self, max_columns, max_lines)
end
end end
end)
layout_strategies.current_buffer = function(self, _, _) layout_strategies.current_buffer = make_documented_layout('current_buffer', {
local initial_options = self:_get_initial_window_options() -- 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_width = vim.api.nvim_win_get_width(0)
local window_height = vim.api.nvim_win_get_height(0) local window_height = vim.api.nvim_win_get_height(0)
@@ -414,19 +555,20 @@ layout_strategies.current_buffer = function(self, _, _)
results = results, results = results,
prompt = prompt, prompt = prompt,
} }
end 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",
})
--- 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 initial_options = p_window.get_initial_window_options(self)
local results = initial_options.results local results = initial_options.results
local prompt = initial_options.prompt local prompt = initial_options.prompt
local preview = initial_options.preview 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 prompt_width = max_columns
local col = 0 local col = 0
@@ -477,6 +619,8 @@ layout_strategies.bottom_pane = function(self, max_columns, max_lines)
width = result_width, width = result_width,
}), }),
} }
end end)
layout_strategies._validate_layout_config = validate_layout_config
return layout_strategies return layout_strategies

View File

@@ -302,7 +302,6 @@ previewers.git_commit_message = buffer_previewer.git_commit_message
--- The run command is `git --no-pager diff $FILE` --- The run command is `git --no-pager diff $FILE`
previewers.git_file_diff = buffer_previewer.git_file_diff previewers.git_file_diff = buffer_previewer.git_file_diff
previewers.ctags = buffer_previewer.ctags previewers.ctags = buffer_previewer.ctags
previewers.builtin = buffer_previewer.builtin previewers.builtin = buffer_previewer.builtin
previewers.help = buffer_previewer.help previewers.help = buffer_previewer.help

View File

@@ -27,16 +27,26 @@ function themes.get_dropdown(opts)
opts = opts or {} opts = opts or {}
local theme_opts = { local theme_opts = {
-- WIP: Decide on keeping these names or not.
theme = "dropdown", theme = "dropdown",
results_title = false,
preview_title = "Preview",
sorting_strategy = "ascending", sorting_strategy = "ascending",
layout_strategy = "center", layout_strategy = "center",
results_title = false, layout_config = {
preview_title = "Preview",
preview_cutoff = 1, -- Preview should always show (unless previewer = false) preview_cutoff = 1, -- Preview should always show (unless previewer = false)
width = 80,
results_height = 15, 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 = { borderchars = {
{ "", "", "", "", "", "", "", ""}, { "", "", "", "", "", "", "", ""},
prompt = {"", "", " ", "", "", "", "", ""}, prompt = {"", "", " ", "", "", "", "", ""},

View File

@@ -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)

View File

@@ -35,8 +35,11 @@ describe('builtin.find_files', function()
}, vim.tbl_extend("force", { }, vim.tbl_extend("force", {
disable_devicons = true, disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(), sorter = require('telescope.sorters').get_fzy_sorter(),
results_height = max_results,
layout_strategy = 'center', layout_strategy = 'center',
layout_config = {
height = max_results,
width = 0.9,
},
}, vim.fn.json_decode([==[%s]==]))) }, vim.fn.json_decode([==[%s]==])))
]], vim.fn.json_encode(configuration))) ]], vim.fn.json_encode(configuration)))
end) end)
@@ -57,8 +60,11 @@ describe('builtin.find_files', function()
}, vim.tbl_extend("force", { }, vim.tbl_extend("force", {
disable_devicons = true, disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(), sorter = require('telescope.sorters').get_fzy_sorter(),
results_height = 5,
layout_strategy = 'center', layout_strategy = 'center',
layout_config = {
height = max_results,
width = 0.9,
},
}, vim.fn.json_decode([==[%s]==]))) }, vim.fn.json_decode([==[%s]==])))
]], expected, vim.fn.json_encode(configuration))) ]], expected, vim.fn.json_encode(configuration)))
end) end)

View File

@@ -1,4 +1,5 @@
-- Setup telescope with defaults -- Setup telescope with defaults
if RELOAD then RELOAD('telescope') end
require('telescope').setup() require('telescope').setup()
local docgen = require('docgen') local docgen = require('docgen')
@@ -15,6 +16,7 @@ docs.test = function()
"./lua/telescope/actions/state.lua", "./lua/telescope/actions/state.lua",
"./lua/telescope/actions/set.lua", "./lua/telescope/actions/set.lua",
"./lua/telescope/previewers/init.lua", "./lua/telescope/previewers/init.lua",
"./lua/telescope/config/resolve.lua",
"./lua/telescope/themes.lua", "./lua/telescope/themes.lua",
} }