Compare commits

...

5 Commits

5 changed files with 136 additions and 64 deletions

View File

@ -2972,6 +2972,7 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
• skip: (fun(dir_name: string): boolean)|nil Predicate to • skip: (fun(dir_name: string): boolean)|nil Predicate to
control traversal. Return false to stop searching the control traversal. Return false to stop searching the
current directory. Only useful when depth > 1 current directory. Only useful when depth > 1
• follow: boolean|nil Follow symbolic links. (default: true)
Return: ~ Return: ~
(`Iterator`) over items in {path}. Each iteration yields two values: (`Iterator`) over items in {path}. Each iteration yields two values:
@ -3013,7 +3014,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
-- get all files ending with .cpp or .hpp inside lib/ -- get all files ending with .cpp or .hpp inside lib/
local cpp_hpp = vim.fs.find(function(name, path) local cpp_hpp = vim.fs.find(function(name, path)
return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
end, {limit = math.huge, type = 'file'}) end, {limit = math.huge, type = 'file'})
< <
@ -3027,8 +3028,10 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
If {names} is a function, it is called for each traversed If {names} is a function, it is called for each traversed
item with args: item with args:
• name: base name of the current item • name: base name of the current item
• path: full path of the current item The function should • path: full path of the current item
return `true` if the given item is considered a match.
The function should return `true` if the given item is
considered a match.
• {opts} (`table`) Optional keyword arguments: • {opts} (`table`) Optional keyword arguments:
• {path}? (`string`) Path to begin searching from. If • {path}? (`string`) Path to begin searching from. If
omitted, the |current-directory| is used. omitted, the |current-directory| is used.
@ -3042,6 +3045,8 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
• {limit}? (`number`, default: `1`) Stop the search after • {limit}? (`number`, default: `1`) Stop the search after
finding this many matches. Use `math.huge` to place no finding this many matches. Use `math.huge` to place no
limit on the number of matches. limit on the number of matches.
• {follow}? (`boolean`, default: `true`) Follow symbolic
links.
Return: ~ Return: ~
(`string[]`) Normalized paths |vim.fs.normalize()| of all matching (`string[]`) Normalized paths |vim.fs.normalize()| of all matching

View File

