mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-10 23:24:39 +00:00
Don't enqueue a repaint in the middle of one
This can easily lead to an infinite loop, if a variable handler triggers a repaint and the variable is set in the prompt, e.g. some of the git variables. A simple way to reproduce: function fish_mode_prompt commandline -f repaint end Repainting executes the mode prompt, which triggers a repaint, which triggers the mode prompt, .... So we just set a flag and check it. Fixes #7324.
This commit is contained in:
parent
c6cdc06a5b
commit
30b2dc2b97
4 changed files with 26 additions and 0 deletions
|
@ -294,8 +294,14 @@ maybe_t<int> builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_
|
||||||
return STATUS_INVALID_ARGS;
|
return STATUS_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using rl = readline_cmd_t;
|
||||||
for (i = w.woptind; i < argc; i++) {
|
for (i = w.woptind; i < argc; i++) {
|
||||||
if (auto mc = input_function_get_code(argv[i])) {
|
if (auto mc = input_function_get_code(argv[i])) {
|
||||||
|
// Don't enqueue a repaint if we're currently in the middle of one,
|
||||||
|
// because that's an infinite loop.
|
||||||
|
if (mc == rl::repaint_mode || mc == rl::force_repaint || mc == rl::repaint) {
|
||||||
|
if (ld.is_repaint) continue;
|
||||||
|
}
|
||||||
// Inserts the readline function at the back of the queue.
|
// Inserts the readline function at the back of the queue.
|
||||||
reader_queue_ch(*mc);
|
reader_queue_ch(*mc);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -151,6 +151,10 @@ struct library_data_t {
|
||||||
/// Number of recursive calls to builtin_complete().
|
/// Number of recursive calls to builtin_complete().
|
||||||
uint32_t builtin_complete_recursion_level{0};
|
uint32_t builtin_complete_recursion_level{0};
|
||||||
|
|
||||||
|
/// If we're currently repainting the commandline.
|
||||||
|
/// Useful to stop infinite loops.
|
||||||
|
bool is_repaint{false};
|
||||||
|
|
||||||
/// Whether we called builtin_complete -C without parameter.
|
/// Whether we called builtin_complete -C without parameter.
|
||||||
bool builtin_complete_current_commandline{false};
|
bool builtin_complete_current_commandline{false};
|
||||||
|
|
||||||
|
|
|
@ -2781,10 +2781,17 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
||||||
//
|
//
|
||||||
// Because some users set `fish_mode_prompt` to an empty function and display the mode
|
// Because some users set `fish_mode_prompt` to an empty function and display the mode
|
||||||
// elsewhere, we detect if the mode output is empty.
|
// elsewhere, we detect if the mode output is empty.
|
||||||
|
|
||||||
|
// Don't go into an infinite loop of repainting.
|
||||||
|
// This can happen e.g. if a variable triggers a repaint,
|
||||||
|
// and the variable is set inside the prompt (#7324).
|
||||||
|
// builtin commandline will refuse to enqueue these.
|
||||||
|
parser().libdata().is_repaint = true;
|
||||||
exec_mode_prompt();
|
exec_mode_prompt();
|
||||||
if (!mode_prompt_buff.empty()) {
|
if (!mode_prompt_buff.empty()) {
|
||||||
s_reset_line(&screen, true /* redraw prompt */);
|
s_reset_line(&screen, true /* redraw prompt */);
|
||||||
if (this->is_repaint_needed()) this->layout_and_repaint(L"mode");
|
if (this->is_repaint_needed()) this->layout_and_repaint(L"mode");
|
||||||
|
parser().libdata().is_repaint = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Else we repaint as normal.
|
// Else we repaint as normal.
|
||||||
|
@ -2792,10 +2799,12 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
|
||||||
}
|
}
|
||||||
case rl::force_repaint:
|
case rl::force_repaint:
|
||||||
case rl::repaint: {
|
case rl::repaint: {
|
||||||
|
parser().libdata().is_repaint = true;
|
||||||
exec_prompt();
|
exec_prompt();
|
||||||
s_reset_line(&screen, true /* redraw prompt */);
|
s_reset_line(&screen, true /* redraw prompt */);
|
||||||
this->layout_and_repaint(L"readline");
|
this->layout_and_repaint(L"readline");
|
||||||
force_exec_prompt_and_repaint = false;
|
force_exec_prompt_and_repaint = false;
|
||||||
|
parser().libdata().is_repaint = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case rl::complete:
|
case rl::complete:
|
||||||
|
|
|
@ -23,3 +23,10 @@ sendline(
|
||||||
expect_prompt()
|
expect_prompt()
|
||||||
sendline("echo one \"two three\" four'five six'{7} 'eight~")
|
sendline("echo one \"two three\" four'five six'{7} 'eight~")
|
||||||
expect_prompt("\r\n@GUARD:2@\r\n(.*)\r\n@/GUARD:2@\r\n")
|
expect_prompt("\r\n@GUARD:2@\r\n(.*)\r\n@/GUARD:2@\r\n")
|
||||||
|
|
||||||
|
# Check that we don't infinitely loop here.
|
||||||
|
sendline("function fish_mode_prompt; commandline -f repaint; end")
|
||||||
|
expect_prompt()
|
||||||
|
|
||||||
|
sendline("echo foo")
|
||||||
|
expect_prompt("foo")
|
||||||
|
|
Loading…
Reference in a new issue