Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimenting with jump modes and multi-caret mode #350

Closed
chylex opened this issue Dec 14, 2020 · 19 comments
Closed

Experimenting with jump modes and multi-caret mode #350

chylex opened this issue Dec 14, 2020 · 19 comments
Labels

Comments

@chylex
Copy link
Collaborator

chylex commented Dec 14, 2020

I experimented with a major change to how jump modes work. Perhaps a video is best, so I uploaded a recording (or zip download if the video isn't available anymore). I will give context to the video below.

Basically, it moves "jump mode selection" after typing the tag, allowing you to press a single key to select the jump mode:

obrazek

Here I typed my AceJump shortcut Alt+J, then "sp" as the query, followed by the tag. I added a hint tooltip with currently implemented modes:

  • Jump to Tag is the old style of jump: is|Sprint
  • Jump to Query jumps to the last character of the query: isS|print
  • Jump past Query jumps right after the last character of the query: isSp|rint
  • Select Word is target mode: [isSprintToggled] but holding Shift selects only a "camel hump": is[Sprint]Toggled
  • The rest should be self-explanatory, but Shift has some potentially interesting properties

This way of jumping also allowed me to create a neat multi-caret mode. In the video I activate it with Alt+Shift+J, and then it works as follows:

  1. Type the initial query if you want
  2. As soon as you start typing a tag, the query is saved
  3. Every time you finish typing a tag, you get the option to select a jump mode which adds a caret or selection
  4. Repeat the saved query and type either another tag, or cancel using Escape or Enter

If you're interested, the current version is in https://github.com/chylex/AceJump/tree/experimental-jump-modes branch. If not, feel free to close the issue.

@breandan
Copy link
Collaborator

breandan commented Jan 4, 2021

Hi @chylex, great idea! I like this feature for a few reasons.

  • It enables us to get rid of the platform-specific actions, which require binding a new hotkey or cycling through jump modes, a feature I always felt was a bit clunky. The solution you propose is the same solution TraceJump uses, a similar tool which supports whole-screen navigation.
  • Second, it fits the Vim modal philosophy, as opposed to Emacs' chord-based scheme. Although AceJump was originally inspired by a plugin developed in the Emacs community called ace-jump-mode, a large fraction of AceJump users now depend on IdeaVim interoperability.
  • It enables better feature discoverability over the rainbow cursor. While new users might not be familiar with cyclic selection or understand what the cursor color means, the tooltip is self-documenting. It could also feature other post-jump actions, i.e. "search for usage in browser". Regardless of whether the post-jump behavior works out, the tooltip alone is worth adding to improve usability.

Some ways your proposed behavior could be improved:

  • Each AceJump activation now requires at least four keys: 1-2 to activate AceJump, 1-2 to select the tag and 1 to select or escape the mode selection menu. One solution could be to use Jump mode by default when the last tag character is pressed, but if a modifier key is pressed, e.g. Ctrl it activates the modal menu. Another could be to add a separate action or settings toggle to enable/disable.
  • In general, it reproduces some functionality that is already available in the IDE or Vim. It might be better to just return control to Vim or the IDE as you suggested. We could also activate a drop-down menu, containing editor specific actions but before adding a bunch of specific functionality, we ought to think about what alternatives exist, assuming the user is proficient with [Idea]Vim and the IDE.
  • Regarding the multi-caret functionality, this is related to Multi Caret Selection Mode #279. It's a good idea and we would like to have something along these lines, however it's also a non-trivial feature that requires a more detailed discussion. In particular, we should think a bit more carefully about how this compares with the existing Find/Replace or multicaret functionality available in IdeaVim or the IntelliJ Platform.

If you believe the benefits outweigh the disadvantages and want to move forward, happy to consider a PR. If we can iron out the details, I think it deserves a major version release, we should call it 4.0.

@chylex
Copy link
Collaborator Author

chylex commented Jan 4, 2021

I'm definitely still figuring out what feels best, and I've likely gone beyond what's reasonable for most users (whether they know use IdeaVim or not), so I'm definitely going to be biased just because I got used to how things work. Currently the "main menu" looks like this:

obrazek

Most actions have Shift modifiers, which are not clear from the tooltip and probably confusing at first. I did just make a change in an attempt to reduce keystrokes to select between caret and another point. I'm not sure how to best explain it, but here are a few examples of actions:

sw - select word
sW - select word, and append to existing selections
Sj - select from caret to tag
SJ - select from caret to tag, and append to existing selections

If it seems too complicated, I can make a simplified version that's more like the initial post. You could try https://github.com/chylex/IntelliJ-AceJump/commits/experimental-interactive-modes and let me know which modes and modifier interactions you'd want to keep.

To reduce keystrokes for simple jumps, a separate action sounds like the best option - maybe Ctrl+; does a simple jump and Ctrl+Alt+; shows the menu?

I took out multi-caret mode because some previous changes broke it and the "append to existing selections" modifier does an alright job if you need something more complex than IDEA's Find with 'Add Selection for Next Occurrence' feature.

@chylex
Copy link
Collaborator Author

chylex commented Jan 9, 2021

Did a second pass on the idea, the latest version of the branch has mode cycling again, but this time it works like this:

  • Mode 1 is a simple jump mode where you type the query + tag, then it asks you what to do:
    obrazek
  • Mode 2 is called 'From Caret' and performs an action on a range between the caret and another point. It starts by asking you the action:
    obrazek
    After you select the action, type the query:
    obrazek
    Finally it asks you where to select to:
    obrazek
    For example, pressing E would select from caret to the end of the word "setup".

I'm going to test it out over the next while. The main problem I had with my previous solution, besides the excess of nested menus, was that the order of operations for 'From Caret' selections felt unnatural, so I'm hoping that by selecting the action first it'll make using them easier, but I'll see.

One thing I also want to look at is a third mode, in which instead of performing an action between caret and another point, you define both points for the action with two consecutive searches.

@chylex
Copy link
Collaborator Author

chylex commented Jan 21, 2021

Another update with a fresh styling update and color scheme. The video covers:

  • Mode 1 (Select Hump)
  • Mode 1 (Select Line)
  • Mode 2 (Selection between caret and another point)
  • Mode 3 (Move to Caret)
  • Mode 4 (Clone to Caret with multiple carets)

You may also notice that tags only appear after I type either 2 alphanumeric characters or a special character, the amount is configurable and setting it to 2-3 helps prune tags in dense repetitive text.

2021-01-21.19-22-17-1.mp4

I don't plan to PR the current state because to make things simpler when experimenting, I removed even more features and options than with the initial refactor, so I think it'll be easier if I finish the PR for the initial refactor first.

@breandan
Copy link
Collaborator

Hey @chylex, sorry! I lost track of this one for a while, been occupied with some other projects for the last two months. Just wanted to say that all of these improvements look terrific, I really love the new styling updates -- much easier on the eyes. It would be great if you could submit this as a PR, I'd like to merge it into master and give you credit for all your excellent work. Thanks!

@chylex
Copy link
Collaborator Author

chylex commented Mar 28, 2021

Hi, I have also been quite busy lately, but I tinkered with the current version some more (and started using IdeaVIM so I ended up simplifying some of the modes, and among other things added a Quick Jump keybind that works like the old mode-less AceJump).

This is the current state of my branches, you can see that experimental-rework has removed some features, either because I didn't want to maintain them in the branch or they conflicted with another feature I wanted to add (for ex. you can now set a minimum query length before tagging happens, but Enter can bypass it and tag immediately). Let me know which version you want me to PR, and which commits you want me to revert. There are also some things that still need to be re-added that I haven't ported during the original refactoring, such as pinyin support and API for compatibility with other plugins.

obrazek

@breandan
Copy link
Collaborator

breandan commented Mar 28, 2021

Everything before the [WIP] commits looks good to go. If you want to PR everything up to chylex/IntelliJ-AceJump@054f604, we should be able to release those under 3.7 fairly quickly. Once you're happy with this new interactive functionality and are ready, let's start a new PR for 4.0. Don't worry too much about the missing features, I can take care of Pinyin mode and the IdeaVim listeners if it's too much trouble. Thank you!

@chylex
Copy link
Collaborator Author

chylex commented Mar 28, 2021

Pinyin should be easy, all reading of editor text goes through AceUtil.kt (Editor.immutableText) extension, so you can add the conversion there.

Can't remember how much of a pain IdeaVim listeners are now that most global state is gone, but if you can get access to the listener's attached editor then you can use SessionManager[editor] and implement listener callbacks on the Session.

@breandan
Copy link
Collaborator

breandan commented Apr 3, 2021

I've reimplemented #314 and opened a ticket for #354. Initial results look promising, everything seems to be working properly so far.

@chylex
Copy link
Collaborator Author

chylex commented Apr 3, 2021

Cool, just a reminder that these issues should be fixed by the refactor. They should be verified to be still fixed in master, let me know if you'd like me to do that and update you on whether all of them can be closed now, or you can check it when you have time. #348 (comment)

@breandan
Copy link
Collaborator

breandan commented Apr 3, 2021

I'm not sure how to verify all of these issues, but I take your word for it. Even if we missed a few, most of them are stale anyway, and the stack traces would be too much trouble to debug at this point. Maybe we should just close them and hope for the best, people can always reopen if they encounter them again.

@chylex
Copy link
Collaborator Author

chylex commented Apr 3, 2021

I suppose so. All the ones with stack traces shouldn't happen because the offending code is gone completely, the rest I verified when I wrote the issues down but it was a while ago.

@breandan
Copy link
Collaborator

breandan commented Apr 4, 2021

Although I was unable to reproduce all of the issues as described, I verified the ones without stack traces and closed the ones with old stack traces. Nice work!

@chylex
Copy link
Collaborator Author

chylex commented Apr 4, 2021

#310 and #233 are also ok now, the first one now tags regexes without conflicts while also allowing search queries.

Thanks!

@breandan
Copy link
Collaborator

@chylex What do you want to do about these experimental UX features? Have you gotten a better feel for them and do you think they would be useful to release to a wider audience? Personally, I like the idea of using a tooltip to display richer information (e.g. #227), although I would prefer to avoid duplicating features from IdeaVim/EasyMotion if possible. Happy to restart this discussion at some point, otherwise feel free to close if you're happy with the current functionality. Thanks!

@chylex
Copy link
Collaborator Author

chylex commented May 14, 2021

I don't know, I ended up moving to IdeaVim a while back and making shortcuts for some of these or taking advantage of vim to do the same actions:

obrazek

I find that Jump / Word End / Line End / Select Line are easier to do by jumping and then pressing a vim normal mode key, Select Word / Select Around / Select Expansion are quick to do by jumping and pressing the IJ shortcut to expand selection or vim, out of these I find that hump navigation and selection is the only thing I miss in vim sometimes (but I could solve that with a vim plugin).

The Declaration / Usages actions I ended up mapping to <Space>d and <Space>u which I find easier to use than both mode cycling in current release, and the "jump and then decide" popup in the fork. Then, Intentions are easier with IJ's "go to next error" action, and Refactor is fine with a shortcut.

So in the end, most of the special actions I came up with are either already in current AceJump or I found a better alternative; they could still be useful for someone who doesn't use IdeaVim, but I kind of lost the ability to judge that.

I still like the feature where you can perform an action between two searches:

obrazek

These (especially Move to Caret) feel a lot better doing via AceJump than via vim. I could imagine that you would have one shortcut that does a simple jump, and one shortcut that brings up a menu with options like:

  • Jump
  • Word End
  • Declaration
  • Move to Caret
  • Clone to Caret

and both would guide you with hints like:

obrazek

That could be one option. Another option could be to keep the current cycling system, but include hints and refactor jump modes to allow other plugins (like IdeaVim / EasyMotion) to have more control over jumping, perhaps by extending some class like in AceTagAction in my fork.

@breandan
Copy link
Collaborator

breandan commented May 19, 2021

I am all in favor of adding new functionality, and there is probably an unlimited number of features we could implement, but perhaps a better solution might be to conserve our effort and work on improving configurability so that users can define their own custom actions (e.g. in the settings panel or .ideavimrc). The mode cycling solution and one-off actions seem a bit clunky.

refactor jump modes to allow other plugins (like IdeaVim / EasyMotion) to have more control over jumping

Agreed. Most of AceJump's special modes are either regex search (e.g. #215) or a jump followed by a subsequent action (e.g. expand selection, go to declaration). These modes feel pretty ad hoc, and as you mention, it would be great to have a more configurable way to automate these kinds of actions (e.g. through better integration with IdeaVim or some other scripting mechanism).

@chylex
Copy link
Collaborator Author

chylex commented Nov 16, 2021

So, since I've moved to IdeaVIM and am now using it heavily, I ended up integrating parts of EasyMotion into my fork instead of adding a bunch of different modes myself. I feel it's really nice to use that way, though I don't know how much of the fork would be useful to you and the general public.

I kept special actions for Declaration / Usages / Intentions / Refactor which I think are useful, and the implementation is pretty clean since I kept my refactor of jump modes:

session.startJumpMode { ActionMode(AceTagAction.GoToDeclaration, shiftMode = false) }

Here, ActionMode just performs one of predefined actions once a tag is accepted.

object GoToDeclaration : AceTagAction() {
  override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean, isFinal: Boolean) {
    JumpToWordStart(editor, searchProcessor, offset, shiftMode = false, isFinal = isFinal)

    val action = if (shiftMode)
        IdeActions.ACTION_GOTO_TYPE_DECLARATION
    else
        IdeActions.ACTION_GOTO_DECLARATION

    ApplicationManager.getApplication().invokeLater { performAction(action) }
  }
}

I think this approach could make AceJump quite extensible by other plugins, and it should be easy to refactor the existing enums if you're interested. There are some interesting things the jump modes can react to, including handling key presses and manually updating the results or changing to a different jump mode:

sealed class TypeResult {
  object Nothing : TypeResult()
  class UpdateResults(val processor: SearchProcessor) : TypeResult()
  class ChangeMode(val mode: SessionMode) : TypeResult()
  object RestartSearch : TypeResult()
  object EndSession : TypeResult()
}

My fork doesn't have the listeners EasyMotion uses to listen for accepted tags, since I initially designed the custom jump modes for this purpose, but it wouldn't be a good idea to completely replace the listeners and break EasyMotion again.

Unfortunately I didn't have any use for the tooltips anymore, and also ended up removing the "Clone/Move to Caret" and similar actions (though there are still occasions where it's a bit easier to use than vim). The mess of branches in my fork still remains in whatever state they were left last I touched them, so if there's anything from this thread you would like me to adapt & PR, or discuss further, let me know. Otherwise, I'm not planning to further develop the original idea, so feel free to close this.

@breandan
Copy link
Collaborator

This sounds like a great idea and could address the need for more configurability that some users have requested. If you're interested in improving plugin extensibility, I'm all for that, but I think it's a separate topic. Perhaps the best thing for now would be to close this issue and continue the discussion somewhere else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants