When performing multi-selection editing on a large scale, I have always wondered if it is possible to display all selections in a single window, in a diff-like format:
In this way, I can view all (or at least as many as possible) selections at the same time, so that I can be more confident that my consequent editing won’t fail at some particular selections.
So I wonder:
does this feature seem desirable?
are there any plugins available that performs a similar task? If not, is it possible to implement this feature as a plugin (I once tried, but find the “writing edited contents back to original buffer” part difficult)
That does seem pretty cool - allowing the summary buffer to be edited would be neat, but even if it just showed each selection in-context, live-updating as the selections changed in the main buffer, that would give you something to look at to quickly verify your edits.
In the mean time, I use the ( and ) keys to quickly cycle through all my selections and check that they’re doing the right thing.
That’s a cool idea. Pure kakscript using recursion (since maybe this should become a live-update hook… and because this is what I’m currently toying with):
def -params .. info-sel %{
_sels2desc
info -title Selections %opt{_str}
} -override
def -params .. preview-sel %{
eval -save-regs c %{
_sels2desc
reg c %opt{_str}
edit -scratch *selections*
exec %{ggGed"cP} #"unbreak hl
}
} -override
### kakscript library
decl -hidden str-list _slist
decl -hidden str _str
def -params 0 nargs-0-0 %{nop --} -override # fail if nargs != 0
def -params 1.. shift-1-1 %{ # shift arg{1}; result in _slist
set window _slist %arg{@}
set -remove window _slist %arg{1}
} -override
def -params 2.. arg2reg-2 %{ reg %arg{1} %arg{2} } -override
###
decl -hidden str-list _sels2desc_dot_slist
decl -hidden str-list _sels2desc_z_slist
def -params .. _sels2desc-1 %{
try %{ nargs-0-0 %opt{_sels2desc_z_slist} } catch %{
arg2reg-2 c %opt{_sels2desc_dot_slist}
arg2reg-2 d %opt{_sels2desc_z_slist}
set -add window _str "%reg{d}
%reg{c}
"
shift-1-1 %opt{_sels2desc_dot_slist}
set window _sels2desc_dot_slist %opt{_slist}
shift-1-1 %opt{_sels2desc_z_slist}
set window _sels2desc_z_slist %opt{_slist}
_sels2desc-1 # recurse after shifting both lists
}
} -override -hidden
def -params .. _sels2desc %{
set window _sels2desc_dot_slist
eval -itersel %{ set -add window _sels2desc_dot_slist %reg{.} }
shift-1-1 %reg{z}
set window _sels2desc_z_slist %opt{_slist}
set window _str ''; eval -draft -verbatim -save-regs 'cd' _sels2desc-1
} -hidden
Now, what exactly would mean to edit *selections* and update the original buffer?
That’s the tricky part (in terms of implementing this feature as a plugin). Idealy, *selections* is divided into separate small regions each corresponding to some part of the original buffer, and writing *selection* simply replace the corresponding parts of the original buffer with the new content of each small region.
But I have no idea how to separate a buffer into individual small parts in kak. Perhaps one can use kak’s range-spec type can be used, but I am not sure how robust it is and the exact mechanism how these ranges are updated when, for example, part of them are deleted.
Yes… I think there are two possible results of editing *selections*:
edit the selected text → change the text of the original buffer
change the selection columns → extend / shrink selection in original, but don’t touch text.
The first seems… maybe harder (and this is the one you seem to want).
For the second, however (which @Screwtapello seems to have in mind), it’s enough to prefix text lines (but not rangespec lines) with ##, then when update-original is invoked, strip the comments and generate a command that loads the ranges into a register, then execute z.
I think I’ll try to do it in pure kakscript — the code above needs only a few changes (it works as it stands, BTW).
But even for the second case, there still remain some difficulties. For example, what if the user pressed %?
Perhaps the following logic applies: calculate for each selection a surrounding context, and merge those contexts that overlaps (for example two selections on the same line will be displayed in the same small region).
As for live updating, perhaps *selection* can sever as a alternative view of the original buffer (like two windows displaying the same buffer), and the user’s operations are all forwarded to the original buffer. Not diving into the details yet, though.
At this point, it only displays selections in an infobox or a buffer. It works with toolsclient (sel-editor-live-enable / -disable)
It’s pure kakscript, which is what I’m currently intrested in — I want to see how far one can jailbreak from kakscript’s straitjacket. I’ve managed to shift arguments, work with lists, recurse (and stop), implement foreach, and I have some other ideas.
I have also made an attempt to this. I am using shell expansion heavily, though.
There are some differences in behavior. My attempt displays not only the selection, but a (configurable) small region around each selection. screenshot:
Currently it has a poor performance (due to frequent whole buffer operation), and update of the live buffer is not triggered in user modes or prompt modes. I’ll just paste it here as a snippet for now, too see if anyone can get some inspiration from this.
declare-option -docstring %{
Number of rows to display before each selection
in viewsel buffer
} int viewsel_n_rows_before 1
declare-option -docstring %{
Number of rows to display after each selection
in viewsel buffer
} int viewsel_n_rows_after 1
declare-option -hidden str viewsel_client ""
define-command viewsel -docstring %{
view all selections in a temporary buffer
} %{
evaluate-commands -save-regs 'a' %sh{
bufname=$kak_bufname
buf_lines=$kak_buf_line_count
nr_before=$kak_opt_viewsel_n_rows_before
nr_after=$kak_opt_viewsel_n_rows_after
viewsel_buf="*viewsel@$bufname*"
echo "evaluate-commands -try-client $kak_opt_viewsel_client %{"
echo "edit -scratch $viewsel_buf"
echo "}"
echo "evaluate-commands -buffer $viewsel_buf %{"
echo "set-option buffer filetype $kak_opt_filetype"
echo "}"
# copy buffer content to viewsel buffer
echo -n "execute-keys -buffer $bufname %{"
echo -n "%\"ay"
echo "}"
echo "execute-keys -buffer $viewsel_buf %{"
echo -n "%d\"aP"
# remove trailing newline
echo -n "gexd"
echo "}"
# restore selections for the viewsel buffer
echo "evaluate-commands -try-client $kak_opt_viewsel_client %{"
echo "select $kak_selections_desc"
echo "}"
# add annotations and remove unview contents
# sort selections from begin to end
sels=$(for entry in $kak_selections_desc; do
echo $entry
done | sort -n)
cur_row=0
# delete everything that is too far away from any selection
# invariant:
# - either $cur_row is 0,
# or the last row of the last region (inclusive)
echo -n "execute-keys -draft -buffer $viewsel_buf %{"
echo -n "gg"
for sel in $sels; do
beg=${sel%%,*}
end=${sel##*,}
row_s=${beg%%\.*}
row_e=${end%%\.*}
view_s=$(( row_s - nr_before ))
view_e=$(( row_e + nr_after ))
if [ "$view_e" -gt "$buf_lines" ]; then
view_e=$buf_lines
fi
if [ "$view_s" -le "$((cur_row + 1))" ]; then
# Two regions around two selection overlap,
# hence merge the two regions
if [ "$cur_row" -eq 0 ]; then
echo -n "O== #1 =============<esc>"
fi
if [ "$view_e" -gt "$cur_row" ]; then
echo -n "$((view_e - cur_row))j"
fi
else
# A new region should be created.
# Delete everything until the start of new region
if [ "$cur_row" -ne 0 ]; then
echo -n "j"
fi
if [ "$view_s" -gt "$((cur_row + 2))" ]; then
echo -n "$((view_s - cur_row - 2))J"
fi
echo -n "<a-x>d"
# add indicator
echo -n "O== #$view_s =============<esc>"
echo -n "$((view_e - view_s + 1))j"
fi
cur_row=$view_e
done
echo -n "gllGed"
echo "}"
}
}
define-command viewsel-live -docstring %{
view all selections live in a temporary buffer
} %{
evaluate-commands %sh{
echo "set-option buffer viewsel_client $kak_client"
echo "hook global ClientClose $kak_client %{"
echo "evaluate-commands -buffer $kak_bufname %{"
echo "remove-hooks buffer viewsel-live"
echo "}}"
for hook in NormalKey InsertKey; do
echo "hook -group viewsel-live buffer $hook .* viewsel"
done
echo "viewsel"
}
}