feat(defaults): shelltemp=false #33012

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
Alexej Kowalew
2025-04-12 17:24:42 +02:00
committed by GitHub
parent c8fbb0d2ee
commit d77d961b35
11 changed files with 140 additions and 64 deletions

View File

@ -81,6 +81,7 @@ OPTIONS
• 'chistory' and 'lhistory' set size of the |quickfix-stack|.
• 'diffopt' `inline:` configures diff highlighting for changes within a line.
• 'pummaxwidth' sets maximum width for the completion popup menu.
• 'shelltemp' defaults to "false".
PLUGINS

View File

@ -5411,7 +5411,7 @@ A jump table for the options with a short description can be found at |Q_op|.
< Also see 'completeslash'.
*'shelltemp'* *'stmp'* *'noshelltemp'* *'nostmp'*
'shelltemp' 'stmp' boolean (default on)
'shelltemp' 'stmp' boolean (default off)
global
When on, use temp files for shell commands. When off use a pipe.
When using a pipe is not possible temp files are used anyway.

View File

@ -5716,7 +5716,7 @@ vim.go.ssl = vim.go.shellslash
--- `system()` does not respect this option, it always uses pipes.
---
--- @type boolean
vim.o.shelltemp = true
vim.o.shelltemp = false
vim.o.stmp = vim.o.shelltemp
vim.go.shelltemp = vim.o.shelltemp
vim.go.stmp = vim.go.shelltemp

View File

