Whenever a new history node is committed after some undo steps, instead
of creating a new branch in the undo graph, we first append the inverse
modifications starting from the end of the undo list up to the current
position before adding the new node.
For example let's assume that the undo history is A-B-C, that a single undo
has been done (bringing us to state B) and that a new change D is committed.
Instead of creating a new branch starting at B, we add the inverse of C
(noted ^C) at the end, and D afterwards. This results in the undo history
A-B-C-^C-D. Since C-^C collapses to a null change, this is equivalent to
A-B-D but without having lost the C branch of the history.
If a new change is committed while no undo has been done, the new history
node is simply appended to the list, as was the case previously.
This results in a simplification of the user interaction, as two bindings
are now sufficient to walk the entire undo history, as opposed to needing
extra bindings to switch branches whenever they occur.
The <a-u> and <a-U> bindings are now free.
It also simplifies the implementation, as the graph traversal and
branching code are not needed anymore. The parent and child of a node are
now respectively the previous and the next elements in the list, so there
is no need to store their ID as part of the node.
Only the committing of an undo group is slightly more complex, as inverse
history nodes need to be added depending on the current position in the
undo list.
The following article was the initial motivation for this change:
https://github.com/zaboople/klonk/blob/master/TheGURQ.md
The previous code was assuming it was fine to push_next without
growing, which used to be the case with the previous implementation
because we always have poped the current thread that we try to push.
However now that we use a ring-buffer, m_next_begin == m_next_end can
either mean full, or empty. We solve this by assuming it means empty
and never allowing the buffer to become full, which means we need
to grow after pushing to next if we get full.
Fixes#4859
Handle begin/end paste directly in paste csi, manage paste buffer
out of get_char, filter Key::Invalid earlier.
get_next_key returning Key::Invalid means there was some input but
it could not be represented as a Key. An empty optional means there
was no input at all.
Text pasted into Kakoune's normal mode is interpreted as command
sequence, which is probably never what the user wants. Text
pasted during insert mode will be inserted fine but may trigger
auto-indentation hooks which is likely not what users want.
Bracketed paste is pair of escape codes sent by terminals that allow
applications to distinguish between pasted text and typed text.
Let's use this feature to always insert pasted text verbatim, skipping
keymap lookup and the InsertChar hook. In future, we could add a
dedicated Paste hook.
We need to make a decision on whether to paste before or after the
selection. I chose "before" because that's what I'm used to.
TerminalUI::set_on_key has
EventManager::instance().force_signal(0);
I'm not sure if we want the same for TerminalUI::set_on_paste?
I assume it doesn't matter because they are always called in tandem.
Closes#2465
The macOS CI manges to trigger this race. When it happens the
"c" inserted by the last command is not seen by the test runner.
Let's fix this by adding yet another sleep.
Cirrus CI switched macOS machines to M1 where "brew install gcc@10"
fails due to architecture mismatch. I'm not sure if it's possible
to install gcc 10 natively for ARM. Let's work around this by
installing the x86_64 compatibility layer. Apparently we need
to install a x86_64 Homebrew to install x86_64 packages.
This fixes the CI failure where clang could not find <compare>.
Removing the include would not be enough, there are some other
failures.
Debian Stable ships Clang 11 so this seems accessible enough.
I dedicate any and all copyright interest in this software to the
public domain. I make this dedication for the benefit of the public at
large and to the detriment of my heirs and successors. I intend this
dedication to be an overt act of relinquishment in perpetuity of all
present and future rights to this software under copyright law.
I dedicate any and all copyright interest in this software to the
public domain. I make this dedication for the benefit of the public at
large and to the detriment of my heirs and successors. I intend this
dedication to be an overt act of relinquishment in perpetuity of all
present and future rights to this software under copyright law.
If, for example, the buffer path now is a directory, MappedFile will
throw on construction. Using a try block to explicitely allow errors
fixes the issue.
Instead of potentially decoding for each thread, always decode as
its only slightly slower than finding next codepoint (which will
be necessary anyway) and pass the codepoint to each thread.
The command line "hook -group xyz " should get scope completions but
it actually gets hook completions because "xyz" is wrongly interpreted
as positional argument.
Fix this by using the parameters parser to compute positional
arguments.
Fixes#4840
Some plugins (*cough* kak-lsp) and help texts tend to have immensely long content
in a single line. This generates info boxes that span the entire terminal width.
This is made especially worse on widescreen monitors or at small text size.
This grants user control over how wide these boxes are.
I deliberately avoid pushing this change to `kak-lsp` because it's not the only
plugin that this could help--see the `hook` help text for an example of this
problem in vanilla Kakoune. I would also suggest that since this is a rendering
concern, it be handled by the terminal rendering logic.
We only grow when the ring buffer is full, which allows for a nice
simplification of the code.
Tell grow_ifn if we pushed in current or next so that we can
distinguish between filled by next or filled by current when
m_current == m_next_begin
Instead of two stacks growing from the two ends of a buffer, use
a ring buffer growing from the same mid spot.
This avoids the costly memory copy every step when we set next
threads as the current ones.
Erasing fully trimmed display atoms one by one means we have to
shift all the remaining ones every time. This is wasteful and we
can just erase all the fully trimmed atom in one go.
Fixes#4797