helix-editor / helix

A post-modern modal text editor.
https://helix-editor.com
Mozilla Public License 2.0
33.79k stars 2.51k forks source link

Automatically jump to matching brackets #1974

Open major-seven opened 2 years ago

major-seven commented 2 years ago

Coming from Neovim I know the combinations d(elete)-i(nside)-(any type of bracket / quotation mark) which automatically jumps to the next occurrence of matching brackets. To select the characters between two brackets in Helix, you can type m(atch)-i(nside)-(any type of bracket / quotation mark) and then use d(elete) / c(hange) etc. to edit the selection. However for this to work you need to have your cursor placed inside those brackets. I think automatically jumping to the next brackets could be a nice timesaver.

the-mikedavis commented 2 years ago

Duplicate of https://github.com/helix-editor/helix/issues/1072

sudormrfbin commented 2 years ago

This is not an exact duplicate of #1072 since that issue is about making mim work like an all in one version of mi(, mi[, mi{, etc depending on the closest pair found (which still requires that the cursor be inside the pairs), while this issue is about jumping to the pair if the cursor is not inside them.

AligningEntropy commented 2 years ago

This is a feature I miss/would like as well.

For example, given the code line: ABC "DE" FG

If the cursor is on the char 'A' it would be nice if something like mi" would select DE just like it would if the cursor was on the char 'D'.

This would mimic the vim functionality of ci"

dead10ck commented 2 years ago

This does seem like it could be convenient; however, I'm a bit unclear on the expected behavior. For example:

fn foo() {
    // ... Lots of code

    let a = if true {

    }

    // ...
}

Say your cursor is on a. The strictly correct thing to do would be to search backwards until you find the outer {}, but this means you could be searching backwards all the way to the beginning of the document. Is the behavior that it would do this, and only if it doesn't find anything, start searching for the next instance of a closed pair?

AligningEntropy commented 2 years ago

In your example I would expect current behavior to select the entire foo(){...} function following the command mi{ or mim. (which it does)

The problem (for lack of better words) I think stems from match dealing only with surrounding pairs, which fails if there are none, and the lack of an ability to do a look ahead type matching.

For example, if I just had the line let a = "Hello world" Then, with my cursor on 'a', the command mi" fails because there is no surrounding quotes to my current position. Ideally I would like it to lookahead similar to doing f" + mi" which would allow a user to select the "Hello world" in the above example or choose between selecting the foo(){...} and ...true {...} code in your example.

However, this might cause some slightly confusing behavior without a separate command. ex: "Hello" said the man. "Hi" I replied.

If a mi" is done at the word "said" then the text " said the man. " is selected (current vim and helix behavior) rather than the potentially desired "Hi" text.

So, in order to help prevent this potential confusion whilst still allowing for non-surrounding examples to work, a separate command would likely be needed to indicate a look ahead only match. (ex maybe with a capital i: mI<char>)

dead10ck commented 2 years ago

However, this might cause some slightly confusing behavior without a separate command. ex: "Hello" said the man. "Hi" I replied.

If a mi" is done at the word "said" then the text " said the man. " is selected (current vim and helix behavior) rather than the potentially desired "Hi" text.

Yeah this is a good example of the ambiguity that would be probably impossible to avoid without resorting to using tree-sitter. Although it would probably be "good enough" for most cases. And perhaps it might even be a good idea to use tree-sitter when it's available, since we might as well use it for better results if it's there.

xulis commented 2 years ago

ace jump?

YeungOnion commented 1 year ago

In the given quote scenario, if next match is supported, wouldn't previous match be supported as well? So that you can go from cursor, █, to beneath underscores shown in example below,

_______
"Hello" █aid the man. "Hi" I replied.

Perhaps we could expand the goto_next_... and goto_previous... which are default mapped to square brackets from normal to include matches.

As a naive solution, maybe I could literally map ]mi] to the effect of f]mi] and similar for around, other match boundary characters, and prev match with ]m.

At least for brackets, might get something good like this, given input [ma followed by square bracket would move from █ to beneath underscores

     ___
words[i] = █lphabet[nums] // test 1
                   ______
words[i] = alphabet[█ums] // test 2
     ___
words█i] = alphabet[nums] // test 3

word█[i] = alphabet[nums] // test 4

nice quote behavior might be trickier to behave consistently because I could goto_prev and move left if I wanted to never match the quoting the cursor is within before the goto_...


Note: unsure if m should precede or follow bracket since most of the right bracket movements seem to be ts-based, maybe that would be best since this eventually should be syntax aware?