guillermooo / Vintageous

Vi/Vim emulation for Sublime Text 3
http://guillermooo.bitbucket.org/Vintageous/
Other
1.64k stars 115 forks source link

Select all search matches in select mode #511

Closed fenduru closed 10 years ago

fenduru commented 10 years ago

Often times I want to search for a pattern using / (slash), which very neatly highlights all of the matches. I would then like to select all of those highlighted matches (as in select mode), however select mode only lets you choose words or visual-mode selections.

guillermooo commented 10 years ago

I've always thought this might be useful. I think gH would be a good sequence to assign this command to. It should reuse the most recent search pattern.

fenduru commented 10 years ago

I'm working on this right now. Do you think it would be reasonable to use the existing gh, and use the vi_search regions if it exists, otherwise fall back to current behavior? I don't see too much of a use case for having both vi_search regions and select mode selections visible at the same time.

guillermooo commented 10 years ago

vi_search regions are erased after they aren't needed any more. The number of hits in the buffer may have changed between the time they were created and the moment the user wants to reuse them.

Besides, I think it wouldn't be obvious what happened if, after pressing gh, a bunch of regions were highlighted instantly. I think it's better to be more explicit about requesting the last search pattern used, hence gH.

fenduru commented 10 years ago

The way I was thinking was that if there are vi_search selections, these will be used when you enter select mode, and you would still use j, k, A, etc to choose which ones get selected. This way there is only one paradigm for managing selections in select mode, just different "sources" (this is already the case depending on if your cursor is on a word, or if you have a visual selection. We would add another one if there are search results).

guillermooo commented 10 years ago

I see what you mean now, but I'm still unconvinced -- I'd find it confusing. The caret could have traveled away from one of the vi_search regions, so when you pressed gh, it would jump to one of them (which one, BTW?). I'd say let's start simple and try a first implementation with gH that selects all of them and go from there? Allowing j, k and A to work in that case would require a separate mode, as far as I can tell.

fenduru commented 10 years ago

Okay, will do. I already have most of the "select all vi_search selections" stuff done, but I'm still not super familiar with this code base. There seems to be quite a few places that keys are listed, so I'm not sure how to go about adding a new hotkey. Any suggestions?

guillermooo commented 10 years ago

(I need to work on naming and terminology, but bear with me...)

To add a new 'command' you need to:

  1. Make sure that the involved keys are declared as key bindings in Default.sublime-keymap (ok in this case).
  2. Register the key sequence (string) so that Vintageous can process it. You do this in vi/keys.py.
  3. Register the command definition (a dict) in _vi/cmddefs.py.
  4. Route the key presses in the desired modes to the desired command definition. You do this in vi/keys.py.
  5. Process the command data when you're about to run it. You do this in vi/actions.py or vi/motions.py.
  6. Implement the Sublime Text command that does the job in xactions.py or xmotions.py.

I think that's all, but holler if you need help.

EDITED

fenduru commented 10 years ago

For what its worth, the amount of places that need editing in order to add a fairly simple action seems to be a bit much. Why does there need to be so much indirection? I feel like most things could be done using normal keymapping

{ "keys": ["g", "H"], "command": "vi_select_search_results", "args": {"mode": "normal"}}

And if you have a particular key that does multiple things:

{ "keys": ["A"], "command": "vi_append_to_line", "args": {"mode": "normal"}},
{ "keys": ["A"], "command": "vi_select_all", "args": {"mode": "select"}}

Then, in the base class that vi_commands subclass, you could filter based on whether or not you're in the right mode.

guillermooo commented 10 years ago

That was more or less the way it worked before, but then you could not have user mappings. In order to have flexible mappings, Vintageous needs to know about every key press. That's also very important for commands that require input.

I agree there's quite some indirection, but every time I tried to simplify things I would hit a dead end. I'd be happy to be enlightened about a better way, though.

guillermooo commented 10 years ago

I think we can close this, btw.

fenduru commented 10 years ago