@ -127,6 +127,7 @@ end
--- - skip: (fun(dir_name: string): boolean)|nil Predicate --- - skip: (fun(dir_name: string): boolean)|nil Predicate
--- to control traversal. Return false to stop searching the current directory. --- to control traversal. Return false to stop searching the current directory.
--- Only useful when depth > 1 --- Only useful when depth > 1
--- - follow: boolean|nil Follow symbolic links. (default: true)
--- ---
---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type". ---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type".
--- "name" is the basename of the item relative to {path}. --- "name" is the basename of the item relative to {path}.
@ -138,6 +139,7 @@ function M.dir(path, opts)
vim.validate('path', path, 'string') vim.validate('path', path, 'string')
vim.validate('depth', opts.depth, 'number', true) vim.validate('depth', opts.depth, 'number', true)
vim.validate('skip', opts.skip, 'function', true) vim.validate('skip', opts.skip, 'function', true)
vim.validate('follow', opts.follow, 'boolean', true)
path = M.normalize(path) path = M.normalize(path)
if not opts.depth or opts.depth == 1 then if not opts.depth or opts.depth == 1 then
@ -168,7 +170,9 @@ function M.dir(path, opts)
if if
opts.depth opts.depth
and level < opts.depth and level < opts.depth
and t == 'directory' and (t == 'directory' or (t == 'link' and opts.follow ~= false and (vim.uv.fs_stat(
M.joinpath(path, f)
) or {}).type == 'directory'))
and (not opts.skip or opts.skip(f) ~= false) and (not opts.skip or opts.skip(f) ~= false)
then then
dirs[#dirs + 1] = { f, level + 1 } dirs[#dirs + 1] = { f, level + 1 }
@ -202,6 +206,10 @@ end
--- Use `math.huge` to place no limit on the number of matches. --- Use `math.huge` to place no limit on the number of matches.
--- (default: `1`) --- (default: `1`)
--- @field limit? number --- @field limit? number
---
--- Follow symbolic links.
--- (default: `true`)
--- @field follow? boolean
--- Find files or directories (or other items as specified by `opts.type`) in the given path. --- Find files or directories (or other items as specified by `opts.type`) in the given path.
--- ---
@ -225,7 +233,7 @@ end
--- ---
--- -- get all files ending with .cpp or .hpp inside lib/ --- -- get all files ending with .cpp or .hpp inside lib/
--- local cpp_hpp = vim.fs.find(function(name, path) --- local cpp_hpp = vim.fs.find(function(name, path)
--- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') --- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
--- end, {limit = math.huge, type = 'file'}) --- end, {limit = math.huge, type = 'file'})
--- ``` --- ```
--- ---
@ -235,6 +243,7 @@ end
--- If {names} is a function, it is called for each traversed item with args: --- If {names} is a function, it is called for each traversed item with args:
--- - name: base name of the current item --- - name: base name of the current item
--- - path: full path of the current item --- - path: full path of the current item
---
--- The function should return `true` if the given item is considered a match. --- The function should return `true` if the given item is considered a match.
--- ---
---@param opts vim.fs.find.Opts Optional keyword arguments: ---@param opts vim.fs.find.Opts Optional keyword arguments:
@ -247,6 +256,7 @@ function M.find(names, opts)
vim.validate('stop', opts.stop, 'string', true) vim.validate('stop', opts.stop, 'string', true)
vim.validate('type', opts.type, 'string', true) vim.validate('type', opts.type, 'string', true)
vim.validate('limit', opts.limit, 'number', true) vim.validate('limit', opts.limit, 'number', true)
vim.validate('follow', opts.follow, 'boolean', true)
if type(names) == 'string' then if type(names) == 'string' then
names = { names } names = { names }
@ -336,7 +346,14 @@ function M.find(names, opts)
end end
end end
if type_ == 'directory' then if
type_ == 'directory'
or (
type_ == 'link'
and opts.follow ~= false
and (vim.uv.fs_stat(f) or {}).type == 'directory'
)
then
dirs[#dirs + 1] = f dirs[#dirs + 1] = f
end end
end end

View File

@ -641,9 +641,6 @@ bool terminal_enter(void)
curwin->w_p_so = 0; curwin->w_p_so = 0;
curwin->w_p_siso = 0; curwin->w_p_siso = 0;
// Save the existing cursor entry since it may be modified by the application
cursorentry_T save_cursorentry = shape_table[SHAPE_IDX_TERM];
// Update the cursor shape table and flush changes to the UI // Update the cursor shape table and flush changes to the UI
s->term->pending.cursor = true; s->term->pending.cursor = true;
refresh_cursor(s->term); refresh_cursor(s->term);
@ -674,8 +671,8 @@ bool terminal_enter(void)
RedrawingDisabled = s->save_rd; RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
shape_table[SHAPE_IDX_TERM] = save_cursorentry; // Restore the terminal cursor to what is set in 'guicursor'
ui_mode_info_set(); (void)parse_shape_opt(SHAPE_CURSOR);
if (save_curwin == curwin->handle) { // Else: window was closed. if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul; curwin->w_p_cul = save_w_p_cul;

View File

@ -152,7 +152,7 @@ describe('vim.fs', function()
) )
end) end)
it('works with opts.depth and opts.skip', function() it('works with opts.depth, opts.skip and opts.follow', function()
io.open('testd/a1', 'w'):close() io.open('testd/a1', 'w'):close()
io.open('testd/b1', 'w'):close() io.open('testd/b1', 'w'):close()
io.open('testd/c1', 'w'):close() io.open('testd/c1', 'w'):close()
@ -166,7 +166,7 @@ describe('vim.fs', function()
io.open('testd/a/b/c/b4', 'w'):close() io.open('testd/a/b/c/b4', 'w'):close()
io.open('testd/a/b/c/c4', 'w'):close() io.open('testd/a/b/c/c4', 'w'):close()
local function run(dir, depth, skip) local function run(dir, depth, skip, follow)
return exec_lua(function() return exec_lua(function()
local r = {} local r = {}
local skip_f local skip_f
@ -177,7 +177,7 @@ describe('vim.fs', function()
end end
end end
end end
for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f }) do for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f, follow = follow }) do
r[name] = type_ r[name] = type_
end end
return r return r
@ -197,6 +197,7 @@ describe('vim.fs', function()
exp['a/b2'] = 'file' exp['a/b2'] = 'file'
exp['a/c2'] = 'file' exp['a/c2'] = 'file'
exp['a/b'] = 'directory' exp['a/b'] = 'directory'
local lexp = vim.deepcopy(exp)
eq(exp, run('testd', 2)) eq(exp, run('testd', 2))
@ -213,10 +214,32 @@ describe('vim.fs', function()
exp['a/b/c/c4'] = 'file' exp['a/b/c/c4'] = 'file'
eq(exp, run('testd', 999)) eq(exp, run('testd', 999))
vim.uv.fs_symlink('a', 'testd/l', { junction = true, dir = true })
lexp['l'] = 'link'
eq(lexp, run('testd', 2, nil, false))
lexp['l/a2'] = 'file'
lexp['l/b2'] = 'file'
lexp['l/c2'] = 'file'
lexp['l/b'] = 'directory'
eq(lexp, run('testd', 2, nil, true))
end) end)
end) end)
describe('find()', function() describe('find()', function()
before_each(function()
vim.uv.fs_symlink(
test_source_path .. '/build',
test_source_path .. '/build_link',
{ junction = true, dir = true }
)
end)
after_each(function()
vim.uv.fs_unlink(test_source_path .. '/build_link')
end)
it('works', function() it('works', function()
eq( eq(
{ test_build_dir .. '/build' }, { test_build_dir .. '/build' },
@ -224,6 +247,26 @@ describe('vim.fs', function()
) )
eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' })) eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' }))
eq(
{ nvim_prog, test_source_path .. '/build_link/bin/' .. nvim_prog_basename },
vim.fs.find(nvim_prog_basename, {
path = test_source_path,
type = 'file',
limit = 2,
follow = true,
})
)
eq(
{ nvim_prog },
vim.fs.find(nvim_prog_basename, {
path = test_source_path,
type = 'file',
limit = 2,
follow = false,
})
)
local parent, name = nvim_dir:match('^(.*/)([^/]+)$') local parent, name = nvim_dir:match('^(.*/)([^/]+)$')
eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' })) eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' }))
end) end)

