Skip to content

Commit e592169

Browse files
authored
REPL: Query libuv for virtual terminal support (#57132)
The query-and-restore logic here was pretty flawed: - it had no way to guarantee when the "default" mode is available to query, so it could easily save a "bad" mode - it did not ensure / check whether the "default" mode is compatible with the output generated by REPL (esp. ASCII escape codes / color) - it persisted the "default" mode from pre-compilation to runtime, causing #56073 With this PR, `ENABLE_VIRTUAL_TERMINAL_PROCESSING` is the only flag that we're agnostic about. It was added relatively recently (Windows 10 version 1511), and `libuv` has support to emulate its behavior when it's not available natively. We query `libuv` directly to ask whether it is expecting to emulate, rather than measuring the console mode at some random point in time. Otherwise this PR resets ENABLE_PROCESSED_OUTPUT and ENABLE_WRAP_AT_EOL_OUTPUT always, since we output ASCII control sequences unconditionally. Resolves #56073.
1 parent 67e992d commit e592169

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

stdlib/REPL/src/LineEdit.jl

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,19 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf
595595
line_pos = buf_pos
596596
regstart, regstop = region(buf)
597597
written = 0
598+
@static if Sys.iswindows()
599+
writer = Terminals.pipe_writer(terminal)
600+
if writer isa Base.TTY && !Base.ispty(writer)::Bool
601+
_reset_console_mode(writer.handle)
602+
end
603+
end
598604
# Write out the prompt string
599605
lindent = write_prompt(termbuf, prompt, hascolor(terminal))::Int
600606
# Count the '\n' at the end of the line if the terminal emulator does (specific to DOS cmd prompt)
601-
miscountnl = @static Sys.iswindows() ? (isa(Terminals.pipe_reader(terminal), Base.TTY) && !(Base.ispty(Terminals.pipe_reader(terminal)))::Bool) : false
607+
miscountnl = @static if Sys.iswindows()
608+
reader = Terminals.pipe_reader(terminal)
609+
reader isa Base.TTY && !Base.ispty(reader)::Bool
610+
else false end
602611

603612
# Now go through the buffer line by line
604613
seek(buf, 0)
@@ -1676,34 +1685,39 @@ end
16761685
# not leave the console mode in a corrupt state.
16771686
# FIXME: remove when pseudo-tty are implemented for child processes
16781687
if Sys.iswindows()
1679-
function _console_mode()
1680-
hOutput = ccall(:GetStdHandle, stdcall, Ptr{Cvoid}, (UInt32,), -11 % UInt32) # STD_OUTPUT_HANDLE
1681-
dwMode = Ref{UInt32}()
1682-
ccall(:GetConsoleMode, stdcall, Int32, (Ref{Cvoid}, Ref{UInt32}), hOutput, dwMode)
1683-
return dwMode[]
1684-
end
1685-
const default_console_mode_ref = Ref{UInt32}()
1686-
const default_console_mode_assigned = Ref(false)
1687-
function get_default_console_mode()
1688-
if default_console_mode_assigned[] == false
1689-
default_console_mode_assigned[] = true
1690-
default_console_mode_ref[] = _console_mode()
1691-
end
1692-
return default_console_mode_ref[]
1693-
end
1694-
function _reset_console_mode()
1695-
mode = _console_mode()
1696-
if mode !== get_default_console_mode()
1697-
hOutput = ccall(:GetStdHandle, stdcall, Ptr{Cvoid}, (UInt32,), -11 % UInt32) # STD_OUTPUT_HANDLE
1698-
ccall(:SetConsoleMode, stdcall, Int32, (Ptr{Cvoid}, UInt32), hOutput, default_console_mode_ref[])
1688+
1689+
#= Get/SetConsoleMode flags =#
1690+
const ENABLE_PROCESSED_OUTPUT = UInt32(0x0001)
1691+
const ENABLE_WRAP_AT_EOL_OUTPUT = UInt32(0x0002)
1692+
const ENABLE_VIRTUAL_TERMINAL_PROCESSING = UInt32(0x0004)
1693+
const DISABLE_NEWLINE_AUTO_RETURN = UInt32(0x0008)
1694+
const ENABLE_LVB_GRID_WORLDWIDE = UInt32(0x0010)
1695+
1696+
#= libuv flags =#
1697+
const UV_TTY_SUPPORTED = 0
1698+
const UV_TTY_UNSUPPORTED = 1
1699+
1700+
function _reset_console_mode(handle::Ptr{Cvoid})
1701+
# Query libuv to see whether it expects the console to support virtual terminal sequences
1702+
vterm_state = Ref{Cint}()
1703+
ccall(:uv_tty_get_vterm_state, Cint, (Ref{Cint},), vterm_state)
1704+
1705+
mode::UInt32 = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT
1706+
if vterm_state[] == UV_TTY_SUPPORTED
1707+
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
16991708
end
1700-
nothing
1709+
1710+
# Expected to fail (benignly) with ERROR_INVALID_HANDLE if the provided handle does not
1711+
# allow setting the console mode
1712+
ccall(:SetConsoleMode, stdcall, Int32, (Ptr{Cvoid}, UInt32), handle, mode)
1713+
1714+
return nothing
17011715
end
1716+
17021717
end
17031718

17041719
# returns the width of the written prompt
17051720
function write_prompt(terminal::Union{IO, AbstractTerminal}, s::Union{AbstractString,Function}, color::Bool)
1706-
@static Sys.iswindows() && _reset_console_mode()
17071721
promptstr = prompt_string(s)::String
17081722
write(terminal, promptstr)
17091723
return textwidth(promptstr)

0 commit comments

Comments
 (0)