What roadblocks have you run into (just so I can be thinking about all of the different issues).

Just brainstorming a bit, I think you could do something like this:

  1. Press c which calls vi_change (or whatever it is actually called).
  2. vi_change registers a 'motion callback'
  3. Press $ which calls vi_line_end_motion
  4. vi_line_end_motion sees that there is another command that needs a motion, so it calls the callback

There would need to be some special handling for numbers, where you would save a buffer of number key-presses, but that shouldn't be that bad.

This would allow fairly complex key-mappings without doing much more than using the built-in keymapping system.

Having remapping would be hard though. Maybe there is a way to hook into the underlying system and simulate keypresses for remapping? (Not sure about this one)

guillermooo commented 10 years ago

(Writing from memory here.)

  1. Press c which calls vi_change (or whatever it is actually called).
  2. vi_change registers a 'motion callback'
  3. Press $ which calls vi_line_end_motion
  4. vi_line_end_motion sees that there is another command that needs a motion, so it calls the callback

That may work more or less ok for a few commands, but what if you wanted to have a sequence like ys/foo<cr>" (from the surround plugin)? You'd need to load a second set of key bindings and they may or may not work depending on ordering.

Also, Vim waits forever on incomplete commands (unless it's holding an ambiguous mapping, in which case uses a timeout), but I believe ST always times out (or used to, anyway).

There would need to be some special handling for numbers, where you would save a buffer of number key-presses, but that shouldn't be that bad.

Don't forget registers and commands that accept input. You'd have a hard time trying to interpert "ad/foo using just normal key bindings. What about repeat, redo and macros? You cannot run the command until it's ready, and although Vintageous now does a better job of keeping the caret in a sensible place, it should actually be able to see the future carets and remember them. Vim seems to ignore motions when placing the caret, so when you redo or undo, you are left at the beginning of the corresponding operator's target region. This is currently one of the toughest problems in Vintageous. I think it could maybe be solved by looking at the future selection regions, but that means more state and processing ahead of time.

Having remapping would be hard though. Maybe there is a way to hook into the underlying system and simulate keypresses for remapping? (Not sure about this one)

Enabling custom mappings was the whole point of the recent rewrite. You cannot have them if you don't know which keys were pressed. Also, you must control key biding contexts too.

And you need the actual key names to display coherent feedback and to map stuff, like :nmap dx i"__FOOBAR__<esc>ea"<esc>, that should work now.

I haven't kept notes, but there's quite a few places where emulating Vim demands knowing the exact keys the user has pressed. That said, I'm sure there's room for a lot of improvement.

The area that seems to lend itself to further refactoring is the disconnect between defining a command in _cmddefs.py and updating the command just before it's going to be run, in either _viactions.py or _vimotions.py. It may be possible to unify both steps into a single one, but I think I ran into difficulties as well last time I tried.

guillermooo commented 10 years ago

It also looks possible to do away with defining keys for every mode if the command definition itself could return the actual definition for a given mode. That should also keep things tidier.

fenduru commented 10 years ago

Yeah that all makes sense (not totally sure what you mean by the last comment though). I agree that cleaning up the cmd_defs stuff into one location would be nice.

Also there seems to be another redirection (at least in the commands I saw) where the key binds to vi_function_name which "prepares" the command, then sends it to _vi_function_name.

I would love to see the keybinds that let vintageous listen to every keypress, then one hash that maps from key combination to command (and includes information like mode, etc in this hash).

guillermooo commented 10 years ago

Yeah that all makes sense (not totally sure what you mean by the last comment though).

In keys.py, you now have to map a sequence (like gg) to a command definition by mode. This means you need to declare keys for every single mode. I think it's possible to declare only all known sequences to the ideal command definition, and then the command definition would know which data to use for each mode or, if it wasn't a legal sequence for the current mode, simply a _missing command.

I think your last paragraph summarizes the idea, but I believe some restrictions imposed by the edit object in the ST api make it hard to make it that simple.