Skip to content

Commit 5833f01

Browse files
committed
feat(pick)!: set window-local current directory to source's cwd
Apart from being what appears to be a more logical behavior, this also fixes current situation when `default_choose` and `default_preview` don't respect source's cwd when item contains relative path (they used to resolve them against working directory prior starting the picker).
1 parent 3a31784 commit 5833f01

15 files changed

+154
-41
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
## mini.pick
2828

29+
- BREAKING FEATURE: picker window now has local current directory set to source's `cwd`. This allows easier code for "in window" functions (callable items, choose, preview, etc.) as relative paths will be properly resolved. It also results in some changes:
30+
- Calling `set_picker_items_from_cli()` with active picker now resolves explicitly set to relative path `spawn_opts.cwd` against picker's `cwd` (and not against global current directory as was done previously).
2931
- FEATURE: update `grep` and `grep_live` pickers to allow `globs` local option which restricts search to files that match any of its glob patterns (for example, `{ '*.lua', 'lua/**' }` will only search in Lua files and files in 'lua' directory). The `grep_live` picker also has custom `<C-o>` mapping to add globs interactively after picker is opened.
3032
- FEATURE: update `help` picker to have `default_split` local option which customizes split direction of `choose` action (`<CR>` by default).
3133

doc/mini-pick.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ want to just use provided pickers.
229229
`source.items` defines items to choose from. It should be one of the following:
230230
- Array of objects which can have different types. Any type is allowed.
231231
- `nil`. Picker waits for explicit |MiniPick.set_picker_items()| call.
232-
- Callable returning any of the previous types. Will be called once on start.
232+
- Callable returning any of the previous types. Will be called once on start
233+
with source's `cwd` set as |current-directory|.
233234

234235
*MiniPick-source.items-stritems*
235236
Matching is done for items array based on the string representation of its
@@ -303,6 +304,9 @@ This is a part of source to allow persistent way to use relative paths,
303304
i.e. not depend on current directory being constant after picker start.
304305
It also makes the |MiniPick.builtin.resume()| picker more robust.
305306

307+
It will be set as local |current-directory| (|:lcd|) of picker's main window
308+
to allow simpler code for "in window" functions (choose/preview/custom/etc.).
309+
306310
Default value is |current-directory|.
307311

308312
*MiniPick-source.match*
@@ -1345,6 +1349,7 @@ Parameters ~
13451349
Will be called with array of lines as input, should return array of items.
13461350
Default: removes trailing empty lines and uses rest as string items.
13471351
- <spawn_opts> `(table)` - `options` for |uv.spawn|, except `args` and `stdio` fields.
1352+
Note: relative `cwd` path is resolved against active picker's `cwd`.
13481353
- <set_items_opts> `(table)` - table forwarded to |MiniPick.set_picker_items()|.
13491354

13501355
See also ~

lua/mini/pick.lua

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@
225225
--- `source.items` defines items to choose from. It should be one of the following:
226226
--- - Array of objects which can have different types. Any type is allowed.
227227
--- - `nil`. Picker waits for explicit |MiniPick.set_picker_items()| call.
228-
--- - Callable returning any of the previous types. Will be called once on start.
228+
--- - Callable returning any of the previous types. Will be called once on start
229+
--- with source's `cwd` set as |current-directory|.
229230
---
230231
--- *MiniPick-source.items-stritems*
231232
--- Matching is done for items array based on the string representation of its
@@ -299,6 +300,9 @@
299300
--- i.e. not depend on current directory being constant after picker start.
300301
--- It also makes the |MiniPick.builtin.resume()| picker more robust.
301302
---
303+
--- It will be set as local |current-directory| (|:lcd|) of picker's main window
304+
--- to allow simpler code for "in window" functions (choose/preview/custom/etc.).
305+
---
302306
--- Default value is |current-directory|.
303307
---
304308
--- *MiniPick-source.match*
@@ -1276,7 +1280,8 @@ MiniPick.builtin.files = function(local_opts, opts)
12761280
opts = vim.tbl_deep_extend('force', default_opts, opts or {})
12771281

12781282
if tool == 'fallback' then
1279-
opts.source.items = function() H.files_fallback_items(opts.source.cwd) end
1283+
local cwd = H.full_path(opts.source.cwd or vim.fn.getcwd())
1284+
opts.source.items = function() H.files_fallback_items(cwd) end
12801285
return MiniPick.start(opts)
12811286
end
12821287

@@ -1312,7 +1317,8 @@ MiniPick.builtin.grep = function(local_opts, opts)
13121317

13131318
local pattern = type(local_opts.pattern) == 'string' and local_opts.pattern or vim.fn.input('Grep pattern: ')
13141319
if tool == 'fallback' then
1315-
opts.source.items = function() H.grep_fallback_items(pattern, opts.source.cwd) end
1320+
local cwd = H.full_path(opts.source.cwd or vim.fn.getcwd())
1321+
opts.source.items = function() H.grep_fallback_items(pattern, cwd) end
13161322
return MiniPick.start(opts)
13171323
end
13181324

