Commit 9fdea82542 ("Fix various issues with
`SpawnMultiCursor{Up,Down}`") changed SpawnMultiCursorUp/Down actions to
honor softwrap, i.e. spawn cursor in the next visual line within a
wrapped line, which may not be the next logical line in a buffer. That
was done for "consistency" with cursor movement actions CursorUp/Down
etc. But it seems there are no actual use cases for that, whereas at
least some users prefer spawning multicursor in the next logical line
regardless of the softwrap setting. So restore the old behavior.
Fixes#3499
Restore the original meaning of LastVisualX before commit 6d13710d93
("Implement moving cursor up/down within a wrapped line"): last visual x
location of the cursor in a logical line in the buffer, not in a visual
line on the screen (in other words, taking tabs and wide characters into
account, but not taking softwrap into account). And add a separate
LastWrappedVisualX field, similar to LastVisualX but taking softwrap
into account as well.
This allows tracking last x position at the same time for both cases
when we care about softwrap and when we don't care about it. This can be
useful, for example, for implementing cursor up/down movement actions
that always move by logical lines, not by visual lines, even if softwrap
is enabled (in addition to our default CursorUp and CursorDown actions
that move by visual lines).
Also this fixes a minor bug: in InsertTab(), when `tabstospaces` is
enabled and we insert a tab, the amount of inserted spaces depends on
the visual line wrapping (i.e. on the window width), which is probably
not a good idea.
Since we already have the StoreVisualX() helper, use it all over the
place instead of setting LastVisualX directly.
This will allow us to add more logic to StoreVisualX() add let this
extra logic apply everywhere automatically.
When adding a new `BufPane` it is always being inserted last into `MainTab().Panes`.
This leads to a confusion when using the actions `PreviousSplit`, `NextSplit` as the previous/next split may not be the expected one.
How to reproduce:
- Launch micro and insert char "1"
- Open a new vsplit via the command `vsplit` and insert "2"
- Switch back to the left split (1) by using `PreviousSplit`
- Again open a new vsplit via command: `vsplit` and type char "3"
- Now switch between the 3 splits using `PreviousSplit`, `NextSplit`
Switching from most left split to the most right, the expected order would be 1, 3, 2 but actually is 1, 2, 3.
When we temporarily disable the screen (e.g. during RunInteractiveShell)
and then enable it again, we reinitialize tcell.Screen from scratch, so
we need to register all previously registered raw escape sequences once
again. Otherwise raw escape bindings stop working, since the list of
raw escape sequences of this newly create tcell.Screen is empty.
Fixes#3392
It doesn't need to loop over the DefaultCommonSettings() again,
since they're already included in the default settings and set via
SetGlobalOptionNative().
Add `matchbraceleft` option to allow disabling the default behavior
matching not just the brace under cursor but also the brace to the left
of it (which is arguably convenient, but also ambiguous and
non-intuitive). With `matchbraceleft` disabled, micro will only match
the brace character that is precisely under the cursor, and also when
jumping to the matching brace, will always move cursor precisely to the
matching brace character, not to the character next to it.
Nota bene: historical journey:
- There was already a `matchbraceleft` option introduced in commit
ea6a87d41a, when this feature (matching brace to the left) was
introduced first time. That time it was matching _only_ the brace
to the left, _instead_ of the brace under the cursor, and was
disabled by default.
- Later this feature was removed during the big refactoring of micro.
- Then this feature was reintroduced again in commit d1e713ce08, in
its present form (i.e. combined brace matching both under the cursor
and to the left, simulating I-beam cursor behavior), and it was
introduced unconditionally, without an option to disable it.
- Since then, multiple users complained about this feature and asked
for an option to disable it, so now we are reintroducing it as an
option again (this time enabled by default though).
Also tweaked the behavior of Paragraph/{Previous/Next} so that it skips
all empty lines immediately next to cursor position, until it finds the
start/end of the paragraph closest to it. Once it finds the paragraph
closest to it, the same behavior as before applies. With the previous
behavior if the cursor was surrounded by empty lines, then
Paragraph/{Previous/Next} would only jump to the next empty line,
instead of jumping to the start/end of a paragraph.
Instead of calling execAction() and then letting it check whether it
should actually execute this action, do this check before calling
execAction(), to make the code clear and straightforward.
Precisely: for multicursor actions, call execAction() in a loop for
every cursor, but for non-multicursor actions, call execAction() just
once, without a loop. This, in particular, allows to get rid of the
hacky "c == nil" check, since we no longer iterate a slice that may
change in the meantime (since SpawnMultiCursor and RemoveMultiCursor
are non-multicursor actions and thus are no longer executed while
iterating the slice).
- SpawnMultiCursor and RemoveMultiCursor actions change the set of
cursors, so we cannot assume that it stays the same. So refresh the
`cursors` list after executing every action in the chain.
- If execAction() did not execute an action since it is not a
multicursor, it should return true, not false, to not prevent
executing next actions in the chain.
If the original selection was not done by the user manually but as a
result of the initial SpawnMultiCursor, deselect this original selection
if we execute RemoveMultiCursor and there is no multicursor to remove
(i.e. the original spawned cursor is the only one). This improves user
experience by making RemoveMultiCursor behavior nicely symmetrical to
SpawnMultiCursor.
When there is no selection (i.e. selection is empty), SkipMultiCursor
searches for the empty text, "finds" it as the beginning of the buffer,
and as a result, jumps to the beginning of the buffer, which confuses
the user. Fix it.
ClearInfo and ClearStatus actions do exactly the same thing. Let's keep
them both, for compatibility reasons (who knows how many users are using
either of the two), but at least document that there is no difference
between the two.
Return false if there is nothing to undo/redo.
This also fixes false "Undid action" and "Redid actions" infobar
messages in the case when no action was actually undone or redone.
Fix regression caused by the fix 0de16334d3 ("micro: Don't forward
nil events into the sub event handler"): even if the terminal was
started with `wait` set to false, it is not closed immediately after
it finished its job, instead it shows "Press enter to close".
The reason is that since the commit b68461cf72 ("Terminal plugin
callback support") the termpane code has been (slightly hackily) relying
on nil events as notifications to close the terminal after it finished
its job. So fix this by introducing a separate CloseTerms() function
for notifying termpanes about that, decoupled from HandleEvent() which
is for tcell events only.