Let calcHash() unconditionally hash whatever buffer it is asked to hash,
and let its callers explicitly check if the buffer is too large before
calling calcHash(). This makes things simpler and less error-prone
(no extra source of truth about whether the file is too large, we don't
need to remember to check if calcHash() fails, we can be sure calcHash()
will actually update the provided hash), and actually faster (since just
calculating the buffer size, i.e. adding line lengths, is faster than
md5 calculation).
In particular, this fixes the following bugs:
1. Since ReOpen() doesn't check calcHash() return value, if the reloaded
file is too large while the old version of the file is not,
calcHash() returns ErrFileTooLarge and doesn't update origHash, so
so Modified() returns true since the reloaded file's md5 sum doesn't
match the old origHash, so micro wrongly reports the newly reloaded
file as modified.
2. Since Modified() doesn't check calcHash() return value, Modified()
may return false positives or false negatives if the buffer has
*just* become too large so calcHash() returns ErrFileTooLarge and
doesn't update `buff`.
When we have already enabled `fastdirty` but have not updated origHash
yet, we shouldn't use Modified() since it depends on origHash which is
still outdated, and thus returns wrong values.
This fixes the following issue: enable `fastdirty`, modify the buffer,
save the buffer and disable `fastdirty` -> micro wrongly reports the
buffer as modified (whereas it has just been saved).
Note that this fix, though, also causes a regression: e.g. if we run
`set fastdirty false` while fastdirty is already disabled, micro may
unexpectedly report a non-modified buffer as modified (in the case if
isModified is true but the buffer it actually not modified, since its
md5 sum matches and fastdirty is disabled), since this fix assumes that
since we are disabling fastdirty, it has been enabled. This shall be
fixed by PR #3343 which makes `set` do nothing if the option value
doesn't change.
This behavior is then aligned to the actual documentation of `fastdirty`.
Additionally set the origHash to zero in case the buffer was already modified.
Make calcHash() respect the buffer's file endings (unix vs dos), to make
its calculation of the file size consistent with how we calculate it in
other cases (i.e. when opening or saving the file) and with the
`fastdirty` option documentation, i.e. make calcHash() return
ErrFileTooLarge if and only if the exact file size exceeds 50KB.
According to the Go hash package documentation [1]:
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
io.Writer
[1] https://pkg.go.dev/hash#Hash
Remember the cursor location in TextEvent just once - when the original
text event happens, so that when we redo after an undo, the cursor is
placed at the location where the actual redone modification happens (as
the user would expect), not at the location where the cursor was before
the undo (which may be a completely unrelated location and may be far
away).
Fixes#3411
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.
Saving a buffer every time without even checking if it was modified
(i.e. even when the user is not editing the buffer) is wasteful,
especially if the autosave period is set to a short value.
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.
When updateDiffSync() is called asynchronously, it should lock the
line array when calling Bytes(), to prevent race if the line array is
being modified by the main goroutine in the meantime.
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.
Fix `set filetype unknown` not working as expected in the following
scenario:
1. open foo.txt (no filetype detected) -> ft is `unknown`, highlighted
with default.yaml, as expected
2. `set filetype go` -> ft is `go`, highlighted with go.yaml as expected
3. `set filetype unknown` -> ft is still `go`, still highlighted with
go.yaml (whereas expected behavior is: ft is `unknown`, highlighted
with default.yaml)
Fix that by always updating b.SyntaxDef value, not reusing the old one.
This also makes the code simpler and easier to understand.
This is necessary as a preparation to introduce a lock for the whole LineArray.
The modification can then be done without trying to lock the same lock twice.
Co-authored-by: Dmytro Maluka <dmitrymaluka@gmail.com>
Adding InitRuntimeFiles() to buffer_test.go has changed the behavior
of this test: now it tests not just buffer editing per se, but also
how well buffer editing works together with syntax highlighting (since
InitRuntimeFiles() loads syntax files, and many of the test buffers
match the json header pattern in the json.yaml syntax file, so they are
"highlighted" as json). This revealed long existing races between
buffer editing and syntax highlighting.
Until we fix those races, temporarily disable InitRuntimeFiles() in this
test.
When initializing runtime files (syntax files etc) in tests, initialize
built-in runtime files only, to ensure that the tests are not affected
by whatever is in ~/.config/micro/ on the test machine.
micro_test.go already ensures that, by using its own temporary directory
as an (empty) config directory. So we only need to fix buffer_test.go
and rtfiles_test.go. In those tests, don't repeat the same dance with
a temporary directory, instead just ignore the config directory.
Adding InitPlugins() to tests has caused noisy error logs when running
the buffer_test.go test (although the test result is still PASS):
2024/03/23 15:14:30 Plugin does not exist: autoclose at autoclose : &{autoclose autoclose <nil> [runtime/plugins/autoclose/autoclose.lua] false true}
2024/03/23 15:14:30 Plugin does not exist: comment at comment : &{comment comment <nil> [runtime/plugins/comment/comment.lua] false true}
2024/03/23 15:14:30 Plugin does not exist: diff at diff : &{diff diff <nil> [runtime/plugins/diff/diff.lua] false true}
2024/03/23 15:14:30 Plugin does not exist: ftoptions at ftoptions : &{ftoptions ftoptions <nil> [runtime/plugins/ftoptions/ftoptions.lua] false true}
...
These errors are caused simply by the fact that plugins are initialized
but not loaded. Adding config.LoadAllPlugins() to buffer_test.go "fixes"
this problem.
However, at the moment it doesn't seem a good idea to load plugins in
buffer_test.go, since buffer_test.go doesn't properly initialize Lua. It
only does ulua.L = lua.NewState() but doesn't do the other stuff that
init() in cmd/micro/initlua.go does. As a result, plugins will not be
able to do anything correctly.
So in order to initialize Lua correctly we need to be inside cmd/micro/,
so we cannot do it in buffer_test.go or any other tests except
micro_test.go.
Replacing header patterns with signature patterns was a mistake, since
both are quite different from each other, and both have their uses. In
fact, this caused a serious regression: for such files as shell scripts
without *.sh extension but with #!/bin/sh inside, filetype detection
does not work at all anymore.
Since both header and signature patterns are useful, reintroduce support
for header patterns while keeping support for signature patterns as well
and make both work nicely together.
Also, unlike in the old implementation (before signatures were
introduced), ensure that filename matches take precedence over header
matches, i.e. if there is at least one filename match found, all header
matches are ignored. This makes the behavior more deterministic and
prevents previously observed issues like #2894 and #3054: wrongly
detected filetypes caused by some overly general header patterns.
Precisely, the new behavior is:
1. if there is at least one filename match, use filename matches only
2. if there are no filename matches, use header matches
3. in both cases, try to use signatures to find the best match among
multiple filename or header matches
The original meaning of foundDef was: "we already found the final syntax
definition in a user's custom syntax file". After introducing signatures
its meaning became: "we found some potential syntax definition in a
user's custom syntax file, but we don't know yet if it's the final one".
This makes the code confusing and actually buggy.
At least one bug is that if we found some potential filename matches in
the user's custom syntax files, we don't search for more matches in the
built-in syntax files. Which is wrong: we should keep searching for as
many potential matches as possible, in both user's and built-in syntax
files, to select the best one among them.
Fix that by restoring the original meaning of foundDef and updating the
logic accordingly.
* 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.
* 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>
Handle the case when the cursor itself hasn't really moved to
another line, but its line number has changed due to insert
or remove of some lines above.
In this case, if the cursor is still at its new trailingws,
we should not reset NewTrailingWsY to -1 but update it to the
new line number.
A scenario exemplifying this issue:
Bind some key, e.g. Alt-r, to such a lua function:
function insertNewlineAbove(bp)
bp.Buf:Insert(buffer.Loc(0, bp.Cursor.Y), "\n")
end
Then in a file containing these lines:
aaa
bbb
ccc
insert a space at the end of bbb line, and then press Alt-r.
bbb and ccc are moved one line down, but also the trailing space
after bbb becomes highlighted, which isn't what we expect.
This commit fixes that.
Added option `hltrailingws` for highlighting trailing whitespaces
at the end of lines. Note that it behaves in a "smart" way.
It doesn't highlight newly added (transient) trailing whitespaces
that naturally occur while typing text. It would be annoying to
see transient highlighting every time we enter a space at the end
of a line while typing.
So a newly added trailing whitespace starts being highlighting
only after the cursor moves to another line. Thus the highlighting
serves its purpose: it draws our attention to annoying sloppy
forgotten trailing whitespaces.
* Update docs to include `matchbracestyle`
* Add `matchbracestyle` to infocomplete.go
* Add validator and default settings for `matchbracestyle`
* Highlight or underline braces based on `matchbracestyle`
* Add `match-brace` to default colorschemes
* Correct `FindMatchingBrace()` counting
Make brace under the cursor have priority over brace to the left in
ambiguous cases when matching braces
Co-authored-by: Dmitry Maluka <dmitrymaluka@gmail.com>
* Fix conflicts
---------
Co-authored-by: Jöran Karl <3951388+JoeKar@users.noreply.github.com>
Co-authored-by: Dmitry Maluka <dmitrymaluka@gmail.com>
* SpawnMultiCursorUp/Down: change order of adding cursors
SpawnMultiCursor{Up,Down} currently works in a tricky way: instead of
creating a new cursor above or below, it moves the current "primary"
cursor above or below, and then creates a new cursor below or above the
new position of the current cursor (i.e. at its previous position),
creating an illusion for the user that the current (top-most or
bottom-most) cursor is a newly spawned cursor.
This trick causes at least the following issues:
- When the line above or below, where we spawn a new cursor, is shorter
than the current cursor position in the current line, the new cursor
is placed at the end of this short line (which is expected), but also
the current cursor unexpectedly changes its x position and moves
below/above the new cursor.
- When removing a cursor in RemoveMultiCursor (default Alt-p key), it
non-intuitively removes the cursor which, from the user point of view,
is not the last but the last-but-one cursor.
Fix these issues by replacing the trick with a straightforward logic:
just create the new cursor above or below the last one.
Note that this fix has a user-visible side effect: the last cursor is
no longer the "primary" one (since it is now the last in the list, not
the first), so e.g. when the user clears multicursors via Esc key, the
remaining cursor is the first one, not the last one. I assume it's ok.
* SpawnMultiCursorUp/Down: move common code to a helper fn
* SpawnMultiCursorUp/Down: honor visual width and LastVisualX
Make spawning multicursors up/down behave more similarly to cursor
movements up/down. This change fixes 2 issues at once:
- SpawnMultiCursorUp/Down doesn't take into account the visual width of
the text before the cursor, which may be different from its character
width (e.g. if it contains tabs). So e.g. if the number of tabs before
the cursor in the current line is not the same as in the new line, the
new cursor is placed at an unexpected location.
- SpawnMultiCursorUp/Down doesn't take into account the cursor's
remembered x position (LastVisualX) when e.g. spawning a new cursor
in the below line which is short than the current cursor position, and
then spawning yet another cursor in the next below line which is
longer than this short line.
* SpawnMultiCursorUp/Down: honor softwrap
When softwrap is enabled and the current line is wrapped, make
SpawnMultiCursor{Up,Down} spawn cursor in the next visual line within
this wrapped line, similarly to how we handle cursor movements up/down
within wrapped lines.
* SpawnMultiCursorUp/Down: deselect when spawning cursors
To avoid weird user experience (spawned cursors messing with selections
of existing cursors).
* Fixed newline format detection for files not ending with a newline
Files with Windows-style line endings were being converted to
Unix-style if the file did not end with a newline
* Updated file format detection fix for consistency
Similarly to the crash fixed by #2967, which happens if sudo failed,
a crash also happens when sudo even fails to start. The reason for
the crash is also similar: nil dereference of screen.Screen caused by
the fact that we do not restore temporarily disabled screen.
To reproduce this crash, set the `sucmd` option to some non-existing
command, e.g. `aaa`, and try to save a file with root privileges.
* Fix panic due to invalid regex in a syntax file
When a user's custom syntax file has a malformed filename regex or
header regex, MakeHeaderYaml() returns error but we do not properly
handle it, which results in a panic due to a dereference of the `header`
pointer which is nil:
Micro encountered an error: runtime.errorString runtime error: invalid memory address or nil pointer dereference
runtime/panic.go:221 (0x44c367)
runtime/panic.go:220 (0x44c337)
github.com/zyedidia/micro/v2/internal/buffer/buffer.go:709 (0x82bc0f)
github.com/zyedidia/micro/v2/internal/buffer/buffer.go:392 (0x828292)
github.com/zyedidia/micro/v2/internal/buffer/buffer.go:261 (0x8278c8)
github.com/zyedidia/micro/v2/cmd/micro/micro.go:203 (0x8b9e7b)
github.com/zyedidia/micro/v2/cmd/micro/micro.go:331 (0x8ba9e5)
runtime/proc.go:255 (0x4386a7)
runtime/asm_amd64.s:1581 (0x467941)
* Do not ignore invalid filename regex error in a syntax file
When the filename regex in a syntax file is malformed but the subsequent
header regex is correct, the filename regex error gets silently ignored,
since the `err` value is overwritten by the subsequent successful header
regex result.
On modern Linux systems, it can take 30 seconds for
the data to actually hit the disk (check
/proc/sys/vm/dirty_expire_centisecs).
If the computer crashes in those 30 seconds, the user
may end up with an empty file as seen here:
https://github.com/neovim/neovim/issues/9888
This is why editors like vim and nano call
the fsync syscall after they wrote the file.
This syscall is available as file.Sync() in Go.
Running strace against micro shows that fsync is
called as expected:
$ strace -f -p $(pgrep micro) -e fsync
strace: Process 3284344 attached with 9 threads
[pid 3284351] fsync(8) = 0
Also, we now catch errors returned from w.Flush().
* Comment fix & gofmt fix
* Goto next/previous diff commands
These commands will work in `git` repositories or whenever `set diff on` is
working. They are bound to `Alt-[` and `Alt-]` by default. I would prefer
`Alt-Up` and `Alt-Down`, but that's already taken.
There are no tests at the moment; I'm looking into writing some since that will
be needed for the rest of the plan to make
https://github.com/zyedidia/micro/discussions/2753 a reality. I'm not sure how
difficult that will be.
* Realign JSON in keybindings.md
* Support for highlighting all search matches (hlsearch)
hlsearch is implemented efficiently using the buffer's line array,
somewhat similarly to the syntax highlighting.
Unlike the syntax highlighter which highlights the entire file,
hlsearch searches for matches for the displayed lines only.
Matches are searched when the given line is displayed first time
or after it was modified. Otherwise the previously found matches
are used.
* Add UnhighlightSearch action
and add it to the list of actions triggered by Esc key by default.
* Add comment explaining the purpose of search map
* Add hlsearch colors to colorschemes
Mostly just copied from the corresponding original (mostly vim) colorschemes.
* Highlight matches during/after replace as well
As a side effect it also changes the last search value, i.e. affects FindNext
and FindPrevious, but it's probably fine. In vim it works the same way.
* Improve hlsearch option description
Modified behavior of CursorUp, CursorDown, CursorPageUp etc:
if softwrap is enabled, cursor moves by visual lines, not logical lines.
TODO: implement it also for Home and End keys: move cursor to the
visual start or end of a line. I haven't implemented it for now, because
I'm not sure what should be the behavior of StartOfTextToggle then
(considering that Home key is bound to StartOfTextToggle by default).
Fixes#1598
Fix regressions after ba98b55:
- Unable to override filetype autodetection by setting a specific filetype
for specific files, i.e. this doesn't work:
"*.h": {
"filetype": "c++"
},
- Unable to enable/disable syntax highlighting for specific files,
i.e. this doesn't work:
"*.c": {
"syntax": false
},
- "readonly" setting doesn't work (neither global nor per-filetype).
Fix the regression after 3b34a02: setting readonly option to true
in onBufferOpen lua callback doesn't work, since it is automatically
reset to false if write permission is not denied.