@@ -1347,7 +1353,8 @@ MiniPick.builtin.grep_live = function(local_opts, opts)
13471353
local default_source = { name = string.format('Grep live (%s%s)', tool, name_suffix), show = show }
13481354
opts = vim.tbl_deep_extend('force', { source = default_source }, opts or {})
13491355

1350-
local set_items_opts, spawn_opts = { do_match = false, querytick = H.querytick }, { cwd = opts.source.cwd }
1356+
local cwd = H.full_path(opts.source.cwd or vim.fn.getcwd())
1357+
local set_items_opts, spawn_opts = { do_match = false, querytick = H.querytick }, { cwd = cwd }
13511358
local process
13521359
local match = function(_, _, query)
13531360
pcall(vim.loop.process_kill, process)
@@ -1499,7 +1506,9 @@ MiniPick.builtin.cli = function(local_opts, opts)
14991506
local_opts = vim.tbl_deep_extend('force', { command = {}, postprocess = nil, spawn_opts = {} }, local_opts or {})
15001507
local name = string.format('CLI (%s)', tostring(local_opts.command[1] or ''))
15011508
opts = vim.tbl_deep_extend('force', { source = { name = name } }, opts or {})
1502-
local_opts.spawn_opts.cwd = local_opts.spawn_opts.cwd or opts.source.cwd
1509+
-- Explicitly use full path to not conflict with `set_picker_items_from_cli`
1510+
-- behavior of treating `spawn_opts.cwd` relative to source's cwd
1511+
local_opts.spawn_opts.cwd = H.full_path(local_opts.spawn_opts.cwd or opts.source.cwd or vim.fn.getcwd())
15031512

15041513
local command = local_opts.command
15051514
local set_from_cli_opts = { postprocess = local_opts.postprocess, spawn_opts = local_opts.spawn_opts }
@@ -1515,7 +1524,7 @@ MiniPick.builtin.resume = function()
15151524
H.cache = {}
15161525
local buf_id = H.picker_new_buf()
15171526
local win_target = vim.api.nvim_get_current_win()
1518-
local win_id = H.picker_new_win(buf_id, picker.opts.window.config)
1527+
local win_id = H.picker_new_win(buf_id, picker.opts.window.config, picker.opts.source.cwd)
15191528
picker.buffers = { main = buf_id }
15201529
picker.windows = { main = win_id, target = win_target }
15211530
picker.view_state = 'main'
@@ -1673,6 +1682,7 @@ end
16731682
--- Will be called with array of lines as input, should return array of items.
16741683
--- Default: removes trailing empty lines and uses rest as string items.
16751684
--- - <spawn_opts> `(table)` - `options` for |uv.spawn|, except `args` and `stdio` fields.
1685+
--- Note: relative `cwd` path is resolved against active picker's `cwd`.
16761686
--- - <set_items_opts> `(table)` - table forwarded to |MiniPick.set_picker_items()|.
16771687
---
16781688
---@seealso |MiniPick.get_picker_items()| and |MiniPick.get_picker_stritems()|
@@ -1732,9 +1742,11 @@ end
17321742
---@seealso |MiniPick.get_picker_opts()|
17331743
MiniPick.set_picker_opts = function(opts)
17341744
if not MiniPick.is_picker_active() then return end
1735-
H.pickers.active.opts = vim.tbl_deep_extend('force', H.pickers.active.opts, opts or {})
1736-
H.pickers.active.action_keys = H.normalize_mappings(H.pickers.active.opts.mappings)
1737-
H.picker_update(H.pickers.active, true, true)
1745+
local picker, cur_cwd = H.pickers.active, H.pickers.active.opts.source.cwd
1746+
picker.opts = vim.tbl_deep_extend('force', picker.opts, opts or {})
1747+
picker.action_keys = H.normalize_mappings(picker.opts.mappings)
1748+
if cur_cwd ~= picker.opts.source.cwd then H.win_set_cwd(picker.windows.main, picker.opts.source.cwd) end
1749+
H.picker_update(picker, true, true)
17381750
end
17391751

17401752
--- Set target window for active picker
@@ -2076,7 +2088,7 @@ H.picker_new = function(opts)
20762088

20772089
-- Create window
20782090
local win_target = vim.api.nvim_get_current_win()
2079-
local win_id = H.picker_new_win(buf_id, opts.window.config)
2091+
local win_id = H.picker_new_win(buf_id, opts.window.config, opts.source.cwd)
20802092

