Another consequence of a583fe723 ("commandline -f foo" to skip queue
and execute immediately, 2024-04-08) is that "commandline -f repaint"
will paint the prompt with the current value of $status which might be
set from a shell command in a the currently executing binding, instead of
waiting for the top-level status. This is wrong, at least historically. It
surfaces in bindings like alt-w which always paint a status value of [1]
when on single-lines commandlines.
Another regression is that a redundant repaint in a signal handler outputs
an extra prompt.
Fix both by making repaint commands go over the input queue again. This way,
they are always run with a good commandline state. There is no need to
repaint immediately because I don't think anyone has a data dependency on it
(we currently don't expose the prompt string), it's only for rendering.
We sometimes leak ^[[I and ^[[O focus reporting events when run from VSCode's
"Run python file" button in the top right corner. To reproduce I installed
the ms-python extension set the VSCode default shell to fish and repeatedly
ran a script that does "time.sleep(1)". I believe VSCode synthesizes keys
and triggers a race condition.
We can probably fix this but I'm not sure when I'll get to it (given how
relatively unimportant this feature is).
So let's go back to the old behavior of only enabling focus reporting in tmux.
I believe that tmux is affected by the same VSCode issue (also on 3.7.1 I
think) but I haven't been able to get tmux to emit focus reporting sequences
yet. Still, keep it to not regress cursor shape (#4788). So far this is
the only motivation for focus reporting and I believe it is only relevant
for terminals that can split windows (though there are a bunch that do).
Closes#10448
While it does need to store the string, we also need to use the string after
storing it, so we aren't getting any advantage from passing by value. Just pass
by reference to simplify the call sites.
This is another problem that has been bothering me for years: as mentioned
in 1dd901e52 (Maintain cursor in history prefix search, 2024-04-12), up-arrow
search highlights search matches but the contrast is really bad, especially in
command position, because the search matches --background=brblack is combined
with whatever foreground syntax highlighting the command has. The history
pager had a similar problem (for the selected history item) but circumented
it by disabling syntax highlighting altogether for the selected item.
fish_color_search_match's foreground component is ignored.
Let's use it instead of syntax highlighting.
This fixes the contrast on some default colorschemes but the bryellow
foreground looks weirdly like an error/warning on some terminals. Change it
to white. This needs a hack because we don't have a canonical way to tell
if a uvar has been set by the user. Fortunately the foreground component
hasn't been used at all so far, so we're not so much changing it as much as
initializing it.
On Konsole with
function my-bindings
bind --preset --erase escape
bind escape,i 'echo escape i'
end
set fish_key_bindings my-bindings
the "escape,i" binding doesn't trigger. This is because of our special
handling of the escape key prefix. Other multi-key bindings like "bind j,k"
wait indefinitely for the second character. But not "escape,i"; that one
has historically had a low timeout (fish_escape_delay_ms). The motivation
is probably that we have a "escape" binding as well that shouldn't wait
indefinitely.
We can distinguish between the case of raw escape sequence binding like "\e123"
and a binding that talks about the actual escape key like "escape,i". For the
latter we don't need the special treatment of having a low timeout, so make it
fall back to "fish_sequence_key_delay_ms" which waits indefinitely by default.
When we read bytes like \xfc that don't produce a Unicode code point,
we encode them in a Unicode private use area.
This encoding should be transparent to the user.
We accidentally add it to uvar files as \uf6fc in this case. When reading
it back, read_unquoted_escape() will fail at the "fish_reserved_codepoint(c)"
check. This check is to avoid external input being misinterpreted
as one of our in-band signalling characters like ANY_CHAR (for *).
For encoded raw bytes, this check probably doesn't really matter in terms of
security because the only thing we do with these bytes is convert them back
to raw. So we could allow unescaping them at this point, thus supporting
old uvar files.
However that seems like the wrong direction. PUA encoding should never leak.
So let's instead make sure to serialize it as \xfc instead of \f6fc going
forward.
Fixes#10313
Popular operating systems support shift-delete to delete the selected item
in an autocompletion widgets. We already support this in the history pager.
Let's do the same for up-arrow history search.
Related discussion: https://github.com/fish-shell/fish-shell/pull/9515
On
a;
we don't expand the abbreviation because the cursor is right of semicolon,
not on the command token. Fix this by making sure that we call expand-abbr
with the cursor on the semicolon which is the end of the command token.
(Now that our bind command execution order is less surprising, this is doable.)
This means that we need to fix the cursor after successfully expanding
an abbreviation. Do this by setting the position explicitly even when no
--set-position is in effect.
An earlier version of this patch used
bind space self-insert backward-char expand-abbr or forward-char
The problem with that (as a failing test shows) was that given "abbr m
myabbr", after typing "m space ctrl-z", the cursor would be after the "m",
not after the space. The second space removes the space, not changing the
cursor position, which is weird. I initially tried to fix this by adding
a hack to the undo group logic, to always restore the cursor position from
when begin-undo-group was used.
bind space self-insert begin-undo-group backward-char expand-abbr end-undo-group or forward-char
However this made test_torn_escapes.py fail for mysterious reasons.
I believe this is because that test registers and triggers a SIGUSR1 handler;
since the signal handler will rearrange char events, that probably messes
with the undo group guards.
I resorted to adding a tailor-made readline cmd. We could probably remove
it and give the new behavior to expand-abbr, not sure.
Fixes#9730
File names that have lots of spaces look quite ugly when inserted as
completions because every space will have a backslash.
Add an initial heuristic to decide when to use quotes instead of
backslash escapes.
Quote when
1. it's not an autosuggestion
2. we replace the token or insert a fresh one
3. we will add a space at the end
In future we could relax some of these requirements.
Requirement 2 means we don't quote when appending to an existing token.
Need to find a natural behavior here.
Re 3, if the completion adds no space, users will probably want to add more
characters, which looks a bit weird if the token has a trailing quote.
We could relax this requirement for directory completions, so «ls so»
completes to «ls 'some dir with spaces'/».
Closes#5433
On Konsole, given
bind escape,i 'echo escape i'
bind alt-i 'echo alt-i'
pressing alt-i triggers the wrong binding. This is because we treat "escape
followed by i" as "alt-i". This is to support raw sequences like "\ei"
which are probably meant as "alt-i" -- we match such inputs to both mappings.
This double matching is not necessary for new-style bindings which
unambiguously describe the key presses, so let's activate this sequence
matching only for bindings specified as raw sequences.
Conversely, we currently fail to match an XTerm raw binding for ctrl-enter:
echo 'XTerm.vt100.formatOtherKeys: 0' | xrdb
xterm -e fish
bind \e\[27\;5\;13~ execute
because we decode this to a single char; we match the leading CSI but not
the entire sequence. So this is a raw binding where we accidentally
match full, modified keys. Fix that too (two birds with one stone).
I think commit 8386088b3 (Update commandline state changes eagerly as well,
2024-04-11) broke the alt-s binding.
This is because we update the commandline state snapshot (which is consumed
by builtin commandline and others) only at key points. This seems like a
dubious optimization. With the new streamlined bind execution semantics,
this doesn't really work anymore; any shell command can run any number of
commands like "commandline -i foo" which should synchronize.
Do the simple thing of calculating the snapshot whenever needed.
The search term highlighting looks looks really bad on the default theme
because the command is highlighted as dark blue and the search term adds
a dark background. If this new feature motivates us to finally fix this,
that would be great.
Closes#10430
The new reader_execute_readline_cmd() runs apply_commandline_state_changes()
to make sure that given
bind x "commandline --insert foo; commandline -f backward-char"
the backward-char command knows about the insertion of "foo". This
causes problems when running "sleep 1&" and typing some characters -
the commandline will be cleared when the job finishes. This is because
apply_commandline_state_changes() works with stale information in this case.
Let's call it as soon as we know it's needed. This is less messy and fits
better with the new bind function semantics ("execute things in the order
they are written").
This makes them more convenient to use interactively, similar to the existing
\c and \a versions. The resulting bind output keeps using the canonical
ctrl/alt version.
Not sure about s- because that's somewhat ambiguous, it could be "super".
See the parent commit for some context. Turns out that 8bf8b10f6 (Extended &
human-friendly keys, 2024-03-30) broke this for terminals that speak CSI u.
This is pretty complex, probably not worth it.
When a terminal sends \x1ba, that could be either escape,a or alt-a.
Historically we've handled this with an escape delay that defaults to 30
milliseconds. If we read nothing for that time, it's escape. Otherwise it's
an alt modifier (or an escape sequence).
As a side effect of 8bf8b10f6 (Extended & human-friendly keys, 2024-03-30) we
added a new way of disambiguating escape: whenever we read the escape byte,
we immediately try another (nonblocking) read. If it succeeds, we treat it
as modifier, else it's escape. Before that commit, we didn't have a concept
of modifiers.
The new way works fine for disambiguating escape,a from alt-a (as pressed
by the user) because only for alt-a the data is sent in the same packet.
So we no longer need the escape delay to disambiguate the alt from the
escape key. Let's simplify things by not using it by default.
The escape delay as set by fish_escape_delay_ms also serves another purpose;
it allows to disambiguate "escape,a" from "escape (pause) a". For that use
case we want to keep it.
As mentioned in 8a7c3ceec (Don't abandon line after writing control sequences,
2024-04-06) we need to freshed stdout timestamps after writing to stdout
but before we might redraw, in particular when writing control sequences.
Commit a583fe723 ("commandline -f foo" to skip queue and execute immediately,
2024-04-08) made "commandline -f repaint" redraw immediately, while still
executing the bound shell command; at that time we have written "disabling"
sequences but not refreshed timestamps yet, so do that.
This is probably not needed for commands outside the repaint family.
Needless to say that this is messy, maybe we can simplify things in future.
Ref https://github.com/fish-shell/fish-shell/issues/10409#issuecomment-2044863817
If a key's codepoint is in the PUA1 range, it could
be either from our own named keys (like key::Space)
or from a CSI u key that we haven't assigned a name yet
https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
(The latter can still be bound using the \u1234 or the equivalent \e[4660u
raw CSI u sequence.)
It doesn't make sense to insert a PUA character into the commandline when
the user presses PrintScreen; ignore them silently.
This partially reverts b77d1d0e2 (Stop crashing on invalid Unicode input,
2024-02-27). That commit did:
1. convert input byte sequences that map to a PUA codepoint into several
characters, using our on-char-per-byte PUA encoding.
2. do the same for inputs that are codepoints outside the valid Unicode range.
3. render them as replacement character (one per input byte)
In future, we should probably remove these features altogether, and simply
ignore invalid Unicode code points.
Commit c3cd68dda (Process shell commands from bindings like regular char
events, 2024-03-02) mentions a "weird ordering difference".
The issue is that "commandline -f foo" goes through the input
queue while other commands are executed directly.
For example
bind ctrl-g "commandline -f end-of-line; commandline -i x"
is executed in the wrong order. Fix that.
This doesn't yet work for "commandline -f exit" but that can be fixed easily.
It's hard to imagine anyone would rely on the existing behavior. "commandline
-f" in bindings is mostly used for repainting the commandline.
If a binding was input starting with "\e", it's usually a raw control sequence.
Today we display the canonical version like:
bind --preset alt-\[,1,\;,5,C foo
even if the input is
bind --preset \e\[1\;5C foo
Make it look like the input again. This looks more familiar and less
surprising (especially since we canonicalize CSI to "alt-[").
Except that we use the \x01 representation instead of \ca because the
"control" part can be confusing. We're inside an escape sequence so it seems
highly unlikely that an ASCII control character actually comes from the user
holding the control key.
The downside is that this hides the canonical version; it might be surprising
that a raw-escape-sequence binding can be erased using the new syntax and
vice versa.
We don't yet support all keys from
https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
Instead of displaying a private-use character, show the character code;
this can be used to map the key even if we don't know a name for it.
bind \uE011 'echo print screen'
bind ctrl-\uE011 'echo do control + print screen'
Note that it's also possible to mape the raw CSI u sequence, like
bind \e\[57361u 'echo print screen'
but we should not encourage that syntax because it does not allow adding
the modifiers like ctrl.
Of course leaking the PUA character code is not ideal.
As implied by the changelog.
Unfortunately it's not obvious how to access the RefCell value in spite
of a potential (albeit unlikely) present mutable borrow. We need to use a
different type to make it work in such cases, hopefully doing that in future.
In future we could even use panic=abort and use this style of cleanup for
panics (instead of RAII).
For numpad 1 with nulock, Alacritty sends
escape,[,5,7,4,0,0,u
which is codepoint \x31, key "1". We have a terminfo mapping for "sright"
which translates to
escape,[,1,;,2,C
The first two characters, escape and [ match. Then we accidentally match the
"1" from the mapping against the entire sequence, because that sequence is
canonicalized to codepoint "1" . The most blatant problem is that we discard
the rest of the sequence. Fix that.
This allows us to re-enable raw CSI u mappings like "bind \e[1u ..."
which is what kitty uses for shell integration.
This allows terminals like foot and kitty to
* scroll to the previous/next prompt with ctrl-shift-{z,x}
* pipe the last command's output to a pager with ctrl-shift-g
Kitty has existing fish shell integration
shell-integration/fish/vendor_conf.d/kitty-shell-integration.fish which we
can simplify now. They keep a state variable to decide which of prompt start,
command start or command end to output. I think with our implementation
this is no longer necessary, at least I couldn't reproduce any difference.
We also don't need to hook into fish_cancel or fish_posterror like they do;
only in the one place where we actually draw the prompt.
As mentioned in the above shell integration script, kitty disables reflow
when it sees an OSC 133 marker, so we need to do it ourselves,
otherwise the prompt will go blank after a terminal resize.
Closes#10352
Commit 8164855b7 (Disable terminal protocols throughout evaluation, 2024-04-02)
changed where we output control sequences (to enable bracketed paste and CSI).
Likewise, f285e85b0 (Enable focus reporting only just before reading from
stdin, 2024-04-06) added control sequence output just before we read().
This output causes problems because it invalidates our stdout/stderr
timestamps, which causes us to think that a rogue background process wrote
to the terminal; we react by abandoning the current line and redrawing the
prompt below. Our fix was to refresh the TTY timestamps after we run a bind
command that might add stdout (#3481).
Since commit c3cd68dda (Process shell commands from bindings like regular
char events, 2024-03-02), this timestamp refresh logic is in the wrong place;
shell commands are run later now; we could move it but wait -
... we also need to make sure to refresh timestamps after outputting control
sequences. Since bracketed paste is enabled after CSI u, we can skip the
latter. Additionally, since we currently output control sequences before
every single top-level interactive command, we no longer need to separately
refresh timestamps in between commands.
Fixes#10409
Some terminals send the focus-in sequences ("^[I") whenever focus reporting is
enabled. We enable focus reporting whenever we are finished running a command.
If we run two commands without reading in between, the focus sequences
will show up on the terminal.
Fix this by enabling focus-reporting as late as possible.
This fixes the problem with `^[I` showing up when running "cat" in
gnome-terminal https://github.com/fish-shell/fish-shell/issues/10411.
This begs the question if we should do the same for CSI u and bracketed paste.
It's difficult to answer that; let's hope we find motivating test cases.
If we enable CSI u too late, we might misinterpret key presses, so for now
we still enable those as early as possible.
Also, since we now read immediately after enabling focus events, we can get
rid of the hack where we defer enabling them until after the first prompt.
When I start a fresh terminal, the ^[I no longer shows up.
It's not clear whether builtin read should be able to do everything
that the normal prompt does but I guess we haven't found a problem yet.
Given that read could be used to read a single character at a type,
it's a bit odd to toggle terminal protocols all the time.
But that's not the typical case (at least not for when stdin is a TTY),
and it seems fine.
Teste with
bind ctrl-4 'echo yay'
Regressed in 8164855b7 (Disable terminal protocols throughout evaluation,
2024-04-02).
Apparently VTE terminals send the "focus in" event whenever we re-enable
focus reporting. That's probably a sensible thing to do.
Anyway, our problem is simply that we accidentally end history search on these
focus events which are implemented as anonymous (unmappable) readline cmds.
Perhaps there should be a separate cmd category.
Focus events show up as key::Invalid which is a weird private use code point;
probably we can get rid of this key..
Fixes#10411
See the changelog additions for user-visible changes.
Since we enable/disable terminal protocols whenever we pass terminal ownership,
tests can no longer run in parallel on the same terminal.
For the same reason, readline shortcuts in the gdb REPL will not work anymore.
As a remedy, use gdbserver, or lobby for CSI u support in libreadline.
Add sleep to some tests, otherwise they fall (both in CI and locally).
There are two weird failures on FreeBSD remaining, disable them for now
https://github.com/fish-shell/fish-shell/pull/10359/checks?check_run_id=23330096362
Design and implementation borrows heavily from Kakoune.
In future, we should try to implement more of the kitty progressive
enhancements.
Closes#10359
To do so add an ad-hoc "commandline --search-field" to operate on pager
search field.
This is primarily motivated because a following commit reuses the
fish_clipboard_paste logic for bracketed paste. This avoids a regression.
Terminal titles are set with an OSC 0 sequence. I don't think we want to
support terminals that react badly to unknown OSC (or CSI) sequences.
So let's remove our feature detection.
This will fix future false negatives along the lines of
https://github.com/fish-shell/fish-shell/pull/10037
This binding is akin to ForwardSingleChar but it is "passive" in that is not
intended to affect the meta state of the shell: autocompletions are not accepted
if the cursor is at the end of input and it does not have any effect in the
completions pager.
Currently, we expand command-abbrs (those with `--position command`) after `if`, but not after `command` or `builtin` or `time`:
```fish
abbr --add gc "git checkout"
```
will expand as `if gc` but not as `command gc`.
This was explicitly tested, but I have no idea why it shouldn't be?
The incorrect order of operations was being used since && binds tighter than ||
in rust (as with most sane languages).
Under Linux, EAGAIN == EWOULDBLOCK so this would always succeed in the case of a
non-blocking fd without making the call to make_fd_nonblocking().
Comparing to the 3.7.0 C++ code, it looks like this was an oversight introduced
in the migration to rust.
In particular, this allows restoring the terminal on crashes, which is
feasible now that we have the panic handler. Since std::process::exit() skips
destructors, we need to reshuffle some code. The "exit_without_destructors"
semantics (which std::process::exit() als has) was mostly necessary for C++
since Rust leaks global variables by default.
When fish crashes due to a panic, the terminal window is closed. Some
terminals keep the window around when the crash is due to a fatal signal,
but today we don't exit via fatal signal on panic.
There is the option to set «panic = "abort"» in Cargo.toml, which
would give us coredumps but also worse stacktraces on stderr.
More importantly it means that we don't unwind, so destructors are skipped
I don't think we want that because we should use destructors to
restore the terminal state.
On crash in interactive fish, read one more line before exiting, so the
stack trace is always visible.
In future, we should move this "read one line before exiting" logic to where
we call "panic!", so I can attach a debugger and see the stacktrace.
Today fish_cursor_selection_mode controls whether selection mode includes
the cursor. Since it's by default only used for Vi mode, perhaps use it to
also decide whether it should be allowed to select one-past the last character.
Not allowing to select to select one-past the last character is much nicer
in Vi mode. Unfortunately Vi mode sometimes needs to temporarily select
past end (using forward-single-char and such), so reset fish_cursor_selection_mode
for the duration of the binding.
Also fix other things like cursor placement after yank/yank-pop.
Closes#10286Closes#3299
A long standing issue is that bindings cannot mix special input functions
and shell commands. For example,
bind x end-of-line "commandline -i x"
silently does nothing. Instead we have to do lift everything to shell commands
bind x "commandline -f end-of-line; commandline -i x"
for no good reason.
Additionally, there is a weird ordering difference between special input
functions and shell commands. Special input functions are pushed into the
the queue whereas shell commands are executed immediately.
This weird ordering means that the above "bind x" still doesn't work as
expected, because "commandline -i" is processed before "end-of-line".
Finally, this is all implemented via weird hack to allow recursive use of
a mutable reference to the reader state.
Fix all of this by processing shell commands the same as both special input
functions and regular chars. Hopefully this doesn't break anything.
Fixes#8186Fixes#10360Closes#9398
Multiline search strings are weirdly broken (inserting control characters
in the command line) and probably not very useful anyway.
On the other hand I often want to compose a multi-line command
from single-line commands I ran previously.
Let's support this case by limiting the initial search string to the current
line; and replace only that line.
Alternatively this could operate on jobs (that is, replace a surrounding
"foo | bar") instead of using line boundaries.
This is resistant to misuse by including O_DIRECTORY in the open flags and it is
a separate function from {w,}open_cloexec() in preparation for making that one
return a `File` instead of an `OwnedFd`.
`intersects()` is "any of" while `contains()` is "all of" and while it makes no
difference when testing a single bit, I believe `contains()` is less brittle
for future maintenance and updates as its meaning is clearer.
</pedantic>
More work in prep for having wopen_cloexec() return `File` directly.
This eliminates checking for an invalid fd and makes both ownership and
mutability clear (some more operations that involve changes to the underlying
state of the fd now require `&mut File` instead of just a `RawFd`).
Code that clearly does not use non-blocking IO is ported to use
`Write::write_all()` directly instead of our rusty port of the `write_loop()`
function (which handles EAGAIN/EWOULDBLOCK in addition to EINTR, while
`write_all()` only handles the latter).
This is a step towards converting `wopen_cloexec()` to return `File` instead of
`OwnedFd`/`AutocloseFd`.¹
In addition to letting us use native standard library functions instead of
unsafe libc calls, we gain additional semantic safety because `File` operations
that manipulate the state of the fd (e.g. `File::seek()`) require a `&mut`
reference to the `File`, whereas using `RawFd` or `OwnedFd` everywhere leaves us
in a position where it's not clear whether or not other references to the same
fd will manipulate its underlying state.
¹ We actually wouldn't even need `wopen_cloexec()` at all (just a widechar
wrapper) as Rust's native `File::open()`/`File::create()` functionality uses
`FD_CLOEXEC` internally.
* builtin/test: Split Token enum into 2-level hierarchy
* builtin/test: Rearrange the Token enum hierarchy
* builtin/test: Separate Token into Unary and Binary
* builtin/test: import IsOkAnd polyfill
* builtin/test: Rename enum variants one more time
I was able to trigger this by flipping around the history pager.
Since the only applicable caller here already stops if it gets None,
just don't assert.
I don't think the existing logic is correct, as the comment says, our internal
state is only matched if we *actually* wrote out the file. But if we ran into an
error, it doesn't match, does it?
The incompatible_msrv one is a false positive because we have polyfills for
is_some_and() and is_ok_or() which are Rust 1.74. I'm not yet sure how to
communicate that to Clippy.
Call fish_should_add_to_history to see if a command should be saved
If it returns 0, it will be saved, if it returns anything else, it
will be ephemeral.
It gets the right-trimmed text as the argument.
If it doesn't exist, we do the historical behavior of checking for a
leading space.
That means you can now turn that off by defining a
`fish_should_add_to_history` that just doesn't check it.
documentation based on #9298
The existing logic did not work because:
- Path::new("/foo/bar").ends_with("/bar") does not return true.
- PathBuf::shrink_to() only (potentially) reallocates the backing
storage, and won't have an effect on the stored value.
As mentioned in the comment the historical behavior is because pressing unknown
control characters like Ctrl+4 inserts confusing characters, so let's back
out that part of b77d1d0e2 (Stop crashing on invalid Unicode input, 2024-02-27).
We still have the code for rendering control characters, for pasted text,
or text recalled from history. It is unclear whether we should strip those.
Some terminals already strip control characters from pasted text -- but not
all of them: see https://codeberg.org/dnkl/foot/pulls/312 for example which
has a follow up called "Don't strip HT when pasting in non-bracketed mode".
Don't force the internal use of `RefCell<T>`, let the caller place that into
`MainThread<>` manually. This lets us remove the reference to `MainThread<>`
from the definition of `Screen` again and reduces the number of
`assert_is_main_thread()` calls.
Fairly straightforward, with the only unfortunate part of this being that
`Screen` isn't as pure and now encodes the facte that we use it with
main-thread-only stdout `Outputter`.
The regex struct is pretty large at 560 bytes, with the entire
Abbreviation being 664 bytes.
If it's an "Option<Regex>", any abbr gets to pay the price. Boxing it
means abbrs without a regex are over 500 bytes smaller.
IfStatement is 680 bytes, much larger than the other
variants (SwitchStatement is next at 232). An enum is as large as its
largest variant, so this saves a bunch, especially since
DecoratedStatement is much more likely than IfStatement.
This will speed up the no-execute benchmark by 1.07x.
Unlike C++, Rust requires "char" to be a valid Unicode code point. As a
workaround, we take the raw (probably UTF-8-encoded) input and convert each
input byte to a char representation from the private use area (see commit
3b15e995e (str2wcs: encode invalid Unicode characters in the private use
area, 2023-04-01)). We convert back whenever we output the string, which
is correct as long as the encoding didn't change since the data was input.
We also need to convert keyboard input; do that.
Quick testing shows that our reader drops PUA characters. Since this patch
converts both invalid Unicode input as well as PUA input into a safe PUA
representation, there's no longer a reason to not add PUA characters to
the commandline, so let's do that to restore traditional behavior.
Render them as � (REPLACEMENT CHARACTER); unfortunately we show one per
input byte instead of one per code point. To fix this we probably need our
own char type.
While at it, remove some special cases that try to prevent insertion of
control characters. I don't think they are necessary. Could be wrong..
This makes it so code like
```fish
echo foo
echo bar
```
is collapsed into
```fish
echo foo
echo bar
```
One empty line is allowed, more is overkill.
We could also allow more than one for e.g. function endings.