Commit Graph

661 Commits (ef5bcd5060e6298a2deae53a7c29514d4a50a2fc)

Author SHA1 Message Date
vwkd 352d1574a6
add move_prev_long_word_end and extend_prev_long_word_end (#6905) 1 year ago
Ilya Sovtsov 77e9a22aff
Add check for a non-zero value for tab width (#7178) 1 year ago
Alex Vinyals a2b8cfdb8c
feat(core): add plaintext matching fallback to tree-sitter matching (#4288) 1 year ago
Ikko Eltociear Ashimine 349e2b5f0e
Fix typo in syntax.rs (#7114)
seperator -> separator
1 year ago
Szabin 70e4cdbd8e
Add command to merge non-consecutive ranges (#7053)
* 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`
1 year ago
Philipp Mildenberger 2eeac10755 Refactor doc language servers to a HashMap, and the config to use a Vec to retain order 2 years ago
Philipp Mildenberger ec2f9091a0 Simplify Display implementation for LanguageServerFeature 2 years ago
Philipp Mildenberger 76b5cab524 Refactored doc.language_servers and doc.language_servers_with_feature to return an iterator and refactor LanguageServerFeature handling to a HashMap (language server name maps to features)
Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>
2 years ago
Philipp Mildenberger 19f88fc577 Simplify Display implementation of LanguageServerFeature
Co-authored-by: Ivan Tham <pickfire@riseup.net>
2 years ago
Philipp Mildenberger 71551d395b Adds support for multiple language servers per language.
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.
2 years ago
Pascal Kuthe b0705337be automatically disable TS when parsing takes longer than 500ms 2 years ago
Pascal Kuthe 25d4ebe30d don't move cursor while forward deleting in append mode
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
2 years ago
Pascal Kuthe f8225ed921 fix panic when deleting overlapping ranges
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`.
2 years ago
ZJPzjp 3b8c15618f
Fix warnings from clippy (#7013)
* Fix warnings from clippy

* revert MAIN_SEPARATOR_STR
2 years ago
Timothy DeHerrera 9c6c63a2be
inject language based on file extension & shebang (#3970)
* inject language based on file extension

Nodes can now be captured with "injection.filename". If this capture
contains a valid file extension known to Helix, then the content will
be highlighted as that language.

* inject language by shebang

Nodes can now be captured with "injection.shebang". If this capture
contains a valid shebang line known to Helix, then the content will
be highlighted as the language the shebang calls for.

* add documentation for language injection

* nix: fix highlights

The `@` is now highlighted properly on either side of the function arg.

Also, extending the phases with `buildPhase = prev.buildPhase + ''''`
is now highlighted properly.

Fix highlighting of `''$` style escapes (requires tree-sitter-nix bump)

Fix `inherit` highlighting.

* simplify injection_for_match

Split out injection pair logic into its own method to make the overall
flow easier to follow.

Also transform the top-level function into a method on a
HighlightConfiguration.

* markdown: add shebang injection query
2 years ago
jorge 204d1eba4b
feat(commands): add clear-register typable command (#5695)
Co-authored-by: Jorge <chorcheus@tutanota.com>
2 years ago
ymgyt 61ff2bc094
return early if soft wrap is invalid (#6856) 2 years ago
Blaž Hrastnik 58e457a4e1
Revert "Fix #6605: Remove soft-wrap.enable option wrapping. (#6656)"
This caused a bug that would ignore the global config.

This reverts commit af88a3c15c.
2 years ago
gibbz00 af88a3c15c
Fix #6605: Remove soft-wrap.enable option wrapping. (#6656)
Co-authored-by: gibbz00 <gabrielhansson@gmail.com>
2 years ago
Daniel Sedlak e856906f76
Fix typos (#6643) 2 years ago
jazzfool d04288e0f3
Canonicalize paths before stripping current dir as prefix (#6290)
Co-authored-by: jazzfool <shamoslover69@gmail.com>
2 years ago
Pascal Kuthe 2d10a429eb add workspace config and manual LSP root management
fixup documentation

Co-authored-by: LeoniePhiline <22329650+LeoniePhiline@users.noreply.github.com>

fixup typo

Co-authored-by: LeoniePhiline <22329650+LeoniePhiline@users.noreply.github.com>
2 years ago
Philipp Mildenberger 198ff2c3f9
Fix clippy lints (#6454) 2 years ago
Pascal Kuthe 2af14a24ab respect line annotations in char_idx_at_visual_row_offset
char_idx_at_visual_row_offset asssumed that a single line/block break
always corresponded to a vertical offset of 1. However conceal can hide
the line break (in which case the certical offset would be 0) and line
annotations (or softwrapped inlay hints at the end of the line) can insert
addtional vertical lines.

To correctly account for these cases we simply compute the visual offset
of the start of the next block from the previous block instead of the
visual offset of the block end. This means that the line breaks at the
end of the block (however many there may be) are automatically included
and we don't need to manually add 1 to the `row_offset` anymore.
2 years ago
Pascal Kuthe 7cf448eb5b use partition_point instead of binary_search_by
Using `partition_point` ensures we always find the first entry.
With binary search it is "random" (deterministic but implementation
specific) which index is retruned if there are multiple equal elements.
`partition_point` was added to the standard library to cover extactly
the usecase here.
2 years ago
Pascal Kuthe 72b9311678 fix view anchors not at start of a visual line
The top of a view is marked by a char idx anchor. That char idx is
usually the first character of the visual line it's on. We use a char
index instead of a line index because the view may start in the middle
of a line with soft wrapping. However, it's possible to temporarily
endup in a state where this anchor is not the first character of the
first visual line. This is pretty rare because edits usually happen
inside/after the view. In most cases we handle this case correctly.

However, if the cursor is before the anchor (but still in view)
there can be crashes or visual artifacts. This is caused by the fact
that visual_offset_from_anchor (and the positioning code in view.rs)
incorrectly assumed that the (cursor) position is always after the
view anchor if the cursor is in view. But if the anchor is not the
first character of the first visual line this is not the case anymore.

In that case crashes and visual artifacts are possible. This commit
fixes that problem by changing `visual_offset_from_anchor` (and
callsites) to properly consider that case.
2 years ago
dependabot[bot] 52ced7e85f
build(deps): bump bitflags from 1.3.2 to 2.0.2 (#6387)
* build(deps): bump bitflags from 1.3.2 to 2.0.2

Bumps [bitflags](https://github.com/bitflags/bitflags) from 1.3.2 to 2.0.2.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/1.3.2...2.0.2)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* deps: Resolve bitflags 2.0 breaking changes

Bitflags 2.0 release made some breaking changes requiring some small
changes to the Helix codebase.

Almost all of the necessary changes are to manually `#[derive(..)]`
trait implementations which are no longer automatically derived for
all bitflags. All of these were previously automatically derived:

    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy]

I have derived the minimum traits for each bitflag type.

The other change was to the `.bits` field. This is now a `.bits()`
method so the usage of this has been updated in the `Borders` type.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
2 years ago
Skyler Hawthorne d3b051d28e fix test::plain
test::plain uses char indices when it should use byte indices
2 years ago
Skyler Hawthorne 58ea193054 Allow explicit newlines in test DSL
The current test DSL currently has no way to express being at the end of
a line, save for putting an explicit LF or CRLF inside the `#[|]#`. The
problem with this approach is that it can add unintended extra new lines
if used in conjunction with raw strings, which insert newlines for you.

This is a simple attempt to mitigate this problem. If there is an
explicit newline character at the end of the selection, and then it
is immediately followed by the same newline character at the right end
of the selection, this following newline is removed. This way, one can
express a cursor at the end of a line explicitly.
2 years ago
Stanisław Borowy 39052433ed
Fix indentation level calculation for lines mixing tabs and spaces (#6278) 2 years ago
Poliorcetics bdcd4d9411
Feat: LSP Type Hints (#5934)
* 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
2 years ago
Pascal Kuthe 2b64a64d7e Add API to create a Transaction from potentially overlapping changes
This commit adds new functions to `Transaction` that allow creating
edits that might potentially overlap. Any change that overlaps
previous changes is ignored. Furthermore, a utility method is added
that also drops selections associated with dropped changes (for
transactions that are created from a selection).

This is needed to avoid crashes when applying multicursor
autocompletions, as the edit from a previous cursor may overlap
with the next cursor/edit.
2 years ago
Pascal Kuthe e91289fda1 Add IntoIterator implementation for Selection 2 years ago
gibbz00 2cf4ce2356
Fix `shrink_selection` with multiple cursors. (#6093)
* 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.
2 years ago
Kyle Smith 3849ca4c2a
Add test cases for existing pair matching logic. (#6027)
* Add test cases for existing pair matching logic.

* fix clippy
2 years ago
Clément Delafargue 8dd1ab4899
Softwrapping improvements (#5893)
* use max_line_width + 1 during softwrap to account for newline char

Helix softwrap implementation always wraps lines so that the newline
character doesn't get cut off so he line wraps one chars earlier then
in other editors. This is necessary, because newline chars are always
selecatble in helix and must never be hidden.

However That means that `max_line_width` currently wraps one char
earlier than expected. The typical definition of line width does not
include the newline character and other helix commands like `:reflow`
also don't count the newline character here.

This commit makes softwrap use `max_line_width + 1` instead of
`max_line_width` to correct the impedance missmatch.

* fix typos

Co-authored-by: Jonathan Lebon <jonathan@jlebon.com>

* Add text-width to config.toml

* text-width: update setting documentation

* rename leftover config item

* remove leftover max-line-length occurrences

* Make `text-width` optional in editor config

When it was only used for `:reflow` it made sense to have a default
value set to `80`, but now that soft-wrapping uses this setting, keeping
a default set to `80` would make soft-wrapping behave more aggressively.

* Allow softwrapping to ignore `text-width`

Softwrapping wraps by default to the viewport width or a configured
`text-width` (whichever's smaller). In some cases we only want to set
`text-width` to use for hard-wrapping and let longer lines flow if they
have enough space. This setting allows that.

* Revert "Make `text-width` optional in editor config"

This reverts commit b247d526d69adf41434b6fd9c4983369c785aa22.

* soft-wrap: allow per-language overrides

* Update book/src/configuration.md

Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>

* Update book/src/languages.md

Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>

* Update book/src/configuration.md

Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>

---------

Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>
Co-authored-by: Jonathan Lebon <jonathan@jlebon.com>
Co-authored-by: Alex Boehm <alexb@ozrunways.com>
Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
2 years ago
Urgau ec6e575a40 Correctly handle multiple cursors with LSP snippets 2 years ago
Alexandr 8043959265
Doc string fix in selection.rs (#6077)
* Doc string fix

Delete duplicate `the`

* selection.rs doc string wording

* Remove extra whitespace at end of doc text

---------

Co-authored-by: Ivan Tham <pickfire@riseup.net>
2 years ago
Triton171 a1a6d5f334
Replace incorrect usages of tab_width with indent_width. (#5918) 2 years ago
Mike Trinkala ef221abe83
Prevent a panic when uncommenting a line with only a comment token (#5933)
Open a new document `test.rs` and type the following:
`di//<esc><C-c>`

The margin calculation pushes the range out of bounds for the comment
marker when there are no characters (newline) after it.

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
Char range out of bounds: char range 0..3,
Rope/RopeSlice char length 2', ropey-1.6.0/src/rope.rs:546:37

The debug build catches the error in the transaction: thread 'main'
panicked at 'attempt to subtract with overflow',
helix-core/src/transaction.rs:503:26
2 years ago
Daniel S Poulin 6929a12f29
Make `m` textobject look for pairs enclosing selections (#3344)
* 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>
2 years ago
Pascal Kuthe 8a3ec443f1
Fix new clippy lints (#5892) 2 years ago
Pascal Kuthe 7ebcf4e919
properly handle LSP position encoding (#5711)
* properly handle LSP position encoding

* add debug assertion to Transaction::change

* Apply suggestions from code review

Co-authored-by: Michael Davis <mcarsondavis@gmail.com>

---------

Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
2 years ago
Mike Trinkala c704701714
Short-circuit the word and treesitter object movement commands (#5851)
The loop always iterates the number of times the user specified even
if the beginning/end of the document is reached.

For an extreme demonstration try the following commands, Helix will
hang for several seconds.
100000000w
100000000]c
2 years ago
Mike Trinkala 62d046fa21
Fix utf8 length handling for shellwords (#5738)
If the last argument to shellwords ends in a multibyte utf8 character
the entire argument will be dropped.
e.g. `:sh echo test1 test2𒀀` will only output `test1`

Add additional tests based on the code review feedback
2 years ago
Michael Davis d5f17d3f69
Fix initial highlight layer sort order (#5196)
The purpose of this change is to remove the mutable self borrow on
`HighlightIterLayer::sort_key` so that we can sort layers with the
correct ordering using the `Vec::sort` function family.
`HighlightIterLayer::sort_key` needs `&mut self` since it calls
`Peekable::peek` which needs `&mut self`. `Vec::sort` functions
only give immutable borrows of the elements to ensure the
correctness of the sort.

We could instead approach this by creating an eager Peekable and using
that instead of `std::iter::Peekable` to wrap `QueryCaptures`:

```rust
struct EagerPeekable<I: Iterator> {
    iter: I,
    peeked: Option<I::Item>,
}

impl<I: Iterator> EagerPeekable<I> {
    fn new(mut iter: I) -> Self {
        let peeked = iter.next();
        Self { iter, peeked }
    }

    fn peek(&self) -> Option<&I::Item> {
        self.peeked.as_ref()
    }
}

impl<I: Iterator> Iterator for EagerPeekable<I> {
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        std::mem::replace(&mut self.peeked, self.iter.next())
    }
}
```

This would be a cleaner approach (notice how `EagerPeekable::peek`
takes `&self` rather than `&mut self`), however this doesn't work in
practice because the Items emitted by the `tree_sitter::QueryCaptures`
Iterator must be consumed before the next Item is returned.
`Iterator::next` on `tree_sitter::QueryCaptures` modifies the
`QueryMatch` returned by the last call of `next`. This behavior is
not currently reflected in the lifetimes/structure of `QueryCaptures`.

This fixes an issue with layers being out of order when using combined
injections since the old code only checked the first range in the
layer. Layers being out of order could cause missing highlights for
combined-injections content.
2 years ago
Pascal Kuthe 4dcf1fe66b
rework positioning/rendering and enable softwrap/virtual text (#5420)
* 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>
2 years ago
greg-enbala 60f84be40c
Separate jump behavior from increment/decrement (#4123)
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.
2 years ago
Triton171 5f2fe5fca5
Fix erroneous indent between closers of auto-pairs (#5330)
inserting a newline between 2 closers of an auto-pair.
2 years ago
DylanBulfin 1107296ca9
Add command to merge consecutive ranges in selection (#5047) 2 years ago