20812093
-- Construct and return object
20822094
local picker = {
@@ -2178,7 +2190,7 @@ H.picker_new_buf = function()
21782190
return buf_id
21792191
end
21802192

2181-
H.picker_new_win = function(buf_id, win_config)
2193+
H.picker_new_win = function(buf_id, win_config, cwd)
21822194
-- Hide cursor while picker is active (to not be visible in the window)
21832195
-- This mostly follows a hack from 'folke/noice.nvim'
21842196
H.cache.guicursor = vim.o.guicursor
@@ -2198,6 +2210,9 @@ H.picker_new_win = function(buf_id, win_config)
21982210
H.win_update_hl(win_id, 'FloatBorder', 'MiniPickBorder')
21992211
vim.fn.clearmatches(win_id)
22002212

2213+
-- Set window's local "current directory" for easier choose/preview/etc.
2214+
H.win_set_cwd(nil, cwd)
2215+
22012216
return win_id
22022217
end
22032218

@@ -3267,7 +3282,6 @@ end
32673282

32683283
H.files_fallback_items = function(cwd)
32693284
if vim.fn.has('nvim-0.9') == 0 then H.error('Tool "fallback" of `files` builtin needs Neovim>=0.9.') end
3270-
cwd = cwd or '.'
32713285
local poke_picker = H.poke_picker_throttle()
32723286
local f = function()
32733287
local items = {}
@@ -3311,7 +3325,6 @@ end
33113325

33123326
H.grep_fallback_items = function(pattern, cwd)
33133327
if vim.fn.has('nvim-0.9') == 0 then H.error('Tool "fallback" of `grep` builtin needs Neovim>=0.9.') end
3314-
cwd = cwd or '.'
33153328
local poke_picker = H.poke_picker_throttle()
33163329
local f = function()
33173330
local files, files_full = {}, {}
@@ -3478,6 +3491,14 @@ H.win_get_bottom_border = function(win_id)
34783491
return res or ' '
34793492
end
34803493

3494+
H.win_set_cwd = function(win_id, cwd)
3495+
-- Avoid needlessly setting cwd as it has side effects (like for `:buffers`)
3496+
if cwd == nil or vim.fn.getcwd(win_id or 0) == cwd then return end
3497+
local f = function() vim.cmd('lcd ' .. vim.fn.fnameescape(cwd)) end
3498+
if win_id == nil or win_id == vim.api.nvim_get_current_win() then return f() end
3499+
vim.api.nvim_win_call(f, win_id)
3500+
end
3501+
34813502
H.seq_along = function(arr)
34823503
if arr == nil then return nil end
34833504
local res = {}

tests/dir-pick/file

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dir-pick/file

tests/screenshots/tests-test_pick.lua---Info-view---is-updated-after-moving-marking-current-item

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
05|┌Info────────────────────┐
77
06|│General │
88
07|│Source name │ My name │
9-
08|│Source cwd │ mock/cur
9+
08|│Source cwd │ tests/di
1010
09|│Total items │ 3 │
1111
10|│Matched items │ 3 │
1212
11|│Marked items │ 0 │

tests/screenshots/tests-test_pick.lua---Info-view---is-updated-after-moving-marking-current-item-002

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
05|┌Info────────────────────┐
77
06|│General │
88
07|│Source name │ My name │
9-
08|│Source cwd │ mock/cur
9+
08|│Source cwd │ tests/di
1010
09|│Total items │ 3 │
1111
10|│Matched items │ 3 │
1212
11|│Marked items │ 0 │

tests/screenshots/tests-test_pick.lua---Info-view---is-updated-after-moving-marking-current-item-003

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
05|┌Info────────────────────┐
77
06|│General │
88
07|│Source name │ My name │
9-
08|│Source cwd │ mock/cur
9+
08|│Source cwd │ tests/di
1010
09|│Total items │ 3 │
1111
10|│Matched items │ 3 │
1212
11|│Marked items │ 1 │

tests/screenshots/tests-test_pick.lua---Info-view---is-updated-after-moving-marking-current-item-004

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
05|┌Info────────────────────┐
77
06|│General │
88
07|│Source name │ My name │
9-
08|│Source cwd │ mock/cur
9+
08|│Source cwd │ tests/di
1010
09|│Total items │ 3 │
1111
10|│Matched items │ 3 │
1212
11|│Marked items │ 0 │

tests/screenshots/tests-test_pick.lua---Info-view---respects-custom-mappings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
01|┌Info─────────────────────────────────┐
33
02|│General │
44
03|│Source name │ My name │
5-
04|│Source cwd │ mock/current-dir │
5+
04|│Source cwd │ tests/dir-pick
66
05|│Total items │ 3 │
77
06|│Matched items │ 3 │
88
07|│Marked items │ 0 │

tests/screenshots/tests-test_pick.lua---Info-view---supports-vertical-and-horizontal-scroll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
05|┌Info────────────────────┐
77
06|│l │
88
07|│ name │ <No name> │
9-
08|│ cwd │ mock/current-d
9+
08|│ cwd │ tests/dir-pick
1010
09|│items │ 1 │
1111
10|│d items │ 1 │
1212
11|│ items │ 0 │

0 commit comments

Comments
 (0)