edit_command_buffer: send indented commandline to editor

Indented multiline commandlines look ugly in an external editor.  Also,
fish doesn't properly handle the case when the editor runs fish_indent.
Fix is by indenting when exporting the commandline and un-indenting when
importing the commandline again.

Unindent only if the file is properly indented (meaning at least by the
amount fish would use).  Another complication is that we need to offset
cursor positions by the indentation.

This approach exposes "fish_indent --only-indent" and "--only-unindent"
though I don't imagine they are useful for others so I'm not sure if this
is the right place and whether we should even document it.

One alternative is to add "commandline --indented" to handle indentation
transparently.
So  "commandline --indented" would print a indented lines,
and "commandline --indented 'if true' '    echo'" would remove the unecessary
indentation before replacing the commandline.
That would probably simplify the logic for the cursor position offset.
This commit is contained in:
Johannes Altmanninger 2024-04-12 10:16:38 +02:00
parent 47a446ae18
commit 222673f339
2 changed files with 12 additions and 5 deletions

View file

@ -114,9 +114,10 @@ New or improved bindings
For example, ``commandline -f yank -f yank-pop`` inserts the last-but-one entry from the kill ring.
- When the cursor is on a command that resolves to an executable script, :kbd:`Alt-O` will now open that script in your editor (:issue:`10266`).
- During up-arrow history search, :kbd:`shift-delete` will delete the current search item and move to the next older item. Previously this was only supported in the history pager.
- Two improvements to the :kbd:`Alt-E` binding which edits the commandline in an external editor:
- Some improvements to the :kbd:`Alt-E` binding which edits the commandline in an external editor:
- The editor's cursor position is copied back to fish. This is currently supported for Vim and Kakoune.
- Cursor position synchronization is only supported for a set of known editors. This has been extended by also resolving aliases. For example use ``complete --wraps my-vim vim`` to synchronize cursors when `EDITOR=my-vim`.
- Multiline commands are indented before being sent to the editor, which matches the rendering in fish.
- ``backward-kill-path-component`` and friends now treat ``#`` as part of a path component (:issue:`10271`).
- Bindings like :kbd:`alt-l` that print output in between prompts now work correctly with multiline commandlines.
- `history-prefix-search-{backward,forward}` now maintain the cursor position instead of moving the cursor to the end of the command line (:issue:`10430`).

View file

@ -18,7 +18,8 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
set -l editor (__fish_anyeditor)
or return 1
commandline -b >$f
set -l indented_lines (commandline -b | fish_indent --only-indent)
string join -- \n $indented_lines >$f
set -l offset (commandline --cursor)
# compute cursor line/column
set -l lines (commandline)\n
@ -28,7 +29,8 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
set line (math $line + 1)
set -e lines[1]
end
set col (math $offset + 1)
set -l indent 1 + (string length -- $indented_lines[$line]) - (string length -- $lines[1])
set -l col (math $offset + 1 + $indent)
set -l editor_basename (string match -r '[^/]+$' -- $editor[1])
set -l wrapped_commands
@ -88,11 +90,14 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
$editor
set -l raw_lines (command cat $f)
set -l unindented_lines (string join -- \n $raw_lines | fish_indent --only-unindent)
# Here we're checking the exit status of the editor.
if test $status -eq 0 -a -s $f
# Set the command to the output of the edited command and move the cursor to the
# end of the edited command.
commandline -r -- (command cat $f)
commandline -r -- $unindented_lines
commandline -C 999999
else
echo
@ -103,7 +108,8 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
eval set -l pos "$(cat $cursor_from_editor)"
if set -q pos[1] && test $pos[1] = $f
set -l line $pos[2]
set -l column $pos[3]
set -l indent (math (string length -- $raw_lines[$line]) - (string length -- $unindented_lines[$line]))
set -l column (math $pos[3] - $indent)
commandline -C 0
for _line in (seq $line)[2..]
commandline -f down-line