If a document is written with a new path, currently, in the event that
the write fails, the document still gets its path changed. This fixes
it so that the path is not updated unless the write succeeds.
The way that document writes are handled are by submitting them to the
async job pool, which are all executed opportunistically out of order. It
was discovered that this can lead to write inconsistencies when there
are multiple writes to the same file in quick succession.
This seeks to fix this problem by removing document writes from the
general pool of jobs and into its own specialized event. Now when a
user submits a write with one of the write commands, a request is simply
queued up in a new mpsc channel that each Document makes to handle its own
writes. This way, if multiple writes are submitted on the same document,
they are executed in order, while still allowing concurrent writes for
different documents.
Instead of repeatedly checking if it is in_bounds, calculate the
max_indent beforehand and just loop. I added a debug_assert to "prove"
that it never tries drawing out of bounds.
Better performance, and otherwise very long lines with lots of tabs
will wrap around the u16 and come back on the other side, messing up
the beginning skip_levels.
Also changes workspace diagnostic picker bindings to <space>D and
changes the debug menu keybind to <space>g, the previous diagnostic
picker keybind. This brings the diagnostic picker bindings more in
line with the jump to next/previous diagnostic bindings which are
currently on ]d and [d.
The debug assertion that document diagnostics are sorted incorrectly
panics for cases like `[161..164, 162..162]`. The merging behavior
in the following lines that relies on the assertion only needs the
input ranges to be sorted by `range.start`, so this change simplifies
the assertion to only catch violations of that assumption.
Undo/redo/earlier/later call `Document::apply_impl` which applies
transactions to the document. These transactions also need to be
applied to the view as in 0aedef0.
Here we separate the diagnostics by severity and then overlay the Vec
of spans for each severity on top of the highlights. The error
diagnostics end up overlaid on the warning diagnostics, which are
overlaid on the hints, overlaid on info, overlaid on any other severity
(default), then overlaid on the syntax highlights.
This fixes two things:
* Error diagnostics are now always visible when overlapped with other
diagnostics.
* Ghost text is eliminated.
* Ghost text was caused by duplicate diagnostics at the EOF:
overlaps within the merged `Vec<(usize, Range<usize>)>` violate
assumptions in `helix_core::syntax::Merge`.
* When we push a new range, we check it against the last range and
merge the two if they overlap. This is safe because they both
have the same severity and therefore highlight.
The actual merge is skipped for any of these when they are empty, so
this is very fast in practice. For some data, I threw together an FPS
counter which renders as fast as possible and logs the renders per
second.
With no diagnostics, I see an FPS gain from this change from 868 FPS
to 878 (+1.1%) on a release build on a Rust file. On an Erlang file
with 12 error diagnostics and 6 warnings in view (233 errors and 66
warnings total), I see a decrease in average FPS from 795 to 790
(-0.6%) on a release build.
It is easy to forget to call `Document::apply` and/or `View::apply` in
the correct order. This commit introduces a helper function which
closes over both calls.
This change adds View::apply calls for all Document::apply call-sites,
ensuring that changes to a document do not leave invalid entries in
the View's jumplist.
* Implement cursorcolumn
* Add documentation
* Separate column style from line with fallback
* Fallback to cursorcolumn first
* Switch to non-fallback try_get_exact
Add new function `try_get_exact`, which doesn't perform fallback,
and use that instead because the fallback behaviour is being handled
manually.
If the close method fails, the editor will quit before restoring the
terminal. This causes the shell to break if, e.g. the LS times out
shutting down.
This fixes this by always restoring the terminal after closing, and
printing out a message to stderr if there is an error.
This change automatically tracks pending text for for commands which use
on-next-key callbacks. For example, `t` will await the next key event
and "t" will be shown in the bottom right-hand corner to show that we're
in a pending state.
Previously, the text for these on-next-key commands needed to be
hard-coded into the command definition which had some drawbacks:
* It was easy to forget to write and clear the pending text.
* If a command was remapped in a custom config, the pending text would
still show the old key.
With this change, pending text is automatically tracked based on the
key events that lead to the command being executed. This works even
when the command is remapped in config and when the on-next-key
callback is nested under some key sequence (for example `mi`).
* Keep arrow and special keys in insert
Advanced users won't need it and is useful for beginners.
Revert part of #3671.
* Change text for insert mode section
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
* Remove ctrl-up/down in insert
* Reorganize insert keys and docs
* Improve page up experience on last tutor
The last tutor page can page down multiple times and it will break the
heading on the 80x24 screen paging when reaching the last page, this
keeps the style the same and make sure page up and down won't break it.
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
* Change focus to modified docs on quit
When quitting with modified documents, automatically switch focus to
one of them.
* Update helix-term/src/commands/typed.rs
Co-authored-by: Poliorcetics <poliorcetics@users.noreply.github.com>
* Make it work with buffer-close-all and the like
* Cleanup
Use Cow instead of String, and rename DoesntExist -> DoesNotExist
Co-authored-by: Poliorcetics <poliorcetics@users.noreply.github.com>
* Add option to skip the first indent guide
* reorder skip_first option
* change indent-guides.skip_first to a number
* rename skip -> skip_levels
* add skip_levels to the book
* Update book/src/configuration.md
Co-authored-by: A-Walrus <58790821+A-Walrus@users.noreply.github.com>
* Update helix-term/src/ui/editor.rs
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
Co-authored-by: Robin <robinvandijk@klippa.com>
Co-authored-by: A-Walrus <58790821+A-Walrus@users.noreply.github.com>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* keymap: Rename A "Insert at end of line"
The language for the `A` binding is potentially confusing because
`A` behaves like `i` done at the end of the line rather than `a`.
This change renames the command to match Kakoune's language[^1].
[^1]: 021da117cf/src/normal.cc (L2229)
* keymap: Rename I `insert_at_line_start`
* Select inserted space after join
* Split join_selections with space selection to A-J
Kakoune does that too and some users may still want to retain their selections.
* Update join_selections docs
When signature help is too large it may cause a panic when it is too
large, now I just make the hover do an intersection with surface to make
sure it never overflow.
This changes the behavior of operations like `]f`/`[f` to set the
direction of the new range to the direction of the action.
The original behavior was to always use the head of the next function.
This is inconsistent with the behavior of goto_next_paragraph and makes
it impossible to create extend variants of the textobject motions.
This causes a behavior change when there are nested functions. The
behavior in the parent commit is that repeated uses of `]f` will
select every function in the file even if nested. With this commit,
functions are skipped.
It's notable that it's possible to emulate the original behavior by
using the `ensure_selections_forward` (A-:) command between invocations
of `]f`.
* Show "Invalid regex" message on enter (Validate)
* Reset selection on invalid regex
* Add popup for invalid regex
* Replace set_position with position
* Make popup auto close
* Split helix_core::find_root and helix_loader::find_local_config_dirs
The documentation of find_root described the following priority for
detecting a project root:
- Top-most folder containing a root marker in current git repository
- Git repository root if no marker detected
- Top-most folder containing a root marker if not git repository detected
- Current working directory as fallback
The commit contained in https://github.com/helix-editor/helix/pull/1249
extracted and changed the implementation of find_root in find_root_impl,
actually reversing its result order (since that is the order that made
sense for the local configuration merge, from innermost to outermost
ancestors).
Since the two uses of find_root_impl have different requirements (and
it's not a matter of reversing the order of results since, e.g., the top
repository dir should be used by find_root only if there's not marker in
other dirs), this PR splits the two implementations in two different
specialized functions.
In doing so, find_root_impl is removed and the implementation is moved
back in find_root, moving it closer to the documented behaviour thus
making it easier to verify it's actually correct
* helix-core: remove Option from find_root return type
It always returns some result, so Option is not needed
* Improve keymap errors from command typos
Currently, opening helix with a config containing a bad command mapping
fails with a cryptic error. For example, say we have a config (bad.toml)
with a command name that doesn't exist:
[keys.normal]
b = "buffer_close" # should be ":buffer-close"
When we `hx -c bad.toml`, we get...
> Bad config: data did not match any variant of untagged enum KeyTrie for key `keys.normal` at line 1 column 1
> Press <ENTER> to continue with default config
This is because of the way that Serde tries to deserialize untagged
enums such as `helix_term::keymap::KeyTrie`. From the Serde docs[^1]:
> Serde will try to match the data against each variant in order and the
> first one that deserializes successfully is the one returned.
`MappableCommand::deserialize` fails (returns an Err variant) when a
command does not exist. Serde interprets this as the `KeyTrie::Leaf`
variant failing to match and declares that the input data doesn't
"match any variant of untagged enum KeyTrie."
Luckily the variants of KeyTrie are orthogonal in structure: we can tell
them apart by the type hints from a `serde:🇩🇪:Visitor`. This change
uses a custom Deserialize implementation along with a Visitor that
discerns which variant of the KeyTrie applies. With this change, the
above failure becomes:
> Bad config: No command named 'buffer_close' for key `keys.normal.b` at line 2 column 5
> Press <ENTER> to continue with default config
We also provide more explicit information about the expectations on
the field. A config with an unexpected type produces a message with
that information and the expectation:
[keys.normal]
b = 1
> Bad config: invalid type: integer `1`, expected a command, list of commands, or sub-keymap for key `keys.normal.b` at line 2 column 5
> Press <ENTER> to continue with default config
[^1]: https://serde.rs/enum-representations.html#untagged
* Update helix-term/src/keymap.rs
Co-authored-by: Ivan Tham <pickfire@riseup.net>
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Add command line parameter to specify log file
I had the logs of my debug helix mixed in with the logs from the
production helix.
Add a `--log` command line argument to redirect any logs to other
files, making my debugging easier :-)
* Update completion files with `--log` argument
The tutor file is loaded as .txt which can potentially spawn a
language server. Then the path is unset, but the LS remains active.
This can cause panics since updates are now submitted for a doc
with no path.
As a quick workaround we remove the extension which should avoid
detection.
Fixes#3730
* Don't change config to default when refreshing invalid config
* Propely handle theme errors with config-reload
* Extract refresh theme into seperate function
Helix is first and foremost a modal editor. Willingness to support non-modal
editing is there, but it is not one that should be encouraged with the default
settings. There are an increasing number of users who are stumbling because
they are trying to use Helix as a non-modal editor, so this is an effort to
encourage new users to stop and take notice that Helix has a different paradigm
than VSCode, Sublime, etc. Users can still add these bindings back to their own
configs if they wish.
`extend_line_above` (and `extend_line` when facing backwards) skip
a line when the current range does not fully cover a line.
Before this change:
foo
b#[|a]#r
baz
With `extend_line_above` or `extend_line` selected the line above.
#[|foo
bar]#
baz
Which is inconsistent with `extend_line_below`. This commit changes
the behavior to select the current line when it is not already
selected.
foo
#[|bar]#
baz
Then further calls of `extend_line_above` extend the selection up
line-wise.
* fix: Recalculate completion when going through prompt history
* Update completion when the prompt line is changed
It should not be possible to update the line without also updating the
completion since the completion holds an index into the line.
* Fix Prompt::with_line recalculate completion
with_line was the last function where recalculate completion had to be
done manually. This function now also recalculates the completion so
that it's impossible to forget.
* Exit selection when recalculating completion
Keeping the selection index when the completion has been recalculated
doesn't make sense. This clears the selection automatically, removing
most needs to manually clear it.
* Remove &mut on save_filter
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
It was starting to diverge as the normal exit code was restoring the prompt but the panic code
wasn't, and the panic code was disabling bracketed paste but the normal code wasn't.
This changes the panic path slightly in that we won't disable raw mode if exiting alternate screen
and disabling bracketed paste fails. If that happens, things are so busted I don't think it matters
anyway.
Fixes a panic with a config like:
[keys.normal.space]
x = [":buffer-close"]
by bailing out of the command-execution handling if the document
doesn't exist after handling a command.
This refactor changes the overall structure of the goto_ts_object_impl
command without removing any functionality from its behavior. The
refactored motion:
* acts on all selections instead of reducing to one selection
* may be repeated with the `repeat_last_motion` (A-.) command
* informs the user when the syntax-tree is not accessible in the current buffer
This is invalid according to the [LSP spec]:
> In addition the server is not allowed to send any requests
> or notifications to the client until it has responded with an
> InitializeResult, with the exception that during the initialize
> request the server is allowed to send the notifications
> window/showMessage, window/logMessage and telemetry/event as well
> as the window/showMessageRequest request to the client.
So we should discard the message when the language server is not
yet initialized. This can happen if the server sends
textDocument/publishDiagnostics before responding to the initialize
request. clojure-lsp appears to exhibit this behavior in the wild.
[LSP Spec]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
This change adds documents to the view's document history Vec.
(This is used by `ga` for example to access the last buffer.)
Previously, a sequence like so would have confusing behavior:
1. Open file A: any document with an active language server
2. Find some definition that lives in another file - file B - with `gd`
3. Jump back in the jumplist with `C-o` to file A
4. Use `ga` intending to switch back to file B
The behavior prior to this change was that `ga` would switch to file
A: you could not use `ga` to switch to file B.
When changing focus, the lookup with `current!` may change the
view and end up executing mode transition hooks on the newly
focused view. We should use the same view and document to execute
mode transition hooks so that switching away from a view triggers
history save points.
* Derive Document language name from `languages.toml` `name` key
This changes switches from deriving the language name from the
`languages.toml` `scope` key to `name` (`language_id` in the
`LanguageConfiguration` type). For the most part it works to derive the
language name from scope by chopping off `source.` or `rsplit_once` on
`.` but for some languages we have now like html (`text.html.basic`),
it doesn't. This also should be a more accurate fallback for the
`language_id` method which is used in LSP and currently uses the
`rsplit_once` strategy.
Here we expose the language's name as `language_name` on `Document` and
replace ad-hoc calculations of the language name with the new method.
This is most impactful for the `file-type` statusline element which is
using `language_id`.
* Use `Document::language_name` for the `file-type` statusline element
The `file-type` indicator element in the statusline was using
`Document::language_id` which is meant to be used to for telling
Language Servers what language we're using. That works for languages
with `language-server` configurations in `languages.toml` but shows
text otherwise. The new `Document::language_name` method from the
parent commit is a more accurate way to determine the language.
* let extend-line respect range direction
* fix extend above logic
* keep `x` existing binding
* Update book/src/keymap.md
Co-authored-by: Ivan Tham <pickfire@riseup.net>
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Update description of `m` textobject to its actual functionality
Sometime recently the functionality of `m` was changed to match the
nearest pair to the cursor, rather than the former functionality of
matching the pair only if the cursor was on one of the brace characters
directly.
* Rename surround methods to reflect that they work on pairs
The current naming suggests that they may work generally on any
textobject, whereas their implementation really focuses on pairs.
* Change description of m textobject to match actual functionality
The current implementation of `m` no longer merely looks at the pair
character the cursor is on, but actually will search for the pair
(defined in helix-core/src/surround.rs) that encloses the cursor, and
not the entire selection.
* Accept suggested wording change
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* Prefix pair surround for consistency
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
Ported over from 61365dfbf3 in the `gui` branch. This will allow
adding our own events, most notably an idle timer event (useful
for adding debounced input in [dynamic pickers][1] used by interactive
global search and workspace symbols).
[1]: https://github.com/helix-editor/helix/pull/3110
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
These are read-line-like bindings which we'd like to minimize in
insert mode in general.
In particular these two are troublesome if you have a low
`editor.idle-timeout` config and are using LSP completions: the
behavior of C-n/C-p switches from moving down/up lines to moving
down/up the completion menu, so if you hit C-n too quickly
expecting to be in the completion menu, you'll end up moving down
a line instead. Using C-p moves you back up the line but doesn't
re-trigger the completion menu. This kind of timing related change
to behavior isn't realistically that big of a deal but it can be
annoying.
* Fix tab highlight when tab is partially visible
* Make it style based, and not truncation based
Dealing with truncating is a mess, especially when it comes to wide
unicode graphemes. This way it should work no matter what.
* Inline style calculation into branches
* Fix incorrect indent guide styling
Before the indent guides on top of whitespace inherited the theme
from them. Now they do not.
* Fix dark_plus theme indent_guides
* Use whitespace style as fallback for indent-guide
* Fix dark_plus theme indent_guides
* Move indent_guide style patching out of loop
* Avoid setting stdin handle when not necessary
Avoid setting the stdin handle in `shell_impl` when the input argument
is None.
This permits to run commands with no stdin with :sh
* refactoring to avoid code duplication
* making clippy happy
* Process variable name fix
Indent style may change when choosing a language with `:set-language`.
Line-endings most likely will not change, but `:set-language` should
have a similar effect as reloading a file (`:reload`), plus the two
are currently grouped in the implementation and line-ending detection
is not particularly expensive.
* Change default formatter for any language
* Fix clippy error
* Close stdin for Stdio formatters
* Better indentation and pattern matching
* Return Result<Option<...>> for fn format instead of Option
* Remove unwrap for stdin
* Handle FormatterErrors instead of Result<Option<...>>
* Use Transaction instead of LspFormatting
* Use Transaction directly in Document::format
* Perform stdin type formatting asynchronously
* Rename formatter.type values to kebab-case
* Debug format for displaying io::ErrorKind (msrv fix)
* Solve conflict?
* Use only stdio type formatters
* Remove FormatterType enum
* Remove old comment
* Check if the formatter exited correctly
* Add formatter configuration to the book
* Avoid allocations when writing to stdin and formatting errors
* Remove unused import
Co-authored-by: Gokul Soumya <gokulps15@gmail.com>
The language server sends a char offset range within the
signature help label text to highlight as the current parameter,
but helix uses byte offset ranges for rendering highlights. This
was brought up in the [review of the original signature help PR][1],
but the ranges were being highlighted correctly, and there were no
out of bound or indexing panics. Turns out rust-analyzer was
[incorrectly sending byte offsets] instead of char offsets and this
made it seem like all was well and good with offsets in helix during
initial testing.
[1]: https://github.com/helix-editor/helix/pull/1755#discussion_r906715371
[2]: https://github.com/rust-lang/rust-analyzer/pull/12272
* auto pair-removal
Fixes https://github.com/helix-editor/helix/issues/1673
* autopairs removal: use doc autopairs
* autopairs-removal: limit to one-char selections
* use single_grapheme() to check if range is one char
* fix errouneous deletes of " and other symmetric autopairs when at buffer start
Co-authored-by: Houkime <>