* feat: make `move_vertically` aware of tabs and wide characters
* refactor: replace unnecessary checked_sub with comparison
* refactor: leave pos_at_coords unchanged and introduce separate pos_at_visual_coords
* style: include comment to explain `pos_at_visual_coords` breaking condition
* refactor: use `pos_at_visual_coords` in `text_pos_at_screen_coords`
* feat: make `copy_selection_on_line` aware of wide characters
When a new View of a Document is created, a default cursor of 0, 0 is
created, and it does not get normalized to a single width cursor until
at least one movement of the cursor happens. This appears to have no
practical negative effect that I could find, but it makes tests difficult
to work with, since the initial selection is not what you expect it to be.
This changes the initial selection of a new View to be the width of the
first grapheme in the text.
* Use new macro syntax for encoding sequences of keys
* Make convenience helpers for common test pattern
* Use indoc for inline indented raw strings
* Add feature flag for integration testing to disable rendering
* allows passing extra formatting options to LSPs
- adds optional field 'format' to [[language]] sections in 'languages.toml'
- passes specified options the LSPs via FormattingOptions
* cleaner conversion of formatting properties
* move formatting options inside lsp::Client
* cleans up formatting properties merge
* add reflow command
Users need to be able to hard-wrap text for many applications, including
comments in code, git commit messages, plaintext documentation, etc. It
often falls to the user to manually insert line breaks where appropriate
in order to hard-wrap text.
This commit introduces the "reflow" command (both in the TUI and core
library) to automatically hard-wrap selected text to a given number of
characters (defined by Unicode "extended grapheme clusters"). It handles
lines with a repeated prefix, such as comments ("//") and indentation.
* reflow: consider newlines to be word separators
* replace custom reflow impl with textwrap crate
* Sync reflow command docs with book
* reflow: add default max_line_len language setting
Co-authored-by: Vince Mutolo <vince@mutolo.org>
* log textobject query construction errors
The current behavior is that invalid queries are discarded silently
which makes it difficult to debug invalid textobjects (either invalid
syntax or an update may have come through that changed the valid set
of nodes).
* fix golang textobject query
`method_spec_list` used to be a named node but was removed (I think
for Helix, it was when updated to pull in the support for generics).
Instead of a named node for the list of method specs we have a bunch
of `method_spec` children nodes now. We can match on the set of them
with a `+` wildcard.
Example go for this query:
type Shape interface {
area() float64
perimeter() float64
}
Which is parsed as:
(source_file
(type_declaration
(type_spec
name: (type_identifier)
type: (interface_type
(method_spec
name: (field_identifier)
parameters: (parameter_list)
result: (type_identifier))
(method_spec
name: (field_identifier)
parameters: (parameter_list)
result: (type_identifier))))))
* Make textobject select last paragraph
Last paragraph shoud be selected if the cursor was placed on the
whitespace paragraph part and `map` is done, otherwise it would do
nothing useful, but now we select backwards for the last paragraph
which behaves similarly to kakoune, making `map` useful for the last
paragraph with whitespace. Example usecase is to copy and paste last
ledger cli paragraph quickly by `mapyp` to duplicate last entry.
* Fix typo in core textobject
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* Add runtime language configuration (#1794)
* Add set-language typable command to change the language of current buffer.
* Add completer for available language options.
* Update set-language to refresh language server as well
* Add language id based config lookup on `syntax::Loader`.
* Add `Document::set_language3` to set programming language based on language
id.
* Update `Editor::refresh_language_server` to try language detection only if
language is not already set.
* Remove language detection from Editor::refresh_language_server
* Move document language detection to where the scratch buffer is saved.
* Rename Document::set_language3 to Document::set_language_by_language_id.
* Remove unnecessary clone in completers::language
* WIP: Rework indentation system
* Add ComplexNode for context-aware indentation (including a proof of concept for assignment statements in rust)
* Add switch statements to Go indents.toml (fixes the second half of issue #1523)
Remove commented-out code
* Migrate all existing indentation queries.
Add more options to ComplexNode and use them to improve C/C++ indentation.
* Add comments & replace Option<Vec<_>> with Vec<_>
* Add more detailed documentation for tree-sitter indentation
* Improve code style in indent.rs
* Use tree-sitter queries for indentation instead of TOML config.
Migrate existing indent queries.
* Add documentation for the new indent queries.
Change xtask docgen to look for indents.scm instead of indents.toml
* Improve code style in indent.rs.
Fix an issue with the rust indent query.
* Move indentation test sources to separate files.
Add `#not-kind-eq?`, `#same-line?` and `#not-same-line` custom predicates.
Improve the rust and c indent queries.
* Fix indent test.
Improve rust indent queries.
* Move indentation tests to integration test folder.
* Improve code style in indent.rs.
Reuse tree-sitter cursors for indentation queries.
* Migrate HCL indent query
* Replace custom loading in indent tests with a designated languages.toml
* Update indent query file name for --health command.
* Fix single-space formatting in indent queries.
* Add explanation for unwrapping.
Co-authored-by: Triton171 <triton0171@gmail.com>
This avoids costly conversions via byte_to_char (which are then
reversed back into bytes internally in Ropey).
Reduces time spent in slice/byte_to_char from ~24% to ~5%.
This is a rather large refactor that moves most of the code for
loading, fetching, and building grammars into a new helix-loader
module. This works well with the [[grammars]] syntax for
languages.toml defined earlier: we only have to depend on the types
for GrammarConfiguration in helix-loader and can leave all the
[[language]] entries for helix-core.
The vision with 'use-grammars' is to allow the long-requested feature
of being able to declare your own set of grammars that you would like.
A simple schema with only/except grammar names controls the list
of grammars that is fetched and built. It does not (yet) control which
grammars may be loaded at runtime if they already exist.
This is not strictly speaking necessary. tree_sitter_library was used by
just one grammar: llvm-mir-yaml, which uses the yaml grammar. This will
make the language more consistent, though. Each language can explicitly
say that they use Some(grammar), defaulting when None to the grammar that
has a grammar_id matching the language's language_id.
helix-syntax mostly existed for the sake of the build task which
checks and compiles the submodules. Since we won't be relying on
that process anymore, it doesn't end up making much sense to have
a very thin crate just for some functions that we could port to
helix-core.
The remaining build-related code is moved to helix-term which will
be able to provide grammar builds through the --build-grammars CLI
flag.
Here we add syntax to the languages.toml languge
[[grammar]]
name = "<name>"
source = { .. }
Which can be used to specify a tree-sitter grammar separately of
the language that defines it, and we make this distinction for
two reasons:
* In later commits, we will separate this code from helix-core
and bring it to a new helix-loader crate. Using separate schemas
for language and grammar configurations allows for a nice divide
between the types needed to be declared in helix-loader and in
helix-core/syntax
* Two different languages may use the same grammar. This is currently
the case with llvm-mir-yaml and yaml. We could accomplish a config
that works for this with just `[[languages]]`, but it gets a bit
dicey with languages depending on one another. If you enable
llvm-mir-yaml and disable yaml, does helix still need to fetch and
build tree-sitter-yaml? It could be a matter of interpretation.
* Move runtime file location definitions to core
* Add basic --health command
* Add language specific --health
* Show summary for all langs with bare --health
* Use TsFeature from xtask for --health
* cargo fmt
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
Treesitter captures can contain multiple nodes like so:
```
(line_comment)+ @comment
```
This would match each line in a comment as a separate
`@comment` capture when what we actually want is the
whole set of contiguous `line_comment` nodes to be
captured under the `@comment` capture. This commit enables
this behaviour.
* impl auto pairs config
Implements configuration for which pairs of tokens get auto completed.
In order to help with this, the logic for when *not* to auto complete
has been generalized from a specific hardcoded list of characters to
simply testing if the next/prev char is alphanumeric.
It is possible to configure a global list of pairs as well as at the
language level. The language config will take precedence over the
global config.
* rename AutoPair -> Pair
* clean up insert_char command
* remove Rc
* remove some explicit cloning with another impl
* fix lint
* review comments
* global auto-pairs = false takes precedence over language settings
* make clippy happy
* print out editor config on startup
* move auto pairs accessor into Document
* rearrange auto pair doc comment
* use pattern in Froms
This code:
let start = ensure_grapheme_boundary_next(text, text.byte_to_char(start));
let end = ensure_grapheme_boundary_next(text, text.byte_to_char(end));
Would convert byte to char index, but then internally immediately convert back
to byte index, operate on it, then convert it to char index.
This change reduces the amount of time spent in ensure_grapheme_boundary from
29% to 2%.
* add select_next_sibling and select_prev_sibling commands
* refactor objects to use higher order functions
* address clippy feedback
* move selection cloning into commands
* add default keybindings under left/right brackets
* use [+t,]+t for selecting sibling syntax nodes
* setup Alt-{j,k,h,l} default keymaps for syntax selection commands
* reduce boilerplate of select_next/prev_sibling in commands
* import tree-sitter Node type in commands
Auto pairs were resulting in incorrect ranges in the resulting when the
line terminators are CRLF (i.e. Windows). It turns out this is because
when we were checking if the selection was a single-width cursor, it
incorrectly assumed that this would be a single char. This is not the
case, as a cursor can cover a multi-code point grapheme. Therefore,
we must instead explicitly work with and check graphemes to determine
if the cursor should move or extend the selection.
Fixes#1436
* feat(commands): shrink_selection
Add `shrink_selection` command that can be used to shrink
previously expanded selection.
To make `shrink_selection` work it was necessary to add
selection history to the Document since we want to shrink
the selection towards the syntax tree node that was initially
selected.
Selection history is cleared any time the user changes
selection other way than by `expand_selection`. This ensures
that we don't get some funky edge cases when user calls
`shrink_selection`.
Related: https://github.com/helix-editor/helix/discussions/1328
* Refactor shrink_selection, move history to view
* Remove useless comment
* Add default key mapping for extend&shrink selection
* Rework contains_selection method
* Shrink selection without expand selects first child
* Add injection regex for more languages
To support embedding them in other languages like markdown.
* Add llvm-mir highlighting
LLVM Machine IR is dumped as yaml files that can embed LLVM IR and
Machine IR.
To support this, add a llvm-mir-yaml language that uses the yaml
parser, but uses different injections to highlight IR and MIR.
* Update submodule with fixed multiline comments
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
* feat(commands): ensure_selections_forward
Add command that ensures that selections are in forward direction.
Fixes: https://github.com/helix-editor/helix/issues/1332
* Add keybinding for ensure_selections_forward
Add `A-:` keybinding for the ensure_selections_forward command.
* Re-use range.flip for flip_selections command
* feat(ui): file encoding in statusline
Display file encoding in statusline if the encoding
isn't UTF-8.
* Re-export encoding_rs from core
From there it can be imported by other mods
that rely on it.
* feat(lsp): configurable diagnostic severity
Allow severity of diagnostic messages to be configured.
E.g. allow turning of Hint level diagnostics.
Fixes: https://github.com/helix-editor/helix/issues/1007
* Use language_config() method
* Add documentation for diagnostic_severity
* Use unreachable for unknown severity level
* fix: documentation for diagnostic_severity config
* use auto pairs with selections
Previously, the auto pairs code was converting the user selection into
its cursor form, and setting the transaction's selection to that cursor.
This has the effect of destroying the user's selection if they type a
pair character that gets auto completed.
This fixes the code to work with the user's selection, inserting auto
pairs where appropriate, but either keeping or extending the user's
selection.
* use movement::Direction instead of bool
* assume 0-width cursor is forward
* Add auto pairs for same-char pairs
* Add unit tests for all existing functionality
* Add auto pairs for same-char pairs (quotes, etc). Account for
apostrophe in prose by requiring both sides of the cursor to be
non-pair chars or whitespace. This also incidentally will work for
avoiding a double single quote in lifetime annotations, at least until
<> is added
* Slight factor of moving the cursor transform of the selection to
inside the hooks. This will enable doing auto pairing with selections,
and fixing the bug where auto pairs destroy the selection.
Fixes#1014
Fixes#1077. This was caused by the assumption that a block
cursor is represented as zero width internally and simply
rendered to be a single width selection, where as in reality
a block cursor is an actual single width selection in form and
function.
Behavioural changes:
1. Surround selection no longer works when cursor is _on_ a
surround character that has matching pairs (like `'`
or `"`). This was the intended behaviour from the start
but worked till now because of the cursor position
calculation mismatch.
* Jump to end char of surrounding pair from any cursor pos
* Separate bracket matching into exact and fuzzy search
* Add constants for bracket chars
* Abort early if char under cursor is not a bracket
* Simplify bracket char validation
* Refactor node search and unify find methods
* Remove bracket constants
* Add command to inc/dec number under cursor
With the cursor over a number in normal mode, Ctrl + A will increment the
number and Ctrl + X will decrement the number. It works with binary, octal,
decimal, and hexidecimal numbers. Here are some examples.
0b01110100
0o1734
-24234
0x1F245
If the number isn't over a number it will try to find a number after the
cursor on the same line.
* Move several functions to helix-core
* Change to work based on word under selection
* It no longer finds the next number if the cursor isn't already over
a number.
* It only matches numbers that are part of words with other characters
like "foo123bar".
* It now works with multiple selections.
* Add some unit tests
* Fix for clippy
* Simplify some things
* Keep previous selection after incrementing
* Use short word instead of long word
This change requires us to manually handle minus sign.
* Don't pad decimal numbers if no leading zeros
* Handle numbers with `_` separators
* Refactor and add tests
* Move most of the code into core
* Add tests for the incremented output
* Use correct range
* Formatting
* Rename increment functions
* Make docs more specific
* This is easier to read
* This is clearer
* Type can be inferred
* Add treesitter textobject queries
Only for Go, Python and Rust for now.
* Add tree-sitter textobjects
Only has functions and class objects as of now.
* Fix tests
* Add docs for tree-sitter textobjects
* Add guide for creating new textobject queries
* Add parameter textobject
Only parameter.inside is implemented now, parameter.around
will probably require custom predicates akin to nvim' `make-range`
since we want to select a trailing comma too (a comma will be
an anonymous node and matching against them doesn't work similar
to named nodes)
* Simplify TextObject cell init
* allow language.config (in languages.toml) to be passed in as a toml object
* Change config field for languages from json string to toml object
* remove indents on languages.toml config
* fix: remove patch version from serde_json import in helix-core
* Use same tree-sitter-zig as upstream/master
* feat: merge default languages.toml with user provided languages.toml
* refactor: use catch-all to override all other values for merge toml
* tests: add a test case for merging languages configs
* refactor: change test module name
* Fix around-word text-object selection.
* Text object around-word: select to the left if no whitespace on the right.
Also only select around when there's whitespace at all.
* Make select-word-around select all white space on a side.
* Update commented-out test case.
* Fix unused import warning from rebase.
* Implement `margin` calculation for uncommenting
* Move `margin` calculation to `find_line_comment`
* Fix comment bug with multiple selections on a line
* Fix `find_line_comment` test for new return type
* Generate a single vec of lines for comment toggle
`toggle_line_comments` collects the lines covered by all selections into
a `Vec`, skipping duplicates. `find_line_comment` now returns the lines
to operate on, instead of returning the lines to skip.
* Fix test for `find_line_comment`
* Reserve length of `to_change` instead of `lines`
The length of `lines` includes blank lines which will be skipped, and as
such do not need space for a change reserved for them. `to_change`
includes only the lines which will be changed.
* Use `token.chars().count()` for token char length
* Create `changes` with capacity instead of reserving
* Remove unnecessary clones in `test_find_line_comment`
* Add test case for 0 margin comments
* Add comments explaining `find_line_comment`
* Added option to provide a custom config file to the lsp.
* Simplified lsp loading routine with anyhow
* Moved config to language.toml
* Fixed test case
* Cargo fmt
* Revert now-useless changes
* Renamed custom_config to config
Co-authored-by: Cor <prive@corpeters.nl>
For example when the cursor is _on_ the `'` in `'word'`, the cursor
wouldn't move because the search for a matching pair started _from_ the
position of the cursor and simply found itself.
* Add textobjects for word
* Add textobjects for surround characters
* Apply clippy lints
* Remove ThisWordPrevBound in favor of PrevWordEnd
It's the same as PrevWordEnd except for taking the current char
into account, so use a "flag" to capture that usecase
* Add tests for PrevWordEnd movement
* Remove ThisWord* movements
They did not preserve anchor positions and were only used
for textobject boundary search anyway so replace them with
simple position finding functions
* Rewrite tests of word textobject
* Add tests for surround textobject
* Add textobject docs
* Refactor textobject word position functions
* Apply clippy lints on textobject
* Fix overflow error with textobjects
Surround operations previously ignored other pairs that are
enclosed within which should be skipped. For example if the
cursor is on the `,` in `{{a},{b}}`, doing `md{` previously
would delete the `{` on the left of `a` and `}` on the right
of `b` instead of the outermost braces. This commit corrects
this behavior.
* Fix expansion of `~`, dont use directory relative to cwd.
* Add `expand_tilde`
* Bring back `canonicalize_path`, use `expand_tilde` to `normalize`
* Make `:open ~` completion work
* Fix clippy
* Fold home dir into tilde in Document `realitve_path`
Registers are stored inside `Editor` and accessed without `RwLock`.
To work around ownership, I added a sister method to `Editor::current`:
`Editor::current_with_context`. I tried to modify `Editor::current`
directly but it's used at a lot of places so I reverted into this for
now at least.
- Move char functions into their own module under helix_core.
- Use matches!() macro where appropriate.
- Use a static lifetime on indent_unit() now that we can.
* Disable deleting from an empty buffer which can cause a crash.
* Improve on the fix for deleting from the end of the buffer.
* Clean up leftover log.
* Avoid theoretical underflow.
* Implement :before which accepts a time interval and moves the editor to
the closest history state to the commit of the current time minus that
interval. Current time is now by default, or the commit time if :before
has just been used.
* Add :earlier an :later commands that can move through
the edit history and retrieve changes hidded by undoing
and commiting new changes. The commands accept a number
of steps or a time period relative to the currrent change.
* Fix clippy lint error.
* Remove the dependency on parse_duration, add a custom parser instead.
* Fix clippy errors.
* Make helix_core::history a public module.
* Use the helper for getting the current document and view.
* Handled some PR comments.
* Fix the logic in :later n.
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Add an alias for :earlier.
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Add an alias for later.
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Run cargo fmt.
* Add some tests for earlier and later.
* Add more tests and restore the fix for later that diappeared somehow.
* Use ? instead of a match on an option.
Co-authored-by: Ivan Tham <pickfire@riseup.net>
* Rename to UndoKind.
* Remove the leftover match.
* Handle a bunch of review comments.
* More systemd.time compliant time units and additional description for the new commands.
* A more concise rewrite of the time span parser using ideas from PR discussion.
* Replace a match with map_err().
Co-authored-by: Ivan Tham <pickfire@riseup.net>
Co-authored-by: Jakub Bartodziej <jqb@google.com>
Co-authored-by: Ivan Tham <pickfire@riseup.net>