Previously a count or register selection would be lost while opening
the command palette. This change allows using a register selection or
count in any command chosen from the command palette.
* Add command for merging non-consecutive ranges
* Add `merge_selections` command to book
* Simplify `merge_ranges`
Heeded the advice of @the-mikedavis to stop iterating over all ranges and simply merge the first and the last range, as the invariants of `Selection` guarantee that the list of ranges is always sorted and never empty.
* Clarify doc comment of `merge_ranges`
* Add `helix_lsp::client::Client::supports_feature(&self, LanguageServerFeature)`
* Extend `doc.language_servers_with_feature` to use this method as filter as well
* Add macro `language_server_with_feature!` to reduce boilerplate for non-mergeable language server requests (like goto-definition)
* Refactored most of the `find_map` code to use the either the macro or filter directly via `doc.language_servers_with_feature`
Language Servers are now configured in a separate table in `languages.toml`:
```toml
[langauge-server.mylang-lsp]
command = "mylang-lsp"
args = ["--stdio"]
config = { provideFormatter = true }
[language-server.efm-lsp-prettier]
command = "efm-langserver"
[language-server.efm-lsp-prettier.config]
documentFormatting = true
languages = { typescript = [ { formatCommand ="prettier --stdin-filepath ${INPUT}", formatStdin = true } ] }
```
The language server for a language is configured like this (`typescript-language-server` is configured by default):
```toml
[[language]]
name = "typescript"
language-servers = [ { name = "efm-lsp-prettier", only-features = [ "format" ] }, "typescript-language-server" ]
```
or equivalent:
```toml
[[language]]
name = "typescript"
language-servers = [ { name = "typescript-language-server", except-features = [ "format" ] }, "efm-lsp-prettier" ]
```
Each requested LSP feature is priorized in the order of the `language-servers` array.
For example the first `goto-definition` supported language server (in this case `typescript-language-server`) will be taken for the relevant LSP request (command `goto_definition`).
If no `except-features` or `only-features` is given all features for the language server are enabled, as long as the language server supports these. If it doesn't the next language server which supports the feature is tried.
The list of supported features are:
- `format`
- `goto-definition`
- `goto-declaration`
- `goto-type-definition`
- `goto-reference`
- `goto-implementation`
- `signature-help`
- `hover`
- `document-highlight`
- `completion`
- `code-action`
- `workspace-command`
- `document-symbols`
- `workspace-symbols`
- `diagnostics`
- `rename-symbol`
- `inlay-hints`
Another side-effect/difference that comes with this PR, is that only one language server instance is started if different languages use the same language server.
Currently, when forward deleting (`delete_char_forward` bound to `del`,
`delete_word_forward`, `kill_to_line_end`) the cursor is moved to the
left in append mode (or generally when the cursor is at the end of the
selection). For example in a document `|abc|def` (|indicates selection)
if enter append mode the cursor is moved to `c` and the selection
becomes: `|abcd|ef`. When deleting forward (`del`) `d` is deleted. The
expectation would be that the selection doesn't shrink so that `del`
again deletes `e` and then `f`. This would look as follows:
`|abcd|ef`
`|abce|f`
`|abcf|`
`|abc |`
This is inline with how other editors like kakoune work.
However, helix currently moves the selection backwards leading to the
following behavior:
`|abcd|ef`
`|abc|ef`
`|ab|ef`
`ef`
This means that `delete_char_forward` essentially acts like
`delete_char_backward` after deleting the first character in append
mode.
To fix the problem the cursor must be moved to the right while deleting
forward (first fix in this commit). Furthermore, when the EOF char is
reached a newline char must be inserted (just like when entering
appendmode) to prevent the cursor from moving to the right
Some deletion operations (especially those that use indentation)
can generate overlapping deletion ranges when using multiple cursors.
To fix that problem a new `Transaction::delete` and
`Transaction:delete_by_selection` function were added. These functions
merge overlapping deletion ranges instead of generating an invalid
transaction. This merging of changes is only possible for deletions
and not for other changes and therefore require its own function.
The function has been used in all commands that currently delete
text by using `Transaction::change_by_selection`.
When re requesting a completion that already has a selected item we
reuse that selections savepoint. However, the selection has likely
changed since that savepoint which requires us to use the selection
from that savepoint
While scrolling (with the `scroll`) command scrolloff was calculated
slightly differently than in `ensure_cursor_in_view` which could cause
the cursor to get stuck while scrolling
Virtual text lines (either caused by softwrapped inlay hints that take
multiple or line annotations) currently block scrolling downwards.
if the visual offset passed to char_idx_at_visual_offset or
visual_offset_from_block is within a virtual text line then the char
position before the virtual text and a visual offset are returned.
We previously ignored that visual offset and as a result the cursor
would be stuck at the start of the virtual text. This commit fixes
that by simply moving the cursor to the next char (so past the virtual
text) if this visual offset is non-zero
* misc: missing inline, outdated link
* doc: Add new theme keys and config option to book
* fix: don't panic in Tree::try_get(view_id)
Necessary for later, where we could be receiving an LSP response
for a closed window, in which case we don't want to crash while
checking for its existence
* fix: reset idle timer on all mouse events
* refacto: Introduce Overlay::new and InlineAnnotation::new
* refacto: extract make_job_callback from Context::callback
* feat: add LSP display_inlay_hint option to config
* feat: communicate inlay hints support capabilities of helix to LSP server
* feat: Add function to request range of inlay hint from LSP
* feat: Save inlay hints in document, per view
* feat: Update inlay hints on document changes
* feat: Compute inlay hints on idle timeout
* nit: Add todo's about inlay hints for later
* fix: compute text annotations for current view in view.rs, not document.rs
* doc: Improve Document::text_annotations() description
* nit: getters don't use 'get_' in front
* fix: Drop inlay hints annotations on config refresh if necessary
* fix: padding theming for LSP inlay hints
* fix: tracking of outdated inlay hints should not be dependant on document revision (because of undos and such)
* fix: follow LSP spec and don't highlight padding as virtual text
* config: add some LSP inlay hint configs
* Fix#6092
Cause were some incorrect assumptions that missed an edge case in the
`Selection.contains()` calculation. Tests were added accordingly.
* Fix Selection.contains() edge-case handling.
Removing the len check short-circuit was the only thing needed as
pointed out by @dead10ck.
Repeating completions currently crates a savepoint when a completion
popup was triggered (so after the request completed). Just like for
normal completions the savepoint must be created at the request.
The occurrence of the completion request was previously not saved in
`last_insert`. To that end a new `InsertEvent::RequestCompletion`
variant has been added. When replayed this event creates a snapshot
that is "actived" by the `TriggerCompletion` event and subsequently
used during any `InsertEvent::CompletiuonApply` events.
Completion requests are computed asynchronously to avoid common micro
freezes while editing. This means that once a completion request
completes, the state of the editor might have changed. Currently,
there is a check to ensure we are still in insert mode. However,
we also need to ensure that the view and document hasn't changed
to avoid accidentally using a savepoint with the wrong view/document.
Furthermore, the editor might request a new completion while the
previous completion request hasn't complemented yet. This can
lead to weird flickering or an outdated completion request replacing
a newer completion that has already completed (the LSP server
is not required to process completion requests in order). This change
also needed to ensure determinism/linear ordering so that completion
popup always correspond to the last completion request.
Fixing autocomplete required moving the document savepoint before the
asynchronous completion request. However, this in turn causes new bugs:
If the completion popup is open, the savepoint is restored when the
popup closes (or another entry is selected). However, at that point
a new completion request might already have been created which
would have replaced the new savepoint (therefore leading to incorrectly
applied complies).
This commit fixes that bug by allowing in arbitrary number of
savepoints to be tracked on the document. The savepoints are reference
counted and therefore remain valid as long as any reference to them
remains. Weak reference are stored on the document and any reference
that can not be upgraded anymore (hence no strong reference remain)
are automatically discarded.
Currently, the selection is not saved/restored when completion
checkpoints are applied. This is usually fine because undoing changes
usually restores maps selections back in insert mode. But this is not
always the case and especially problematic in the presence of
multi-cursor completions (since completions are applied relative to
the selection/cursor) and snippets (which can change the selection)
* Do not add intermediate lines to jumplist with :<linenum> command.
* Revert jumplist index changes.
* Reduce calculations during update cycle.
* Use jumplist for undo, set jumplist before preview.
* remove some debug logging
* Revert "remove some debug logging"
This reverts commit 5772c4327e.
* Revert "Use jumplist for undo, set jumplist before preview."
This reverts commit f73a1b2982.
* Add last_selection, update implementation.
* @pascalkuthe initial feedback
* Ensure ":goto 123" keybinding works as expected.
* fix clippies, prefer expect() for expect last_selection state
Add a restart debug session command, which would issue a
[Restart Request][1], if the debugger supports it and a session is
running. It uses the same arguments and requests used to start the
initial session, when recreating it.
It builds upon #5532, making use of the changes to the termination
workflow of a session.
[1]: https://microsoft.github.io/debug-adapter-protocol/specification#Requests_RestartCloses: #5594
Signed-off-by: Filip Dutescu <filip.dutescu@gmail.com>
* Make `m` textobject look for pairs enclosing selections
Right now, this textobject only looks for pairs that surround the
cursor. This ensures that the pair found encloses each selection, which
is likely to be intuitively what is expected of this textobject.
* Simplification of match code
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* Adjust logic for ensuring surround range encloses selection
Prior, it was missing the case where the start of the selection came
before the opening brace. We also had an off-by-one error where if the
end of the selection was on the closing brace it would not work.
* Refactor to search for the open pair specifically to avoid edge cases
* Adjust wording of autoinfo to reflect new functionality
* Implement tests for surround functionality in new integration style
* Fix handling of skip values
* Fix out of bounds error
* Add `ma` version of tests
* Fix formatting of tests
* Reduce indentation levels for readability, and update comments
* Preserve each selection's direction with enclosing pair surround
* Add test case for multiple cursors resulting in overlap
* Mark known failures as TODO
* Make tests multi-threaded or they fail
* Cargo fmt
* Fix typos in integration test comments
---------
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
Example:
```
test
testitem
```
Select line 2 with x, then type Alt-C; Helix will go into an infinite
loop. The saturating_sub keeps the head_row and anchor_row pinned at 0,
and a selection is never made since the first line is too short.
This matches the behavior from 42ad1a9e04
but for the first and last change. The selection rules are the same
as for goto_next/prev_change: additions and modifications select the
added and modified range while deletions are represented with a point.
* hide duplicate symlinks from the picker
* Apply suggestions from code review
Co-authored-by: g-re-g <123515925+g-re-g@users.noreply.github.com>
* minor stylistic fix
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
---------
Co-authored-by: g-re-g <123515925+g-re-g@users.noreply.github.com>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
This change makes `ms<ret>` work similarly to `t<ret>` and related
find commands: when the next event is a keypress of Enter, surround
the selection with the document's line-endings.
* rework positioning/rendering, enables softwrap/virtual text
This commit is a large rework of the core text positioning and
rendering code in helix to remove the assumption that on-screen
columns/lines correspond to text columns/lines.
A generic `DocFormatter` is introduced that positions graphemes on
and is used both for rendering and for movements/scrolling.
Both virtual text support (inline, grapheme overlay and multi-line)
and a capable softwrap implementation is included.
fix picker highlight
cleanup doc formatter, use word bondaries for wrapping
make visual vertical movement a seperate commnad
estimate line gutter width to improve performance
cache cursor position
cleanup and optimize doc formatter
cleanup documentation
fix typos
Co-authored-by: Daniel Hines <d4hines@gmail.com>
update documentation
fix panic in last_visual_line funciton
improve soft-wrap documentation
add extend_visual_line_up/down commands
fix non-visual vertical movement
streamline virtual text highlighting, add softwrap indicator
fix cursor position if softwrap is disabled
improve documentation of text_annotations module
avoid crashes if view anchor is out of bounds
fix: consider horizontal offset when traslation char_idx -> vpos
improve default configuration
fix: mixed up horizontal and vertical offset
reset view position after config reload
apply suggestions from review
disabled softwrap for very small screens to avoid endless spin
fix wrap_indicator setting
fix bar cursor disappearring on the EOF character
add keybinding for linewise vertical movement
fix: inconsistent gutter highlights
improve virtual text API
make scope idx lookup more ergonomic
allow overlapping overlays
correctly track char_pos for virtual text
adjust configuration
deprecate old position fucntions
fix infinite loop in highlight lookup
fix gutter style
fix formatting
document max-line-width interaction with softwrap
change wrap-indicator example to use empty string
fix: rare panic when view is in invalid state (bis)
* Apply suggestions from code review
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* improve documentation for positoning functions
* simplify tests
* fix documentation of Grapheme::width
* Apply suggestions from code review
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* add explicit drop invocation
* Add explicit MoveFn type alias
* add docuntation to Editor::cursor_cache
* fix a few typos
* explain use of allow(deprecated)
* make gj and gk extend in select mode
* remove unneded debug and TODO
* mark tab_width_at #[inline]
* add fast-path to move_vertically_visual in case softwrap is disabled
* rename first_line to first_visual_line
* simplify duplicate if/else
---------
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
increment/decrement (C-a/C-x) had some buggy behavior where selections
could be offset incorrectly or the editor could panic with some edits
that changed the number of characters in a number or date. These stemmed
from the automatic jumping behavior which attempted to find the next
date or integer to increment. The jumping behavior also complicated the
code quite a bit and made the behavior somewhat difficult to predict
when using many cursors.
This change removes the automatic jumping behavior and only increments
or decrements when the full text in a range of a selection is a number
or date. This simplifies the code and fixes the panics and buggy
behaviors from changing the number of characters.
This roughly matches the behavior of the diagnostic picker: when
jumping to a diagnostic with `[d`/`]d`/`[D`/`]D`, the range of the
diagnostic is selected instead of the start point.
* Reset mode when changing buffers
This is similar to the change in
e4c9d4082a139aac3aea4506918171b96e81f5b9: reset the editor to normal
mode when changing buffers. Usually the editor is already in normal
mode but it's possible to setup insert-mode keybindings that change
buffers.
* Move normal mode entering code to Editor
This should be called internally in the Editor when changing documents
(Editor::switch) or changing focuses (Editor::focus).
* add command and keybding to jump to next/prev hunk
* add textobject for change
* Update helix-vcs/src/diff.rs
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* select entire hunk instead of first char
* fix selection range
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* Add `View::ensure_cursor_in_view_center` to adjust view after searching and jumping
Also `offset_coodrs_to_in_view` was refactored to reduce duplicated position calculations.
* Fix a wrong offset calculation in `offset_coords_to_in_view_center`
It ignored `scrolloff` if `centering` is false.
* Change default TS object bindings
Changes 'match inside/around' bindings for:
- type definition from `c` to `t`
- comments from `o` to `c`
- tests from `t` to `T`
Also changes those for the `]` / `[` bindings.
* Update docs for changed keybinds
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
This matches the insert-mode behavior for Vim and Kakoune: if the
current line is empty except for whitespace, `<ret>` should insert a
line ending at the beginning of the line, moving any indentation to the
next line.
Language Servers may signal that they do not support a method in
the initialization result (server capabilities). We can check these
when making LSP requests and hint in the status line when a method
is not supported by the server. This can also prevent crashes in
servers which assume that clients do not send requests for methods
which are disabled in the server capabilities.
There is an existing pattern the LSP client module where a method
returns `Option<impl Future<Output = Result<_>>>` with `None` signaling
no support in the server. This change extends this pattern to the rest
of the client functions. And we log an error to the statusline for
manually triggered LSP calls which return `None`.
Previously, jumplists could grow unchecked. Every transaction is
applied to jumplist selections to ensure that they are up to date
and within document bounds, so this would cause every edit to become
more expensive as jumplist lengths increased throughout a session.
Setting a maximum number of entries limits the cost.
Vim and Neovim limit their jumplists:
* b298fe6cba/src/structs.h (L141)
* e8cc489acc/src/nvim/mark_defs.h (L57)
Notably, Kakoune does not. In Kakoune, changes are applied to jumplist
entries lazily as you hit `<C-o>`/`<C-i>` though, so Kakoune doesn't
have the same growing cost concerns. Kakoune also does not have a
concept of a View which limits the cost further.
Vim and Neovim limit to 100. This seems unreasonably high to me so I've
set this to 30 to start. We can increase if this is problematically
low.
d6323b7cbc changed the behavior of paste
to select the newly inserted text. This is preferrable in normal mode
because it's useful to be able to act on the new text. This behavior
is worse for insert or select mode though:
* In insert mode, the cursor ends up on the last character of the newly
selected text, so further typing inserts text before the last
character.
* In select mode, the current selection is replaced with the new text
selection which doesn't extend the current selection. With this
change, the selection is extended to include the new text.
This aligns the behavior more closely with Kakoune, but it's
coincidental instead of intentional: Kakoune doesn't implement
bracketed paste (AFAIK) which causes this behavior in insert mode,
and Kakoune doesn't have a select mode.
Previously, commands such as `r<tab>` (replace with tab) or `t<tab>`
(select till tab) had no effect. This is because `KeyCode::Tab` needs
special treatment (like `KeyCode::Enter`).
PR #4134 switched the autocomplete menu from alphabetical to fuzzy
sorting. This commit removes the still existing filtering by prefix and
should enable full fuzzy sorting of the autocomplete menu.
closes#3084, #1807
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
The text within the command palette used a custom format to display
the keybinding for a command. This change switches to the key sequence
format that we use for pending keys and macros.
d6323b7cbc introduced a regression for
shell commands like `|`, `!`, and `<A-!>` which caused the new
selections to be incorrect. This caused a panic when piping (`|`)
would cause the new range to extend past the document end.
The paste version of this bug was fixed in
48a3965ab4.
This change also inherits the direction of the new range from the old
range and adds integration tests to ensure that the behavior isn't
broken in the future.
* dynamically resize line number gutter width
* removing digits lower-bound, permitting spacer
* removing max line num char limit; adding notes; qualified successors; notes
* updating tests to use new line number width when testing views
* linenr width based on document line count
* using min width of 2 so line numbers relative is useful
* lint rolling; removing unnecessary type parameter lifetime
* merge change resolution
* reformat code
* rename row_styler to style; add int_log resource
* adding spacer to gutters default; updating book config entry
* adding view.inner_height(), swap for loop for iterator
* reverting change of current! to view! now that doc is not needed
* Fix range offsets in multi-selection paste
d6323b7cbc introduced a regression with
multi-selection paste where pasting would not adjust the ranges
correctly. To fix it, we need to track the total number of characters
inserted in each changed selection and use that offset to slide each
new range forwards.
* Inherit selection directions on paste
* Add an integration-test for multi-selection pasting
The sequence "_y"_p panics because the blackhole register contains an
empty values vec. This causes a panic when pasting since it unwraps
a `slice::last`.
This follows changes in Kakoune to the same effects:
* p/<space>p: 266d1c37d0
* !/<A-!>: 85b78dda2e
Selecting the new data inserted by shell or pasting is often more
useful than retaining a selection of the pre-paste/insert content.
When backward-deleting a character, if this character and the following
character form a Pair, we want to delete both. However, there is a bug
that deletes both characters also if both characters are closers of some
Pair.
This commit fixes that by adding an additional check that the deleted
character should be an opener in a Pair.
Closes https://github.com/helix-editor/helix/issues/4544.
This bug occurs on `shell_insert_output` and `shell_append_output`
commands.
The previous implementation would create a child process using the Rust
stdlib's `Command` builder. However, when nothing should be piped in
from the editor, the default value for `stdin` would be used. According
to the Rust stdlib documentation that is `Stdio::inherit` which will
make the child process inherit the parent process' stdin. This would
cause the terminal to freeze.
This change will set the child process' stdin to `Stdio::null` whenever
it doesn't pipe it. In the `if` statement where this change was made
there was an extra condition for windows that I am not sure if would
require some special treatment.
This complicates the code a little but it often divides by two the number of allocations done by
the functions. LSP labels especially can easily be called dozens of time in a single menu popup,
when listing references for example.
When we do auto formatting, the code that takes the LSP's response and applies
the changes to the document are just getting the currently focused view and
giving that to the function, basically always assuming that the document that
we're applying the change to is in focus, and not in a background view.
This is usually fine for a single view, even if it's a buffer in the
background, because it's still the same view and the selection will get updated
accordingly for when you switch back to it. But it's obviously a problem for
when there are multiple views, because if you don't have the target document in
focus, it will ask the document to update the wrong view, hence the crash.
The problem with this is picking which view to apply any selection change to.
In the absence of any more data points on the views themselves, we simply pick
the first view associated with the document we are saving.
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.
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.
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.
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`).
* 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
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`.
* 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