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.
The lastCutTime feature (reset the clipboard instead of appending to the
clipboard if the last CutLine was more than 10 seconds ago) was
implemented 8 years ago but was always buggy and never really worked,
until we have accidentally found and fixed the bug just now. No one ever
complained or noticed that, which means it is not a very useful feature.
Fixing it changes the existing behavior (essentially adds a new feature
which did not really exist before) and there is no reason to assume that
this new behavior will be welcome by users. So it's better to remove
this feature.
According to tcell documentation, Rune() should only be used for KeyRune
events. Otherwise its return value is not guaranteed and should not be
relied upon.
This fixes issue #2947: Esc key not working on Windows, since tcell
sends lone Esc key event with rune == 0 on Unix but with rune == 27
(the keycode) on Windows.
Similarly to CutLine, DeleteLine and CopyLine actions, if there is a
selection, duplicate not just the current line but all the lines covered
(fully or partially) by the selection.
- Add a new Duplicate action which just duplicates the selection (and
returns false if there is no selection).
- Change the behavior of the DuplicateLine action to only duplicate the
current line, not the selection.
- Change the default action bound to Ctrl-d from DuplicateLine to
Duplicate|DuplicateLine, so that the default behavior doesn't change.
This allows the user to rebind keybindings in a more flexible way, i.e.
to choose whether a key should duplicate just lines, or just selections,
or both, - in a similar fashion to Copy, Cut, Delete actions.
If we ever encounter this clipboard.Read() failure, return false
immediately. Otherwise, InfoBar.Error(err) will have no effect (it will
be immediately overwritten by InfoBar.Message()) so we won't even know
that there was an error.
Weird behavior is observed e.g. if we cut some lines with CutLine, then
copy some selection with Copy, then cut some other lines with CutLine,
and then paste. The pasted cliboard contains not just the lines that
were cut at the last step, but also the selection that was copied before
that.
Fix that by resetting the CutLine's repeated line cuts whenever we
copy anything to the clipboard via any other action (Cut, Copy or
CopyLine).
Since CutLine may add lines to the clipboard instead of replacing the
clipboard, improve its info message to show how many lines are in the
clipboard in total, not just how many lines were added to it last time.
When there is a selection containing multiple lines, CutLine, DeleteLine
and CopyLine actions currently cut/delete/copy just the "current" line,
as usual. This behavior is at least confusing, since when there is a
selection, the cursor is not displayed, so the user doesn't know which
line is the current one.
So change the behavior. When there is a multi-line selection,
cut/delete/copy all lines covered by the selection, not just the current
line. Note that it will cut/delete/copy whole lines, not just the
selection itself, i.e. if the first and/or the last line of the
selection is only partially within the selection, we will
cut/delete/copy the entire first and last lines nonetheless.
Change behavior of the Cut action: don't implicitly call CutLine if
there is no selection. Instead, make it return false in this case
and change the default Ctrl-x binding to Cut|CutLine, to make it clear,
explicit and in line with Copy and CopyLine actions.
Make Copy return false if there is no selection, and change the default
binding for Ctrl-c from CopyLine|Copy to Copy|CopyLine accordingly,
to make the semantics more meaningful: copying selection always fails
if there is no selection.
The CutLine action has a feature: if we execute it multiple times to cut
multiple lines, new cut lines are added to the previously cut lines in
the clipboard instead of replacing the clipboard, unless those
previously cut lines have been already pasted or the last cut was more
than 10 seconds ago. This last bit doesn't really work: newly cut lines
are appended to the clipboard regardless of when was the last cut.
So fix it.
When the cursor is at the last line of buffer and it is an empty line,
CopyLine does not copy this line, which is correct, but it shows a bogus
"Copied line" message. Fix this by adding a check for that, same as in
CutLine and DeleteLine.
After executing the CopyLine action, moving cursor up or down
unexpectedly moves cursor to the beginning of the line, since its
LastVisualX value is lost in the selection/deselection manipulations.
Fix this by restoring the original LastVisualX.
After executing CutLine or DeleteLine action, the cursor is at the
beginning of a line (as expected) but then moving the cursor up or down
moves it to an unexpected location in the middle of the next or previous
line. Fix this by updating the cursor's LastVisualX.
Instead of passing a single brace pair to FindMatchingBrace(), make it
traverse all brace pairs in buffer.BracePairs on its own.
This has the following advantages:
1. Makes FindMatchingBrace() easier to use, in particular much easier
to use from Lua.
2. Lets FindMatchingBrace() ensure that we use just one matching brace -
the higher-priority one. This fixes the following issues:
([foo]bar)
^
when the cursor is on `[`:
- Both `[]` and `()` pairs are highlighted, whereas the expected
behavior is that only one pair is highlighted - the one that the
JumpToMatchingBrace action would jump to.
- JumpToMatchingBrace action incorrectly jumps to `)` instead of
`]` (which should take higher priority in this case).
In contrast, with `((foo)bar)` it works correctly.
The callback passed to UpdateDiff() is superfluous: in the synchronous
case screen.Redraw() is not needed anyway (since the screen is redrawn
at every iteration of the main loop), and in the asynchronous case
UpdateDiff() can just call screen.Redraw() directly.
Ensure that the selection start is always before the selection end,
regardless of the direction of a mouse selection, to make
h.Cursor.Deselect() handle its `start` argument correctly.
This makes the cursor behavior after mouse selections consistent with
the cursor behavior after keyboard selections.
Fixes#3055
Previously `CursorDown` function called `Deselect` with a wrong
argument which lead to the situation when cursor was moved to the
start instead of the end of the selection
Signed-off-by: Yevhen Babiichuk (DustDFG) <dfgdust@gmail.com>
Fix the following buggy behavior:
1. bind "<n><a>" to the Paste action in the command bar
2. open a split pane, type some text and press Ctrl-q to close it
3. answer "n" to the "Save changes before closing?" prompt
4. press Ctrl-e to open the command prompt and press "a"
-> result: instead of inserting the "a" letter, clipboard is pasted.
It is not really defined what is the meaning of this return value.
Currently this value is always true. And even if this value actually
meant something (for example, the result of the last executed action
in the chain), we should not use this value in HandleEvent(). The key
event handling logic should behave the same regardless of whether the
action triggered by this key succeeded or not.
If onBufPaneOpen callback execution fails (e.g. due to a Lua runtime
error), report this error to the user, like we do for all other Lua
callbacks, rather than silently continue working as if nothing
happened.
After reloading a file that has been externally modified, the buffer
view may become invalid: the displayed subset of lines of the file may
no longer exist, since the file may have been truncated. So relocate the
buffer view in this case.
In particular, this fixes crashes caused by out of bounds accesses to
the line array by displayBuffer() trying to display no longer existing
lines.
Currently onSetActive is called when the user clicks with the mouse on
a pane even if this pane is already active. We should avoid calling it
in this case.
Implementation detail: like with tabs in the previous commit, we cannot
check if the pane is already active just by checking the index passed
to the Tab's SetActive() (since the index may not change while the pane
itself changes), we need to check state of the pane itself. So we move
the onSetActive invocation from the Tab's SetActive() to the BufPane's
SetActive().
We should call the onSetActive callback not only when switching to
another bufpane within the same tab but also when switching to another
tab.
Note on implementation details:
- In SetActive() we need to check if the tab is not already active, to
avoid calling onSetActive for an already active bufpane.
- We cannot check that just by checking if the tab index passed to
SetActive() is different from the current active tab index, since this
index may remain the same even if the tab itself is different (in the
case of removing a tab from the tablist). So we need to check the tab
itself, not just the tab index. So we introduce the isActive field,
to track the tab's active state in the Tab structure itself.
* infocomplete: Complete filetypes (follow-up)
The first shot of the feature unfortunately completed the *.yaml file
names instead of the included filetypes. This will be corrected with
this follow up.
* infocomplete: Correct comment of filetypeComplete according to review hint
Co-authored-by: Dmytro Maluka <dmitrymaluka@gmail.com>
---------
Co-authored-by: Dmytro Maluka <dmitrymaluka@gmail.com>
`unknown` is a valid value for the `filetype` option (and executing
`set filetype unknown` does what is expected: it forces filetype
autodetection). So let's add `unknown` to the autocomplete suggestions
for `filetype`, along with actual filetypes.
* rtfiles: Initialize all-/realFiles and Plugins in InitRuntimeFiles
* command: Reload plugins at ReloadCmd too
* command: Don't reload plugins in case of ReloadConfig()
* rtfiles: Split InitRuntimeFiles() into one func for assets and one for plugins
* rtfiles: Remove the unnecessary init function
With this modification the InitRuntimeFiles() and InitPlugins() (if needed)
must be called first, otherwise uninitialized runtime file variables are most
likely.
InfoBar should really receive the resize event, to know the window width
in order to do horizontal scrolling of the command line when it doesn't
fit in the screen. Although currently it doesn't scroll the command line
at all (see issue #2527) and just ignores the resize event, but we
should fix that anyway, so let's forward the resize event to it.
* command: Fix replace to be able to insert '$'
* help: commands: Precise the documentation of `replace`
* help: commands: Further improvement suggested within the review
Co-authored-by: Beni Cherniavsky-Paskin <cben@redhat.com>
* Fix replace with '$' in a more kosher way
On top of JoeKar's fix.
---------
Co-authored-by: Beni Cherniavsky-Paskin <cben@redhat.com>
Co-authored-by: Dmytro Maluka <dmitrymaluka@gmail.com>
* settings: Move all options to the start of the file
This will help with the overview of all available options and their optional
validators.
* settings: Add generic string option validator
* settings: Autocomplete string options
Passing options via micro -option=value in the command line should only
temporarily override the option value for the current micro session,
not change it permanently in settings.json. But currently it wrongly
writes it to settings.json in the case when the value passed via command
line is the default value of this option, while the current permanent
setting in settings.json is a non-default value.
Fixes#3005
If we add something like this to init.lua:
config.AddRuntimeFile("status", config.RTHelp, "help/foo.md")
then start micro and run "help foo", the resulting error message looks
weird:
Unable to load help textfooopen plugins/status/help/foo.md: file does not exist
Change it to:
Unable to load help text for foo: open plugins/status/help/foo.md: file does not exist
- If a mouse event is bound to a Lua function, pass *tcell.EventMouse to
this Lua function, so that it can find out the position where a button
was clicked etc, just like the built-in MousePress and MouseMultiCursor
actions.
- Make mouse actions more a first-class citizen: allow chaining them and
running onAction and preAction callbacks for them, just like key actions.
Improve user experience: if we are at a line with a new (i.e.
not highlighted yet) trailingws and we begin selecting text,
don't highlight the trailingws until we are done with selection,
even if we moved the cursor to another line while selecting.