@ -1146,7 +1146,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
}
// Create the shell command in allocated memory.
char *cmd_buf = make_filter_cmd(cmd, itmp, otmp);
char *cmd_buf = make_filter_cmd(cmd, itmp, otmp, do_in);
ui_cursor_goto(Rows - 1, 0);
if (do_out) {
@ -1344,8 +1344,9 @@ static char *find_pipe(const char *cmd)
/// @param cmd Command to execute.
/// @param itmp NULL or the input file.
/// @param otmp NULL or the output file.
/// @param do_in true if stdin is needed.
/// @returns an allocated string with the shell command.
char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
char *make_filter_cmd(char *cmd, char *itmp, char *otmp, bool do_in)
{
bool is_fish_shell =
#if defined(UNIX)
@ -1367,6 +1368,11 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
len += is_pwsh ? strlen(itmp) + sizeof("& { Get-Content " " | & " " }") - 1 + 6 // +6: #20530
: strlen(itmp) + sizeof(" { " " < " " } ") - 1;
}
if (do_in && is_pwsh) {
len += sizeof(" $input | ");
}
if (otmp != NULL) {
len += strlen(otmp) + strlen(p_srr) + 2; // two extra spaces (" "),
}
@ -1380,8 +1386,11 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
xstrlcat(buf, " | & ", len - 1); // FIXME: add `&` ourself or leave to user?
xstrlcat(buf, cmd, len - 1);
xstrlcat(buf, " }", len - 1);
} else if (do_in) {
xstrlcpy(buf, " $input | ", len - 1);
xstrlcat(buf, cmd, len);
} else {
xstrlcpy(buf, cmd, len - 1);
xstrlcpy(buf, cmd, len);
}
} else {
#if defined(UNIX)

View File

@ -7621,7 +7621,7 @@ local options = {
},
{
abbreviation = 'stmp',
defaults = true,
defaults = false,
desc = [=[
When on, use temp files for shell commands. When off use a pipe.
When using a pipe is not possible temp files are used anyway.

View File

@ -778,7 +778,7 @@ char *get_cmd_output(char *cmd, char *infile, int flags, size_t *ret_len)
}
// Add the redirection stuff
char *command = make_filter_cmd(cmd, infile, tempname);
char *command = make_filter_cmd(cmd, infile, tempname, false);
// Call the shell to execute the command (errors are ignored).
// Don't check timestamps here.
@ -1253,11 +1253,19 @@ static size_t write_output(char *output, size_t remaining, bool eof)
char *start = output;
size_t off = 0;
while (off < remaining) {
if (output[off] == NL) {
// CRLF
if (output[off] == CAR && output[off + 1] == NL) {
output[off] = NUL;
ml_append(curwin->w_cursor.lnum++, output, (int)off + 1, false);
size_t skip = off + 2;
output += skip;
remaining -= skip;
off = 0;
continue;
} else if (output[off] == CAR || output[off] == NL) {
// Insert the line
output[off] = NUL;
ml_append(curwin->w_cursor.lnum++, output, (int)off + 1,
false);
ml_append(curwin->w_cursor.lnum++, output, (int)off + 1, false);
size_t skip = off + 1;
output += skip;
remaining -= skip;
@ -1276,7 +1284,7 @@ static size_t write_output(char *output, size_t remaining, bool eof)
if (remaining) {
// append unfinished line
ml_append(curwin->w_cursor.lnum++, output, 0, false);
// remember that the NL was missing
// remember that the line ending was missing
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
output += remaining;
} else {

View File

@ -23,22 +23,47 @@ describe(':make', function()
n.set_shell_powershell()
end)
it('captures stderr & non zero exit code #14349', function()
it('captures stderr & non zero exit code using "commands" #14349', function()
api.nvim_set_option_value('makeprg', testprg('shell-test') .. ' foo', {})
local out = eval('execute("make")')
-- Error message is captured in the file and printed in the footer
matches(
'[\r\n]+.*[\r\n]+Unknown first argument%: foo[\r\n]+%(1 of 1%)%: Unknown first argument%: foo',
out
)
matches('[\r\n]+.*[\r\n]+%(1 of 1%)%: Unknown first argument%: foo', out)
end)
it('captures stderr & zero exit code #14349', function()
it('captures stderr & zero exit code using "commands" #14349', function()
api.nvim_set_option_value('makeprg', testprg('shell-test'), {})
local out = eval('execute("make")')
-- Ensure there are no "shell returned X" messages between
-- command and last line (indicating zero exit)
matches('LastExitCode%s+ready [$]%s+[(]', out)
matches('[\n]+%(1 of 1%)%: ready [$]', out)
end)
it('captures stderr & non zero exit code using "cmdlets"', function()
api.nvim_set_option_value(
'shellpipe',
'2>&1 | Tee-Object -FilePath %s; exit $LastExitCode',
{}
)
api.nvim_set_option_value('makeprg', testprg('shell-test') .. ' foo', {})
local out = eval('execute("make")')
-- Error message is captured in the file and printed in the footer
matches(
'[\r\n]+.*[\r\n]+.*Unknown first argument%: foo%^%[%[0m[\r\n]+shell returned 3[\r\n]+%(1 of 1%)%: Unknown first argument%: foo',
out
)
end)
it('captures stderr & zero exit code using "cmdlets"', function()
api.nvim_set_option_value(
'shellpipe',
'2>&1 | Tee-Object -FilePath %s; exit $LastExitCode',
{}
)
api.nvim_set_option_value('makeprg', testprg('shell-test'), {})
local out = eval('execute("make")')
-- Ensure there are no "shell returned X" messages between
-- command and last line (indicating zero exit)
matches('.*ready [$]%s+%^%[%[0m', out)
matches('\n.*%: ready [$]', out)
end)
end)

View File

@ -35,6 +35,11 @@ local function prepare_gz_file(name, text)
eq(nil, vim.uv.fs_stat(name))
end
local function prepare_file(name, text)
os.remove(name)
write_file(name, text)
end
describe('file reading, writing and bufnew and filter autocommands', function()
local text1 = dedent([[
start of testfile
@ -63,23 +68,27 @@ describe('file reading, writing and bufnew and filter autocommands', function()
end)
teardown(function()
os.remove('Xtestfile.gz')
os.remove('Xtestfile')
os.remove('XtestfileByFileReadPost')
os.remove('Xtest.c')
os.remove('test.out')
end)
it('FileReadPost', function()
feed_command('set bin')
prepare_file('Xtestfile', text1)
os.remove('XtestfileByFileReadPost')
--execute('au FileChangedShell * echo "caught FileChangedShell"')
feed_command("au FileReadPost Xtestfile '[,']w XtestfileByFileReadPost")
-- Read the testfile.
feed_command('$r Xtestfile')
expect('\n' .. text1)
eq(text1, read_file('XtestfileByFileReadPost'))
end)
if not has_gzip() then
pending('skipped (missing `gzip` utility)', function() end)
else
it('FileReadPost (using gzip)', function()
prepare_gz_file('Xtestfile', text1)
--execute('au FileChangedShell * echo "caught FileChangedShell"')
feed_command('set bin')
feed_command("au FileReadPost *.gz '[,']!gzip -d")
-- Read and decompress the testfile.
feed_command('$r Xtestfile.gz')
expect('\n' .. text1)
end)
it('BufReadPre, BufReadPost (using gzip)', function()
prepare_gz_file('Xtestfile', text1)
local gzip_data = read_file('Xtestfile.gz')
@ -112,18 +121,18 @@ describe('file reading, writing and bufnew and filter autocommands', function()
-- Discard all prompts and messages.
feed('<C-L>')
expect([[
start of testfiLe
Line 2 Abcdefghijklmnopqrstuvwxyz
Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 4 Abcdefghijklmnopqrstuvwxyz
Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 6 Abcdefghijklmnopqrstuvwxyz
Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 8 Abcdefghijklmnopqrstuvwxyz
Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 10 Abcdefghijklmnopqrstuvwxyz
end of testfiLe]])
start of testfiLe
Line 2 Abcdefghijklmnopqrstuvwxyz
Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 4 Abcdefghijklmnopqrstuvwxyz
Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 6 Abcdefghijklmnopqrstuvwxyz
Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 8 Abcdefghijklmnopqrstuvwxyz
Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Line 10 Abcdefghijklmnopqrstuvwxyz
end of testfiLe]])
end)
end

View File

@ -642,7 +642,7 @@ function M.source(code)
end
function M.has_powershell()
return M.eval('executable("' .. (is_os('win') and 'powershell' or 'pwsh') .. '")') == 1
return M.eval('executable("pwsh")') == 1
end
--- Sets Nvim shell to powershell.
@ -655,7 +655,7 @@ function M.set_shell_powershell(fake)
if not fake then
assert(found)
end
local shell = found and (is_os('win') and 'powershell' or 'pwsh') or M.testprg('pwsh-test')
local shell = found and 'pwsh' or M.testprg('pwsh-test')
local cmd = 'Remove-Item -Force '
.. table.concat(
is_os('win') and { 'alias:cat', 'alias:echo', 'alias:sleep', 'alias:sort', 'alias:tee' }
@ -671,7 +671,7 @@ function M.set_shell_powershell(fake)
let &shellcmdflag .= '$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';'
let &shellcmdflag .= ']] .. cmd .. [['
let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode'
let &shellpipe = '2>&1 | %%{ "$_" } | tee %s; exit $LastExitCode'
let &shellpipe = '> %s 2>&1'
]])
return found
end

View File

@ -558,9 +558,12 @@ end)
describe('shell :!', function()
before_each(clear)
it(':{range}! with powershell filter/redirect #16271 #19250', function()
it(':{range}! with powershell using "commands" filter/redirect #16271 #19250', function()
if not n.has_powershell() then
return
end
local screen = Screen.new(500, 8)
local found = n.set_shell_powershell(true)
n.set_shell_powershell()
insert([[
3
1
@ -569,23 +572,44 @@ describe('shell :!', function()
if is_os('win') then
feed(':4verbose %!sort /R<cr>')
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
any = [[Executing command: " $input | sort /R".*]],
}
else
feed(':4verbose %!sort -r<cr>')
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
any = [[Executing command: " $input | sort %-r".*]],
}
end
feed('<CR>')
if found then
-- Not using fake powershell, so we can test the result.
expect([[
4
3
2
1]])
expect([[
4
3
2
1]])
end)
it(':{range}! with powershell using "cmdlets" filter/redirect #16271 #19250', function()
if not n.has_powershell() then
pending('powershell not found', function() end)
return
end
local screen = Screen.new(500, 8)
n.set_shell_powershell()
insert([[
3
1
4
2]])
feed(':4verbose %!Sort-Object -Descending<cr>')
screen:expect {
any = [[Executing command: " $input | Sort%-Object %-Descending".*]],
}
feed('<CR>')
expect([[
4
3
2
1]])
end)
it(':{range}! without redirecting to buffer', function()
@ -596,20 +620,19 @@ describe('shell :!', function()
4
2]])
feed(':4verbose %w !sort<cr>')
if is_os('win') then
screen:expect {
any = [[Executing command: .?sort %< .*]],
}
else
screen:expect {
any = [[Executing command: .?%(sort%) %< .*]],
}
end
screen:expect {
any = [[Executing command: "sort".*]],
}
feed('<CR>')
if not n.has_powershell() then
return
end
n.set_shell_powershell(true)
feed(':4verbose %w !sort<cr>')
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort }]],
any = [[Executing command: " $input | sort".*]],
}
feed('<CR>')
n.expect_exit(command, 'qall!')

View File

@ -19,6 +19,7 @@ if exists('s:did_load')
set nohlsearch noincsearch
set nrformats=bin,octal,hex
set shortmess=filnxtToOS
set shelltemp
set sidescroll=0
set tags=./tags,tags
set undodir^=.