View File

@ -15,9 +15,20 @@ local skip = t.skip
describe(':terminal cursor', function() describe(':terminal cursor', function()
local screen local screen
local terminal_mode_idx ---@type number
before_each(function() before_each(function()
clear() clear()
screen = tt.setup_screen() screen = tt.setup_screen()
if terminal_mode_idx == nil then
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
terminal_mode_idx = i
end
end
assert(terminal_mode_idx)
end
end) end)
it('moves the screen cursor when focused', function() it('moves the screen cursor when focused', function()
@ -143,13 +154,6 @@ describe(':terminal cursor', function()
it('can be modified by application #3681', function() it('can be modified by application #3681', function()
skip(is_os('win'), '#31587') skip(is_os('win'), '#31587')
local idx ---@type number
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
idx = i
end
end
assert(idx)
local states = { local states = {
[1] = { blink = true, shape = 'block' }, [1] = { blink = true, shape = 'block' },
@ -171,13 +175,13 @@ describe(':terminal cursor', function()
]], ]],
condition = function() condition = function()
if v.blink then if v.blink then
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
else else
eq(0, screen._mode_info[idx].blinkon) eq(0, screen._mode_info[terminal_mode_idx].blinkon)
eq(0, screen._mode_info[idx].blinkoff) eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
end end
eq(v.shape, screen._mode_info[idx].cursor_shape) eq(v.shape, screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
end end
@ -191,20 +195,13 @@ describe(':terminal cursor', function()
]]) ]])
-- Cursor returns to default on TermLeave -- Cursor returns to default on TermLeave
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('block', screen._mode_info[idx].cursor_shape) eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
end) end)
it('can be modified per terminal', function() it('can be modified per terminal', function()
skip(is_os('win'), '#31587') skip(is_os('win'), '#31587')
local idx ---@type number
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
idx = i
end
end
assert(idx)
-- Set cursor to vertical bar with blink -- Set cursor to vertical bar with blink
tt.feed_csi('5 q') tt.feed_csi('5 q')
@ -216,9 +213,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]], ]],
condition = function() condition = function()
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape) eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
@ -231,9 +228,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]], ]],
condition = function() condition = function()
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape) eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
@ -256,9 +253,9 @@ describe(':terminal cursor', function()
]], ]],
condition = function() condition = function()
-- New terminal, cursor resets to defaults -- New terminal, cursor resets to defaults
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('block', screen._mode_info[idx].cursor_shape) eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
@ -275,9 +272,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]], ]],
condition = function() condition = function()
eq(0, screen._mode_info[idx].blinkon) eq(0, screen._mode_info[terminal_mode_idx].blinkon)
eq(0, screen._mode_info[idx].blinkoff) eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
eq('horizontal', screen._mode_info[idx].cursor_shape) eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
@ -294,9 +291,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]], ]],
condition = function() condition = function()
eq(500, screen._mode_info[idx].blinkon) eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff) eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape) eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end, end,
}) })
end) end)
@ -326,6 +323,32 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]]) ]])
end) end)
it('preserves guicursor value on TermLeave #31612', function()
eq(3, screen._mode_info[terminal_mode_idx].hl_id)
-- Change 'guicursor' while terminal mode is active
command('set guicursor+=t:Error')
local error_hl_id = call('hlID', 'Error')
screen:expect({
condition = function()
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end,
})
-- Exit terminal mode
feed([[<C-\><C-N>]])
screen:expect([[
tty ready |
^ |
|*5
]])
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end)
end) end)
describe('buffer cursor position is correct in terminal without number column', function() describe('buffer cursor position is correct in terminal without number column', function()
@ -350,12 +373,6 @@ describe('buffer cursor position is correct in terminal without number column',
}, { }, {
cols = 70, cols = 70,
}) })
screen:set_default_attr_ids({
[1] = { foreground = 253, background = 11 },
[2] = { reverse = true },
[3] = { bold = true },
[4] = { background = 11 },
})
-- Also check for real cursor position, as it is used for stuff like input methods -- Also check for real cursor position, as it is used for stuff like input methods
screen._handle_busy_start = function() end screen._handle_busy_start = function() end
screen._handle_busy_stop = function() end screen._handle_busy_stop = function() end
@ -667,13 +684,6 @@ describe('buffer cursor position is correct in terminal with number column', fun
}, { }, {
cols = 70, cols = 70,
}) })
screen:set_default_attr_ids({
[1] = { foreground = 253, background = 11 },
[2] = { reverse = true },
[3] = { bold = true },
[4] = { background = 11 },
[7] = { foreground = 130 },
})
-- Also check for real cursor position, as it is used for stuff like input methods -- Also check for real cursor position, as it is used for stuff like input methods
screen._handle_busy_start = function() end screen._handle_busy_start = function() end
screen._handle_busy_stop = function() end screen._handle_busy_stop = function() end