jeetsukumaran / vim-filebeagle

A VINE-spired (Vim Is Not Emacs) file system explorer.
161 stars 13 forks source link

default key bindings #6

Closed justinmk closed 9 years ago

justinmk commented 10 years ago

Some of the default key bindings shadow some useful operations. Some of these are minor, but the idea is that the user shouldn't have to worry about avoiding certain "reasonable" motions (we agreed that f and t are not "reasonable" in filebeagle).

Specifically, I am suggesting:

Opening Files and Directories~
  <CR>  /  o       
  v         
  s        
  t            
-------------------------------------------------------------------------------
Opening Files and Directories in the Background~
  CTRL-<CR>  /  zo          
  zv
  zs
  zt
justinmk commented 10 years ago

Second part of this request:

0 is a "reasonable" motion in filebeagle buffer, so it is surprising for it to be bound to something else. However, r has no use in filebeagle buffer, so why not replace: R 0r $r with rr r0 r$.

And for consistency with the first comment in this issue, I suggest replacing gr g0r g$r with zrr zr0 zr$.

This frees up R for "refresh", which is a better use of it since it is not a "composable" operation like r.

By the way, none of the above precludes allowing r to take a [count] prefix, if you had any plans to do so (based on your other mappings).

jeetsukumaran commented 10 years ago

You make a good case for these key mapping changes, and I largely agree. I think what is needed here is actually a facility for users to define their own custom key-mappings as needed. Right now, the approach that comes to mind would be a user-defined dictionary of key sequences as keys and <PLUG>'s as values.

jeetsukumaran commented 10 years ago

Also, 'r' is currently mapped to the refresh operation. This choice (and a number of others) was made based on the fact that NERDTree used it. I personally would actually prefer something else, like 'u' (for "update"), but then netrw uses 'u' to go up a directory. Difficult coming up with key-mappings that try to maintain consistency with widely-used plugins that do similar things! Again, I think, this makes the case for providing the facility for users to define their own key maps.

justinmk commented 10 years ago

a facility for users to define their own custom key-mappings as needed. Right now, the approach that comes to mind would be a user-defined dictionary of key sequences as keys and 's as values.

Yeah but that's a pain. The value in many of tpope's plugins is having default mappings that strive for harmony and intuition. In some extreme cases, concessions are made (for example, ys in surround.vim).

I definitely agree with matching NERD/netrw conventions, but r is worth deviating from in this case because AFAIK those plugins don't have a "read contents" feature.

I could already have overridden the default bindings myself, but it benefits everyone to provide well-crafted defaults.

BTW, many plugins try the "dictionary" approach and often end up ditching it. I recommend <Plug> maps instead, although really it would be best to find strong defaults.

jeetsukumaran commented 10 years ago

Yes, sensible and intuitive defaults would indeed be the ideal. However, intuition is so conditioned by things ranging from an individual's idiosyncratic history to personal philosophy to general environment to what he/she had for breakfast to make any generalization very challenging. Or rather, any generalization based on one's notion of intuition is bound to be challenged by someone else. For example, someone conditioned by CtrlP would find "CTRL-V" opening a split in the background a little bit disconcerting. And wars have been fought over different definitions of "sensible".

Nonetheless, I do generally agree with your key suggestions. However, I think that the option to customize it would be the best way to avoid wars and allow the interface to be adjusted to fit individual workflows. I have implemented both in the feature branch "dev-plug-maps".

I have updated the documentation to reflect the new key maps, but I have not yet documentation the customization architecture.

Basically, this involves two user-defined custom dictionaries:

g:filebeagle_buffer_normal_key_maps
g:filebeagle_buffer_visual_key_maps

The first is for normal mode mappings, while the second is for visual mode.

Both dictionaries take '<Plug>' definition strings as keys, and key sequences strings as values. The '<Plug>' names can be found here: https://github.com/jeetsukumaran/vim-filebeagle/blob/dev-plug-maps/autoload/filebeagle.vim#L384-L481.

So, for example, to open a new file in a vertical split using "<F1>" (for some reason), the user might have something like:

let g:filebeagle_buffer_normal_key_maps = {
    \ "FileBeagleDirBufferVisitCurrent"]: "<F1>",
    \}
let g:filebeagle_buffer_visual_key_maps = {
    \ "FileBeagleDirBufferVisitSelected"]: "<F1>",
    \}

The '<Plug>' names are a little clumsy right now, mainly because I am not sure whether the following will cause issues:

    nnoremap <Plug>FileBeagleDirBufferVisit [...]
    vnoremap <Plug>FileBeagleDirBufferVisit [...]

That is, whether or not the same '<Plug>' name will not override each other if one is mapped in normal mode and the other in visual. So, for now, I am suffixing "Current" and "Selected" to avoid name collisions.

Anyway, I have not tested this thoroughly, but so far it seems to be working OK. I will probably merge to MASTER after a little bit more trial time.

Let me know what you think.

jeetsukumaran commented 10 years ago

Also should note that 'CTRL-S' may be problematic as a default with console Vim in some terminals in their stock/default configuration.

justinmk commented 10 years ago

CTRL-S' may be problematic as a default with console Vim in some terminals

You're right. And most vim users don't like "chorded" bindings anyway. I've updated the first and second comments to instead suggest z-prefixed bindings for the "send-to-background" variants. z isn't useful in file beagle unless you plan to support folds, and it's a pretty good analogy with ctrl-z in the terminal.

intuition is so conditioned by things ranging from an individual's idiosyncratic history to personal philosophy to general environment

We can make reasonable inferences based on the most popular plugins. And this multiplies the usefulness of the Vim plugin ecosystem. I personally think q is somewhat lame "quit" keybinding, but it is de facto standard, and I benefit much more from accepting it than fiddling with every damn plugin's keybindings.

two user-defined custom dictionaries

Why are the dictionaries necesssary if <Plug> maps are available?

whether or not the same '' name will not override each other if one is mapped in normal mode and the other in visual. So, for now, I am suffixing "Current" and "Selected" to avoid name collisions.

Same <Plug> name in different modes absolutely will not overlap. I recommend using the same name:

nnoremap <Plug>(FileBeagleFoo) [...]
vnoremap <Plug>(FileBeagleFoo) [...]

Then user can do this:

nmap foo <Plug>(FileBeagleFoo)
vmap foo <Plug>(FileBeagleFoo)

It's also highly recommended to wrap your <Plug> names in () as shown above, otherwise <Plug>FileBeagleFoo and <Plug>FileBeagleFooBar will cause a pause if user invokes the shorter one.

I am suffixing "Current" and "Selected" to avoid name collisions.

Just to be clear: this is not necessary at all.

jeetsukumaran commented 10 years ago

I've updated the first and second comments to instead suggest z-prefixed bindings for the "send-to-background" variants

Yes, that prefix is a good one. However, maybe '&' might be even better? Stronger semantic correspondence (as in "do this in the background") and allows for the optional usage of folds (e.g., if you have many files in a directory, you can create on-the-fly manual folds to tuck away files that you do not want to see)? The only downside is that "&" may be quite a bit more difficult to reach than 'z', but perhaps the semantics and the ability to use folds win out over the slight difference ergonomics here?

Why are the dictionaries necesssary if maps are available?

Because the "nmap" statement must be defined to be local to the FileBeagle buffer. If I just provide the "<Plug>" definitions, how would a user provide mappings to these that are exclusive to the FileBeagle buffer but not to any other buffer? Would not something like the following in the user's "~/.vimrc':

nmap f <Plug>(FileBeagleFoo)

result in the key 'f' being mapped to '<Plug>(FileBeagleFoo)' in all buffers? With a dictionary, I can create the FileBeagle buffer and then run through the maps with "nmap <buffer>" to achieve this.

justinmk commented 10 years ago

The only downside is that "&" may be quite a bit more difficult to reach than 'z', but perhaps the semantics and the ability to use folds win out over the slight difference ergonomics here?

Up to you. I thought about & but rejected it because of the inconvenience. And filebeagle does not have an indented view or "tree" view so folds are almost certainly YAGNI. Too much inconvenience for a very unlikely use-case.

If I just provide the "" definitions, how would a user provide mappings to these that are exclusive to the FileBeagle buffer but not to any other buffer?

I forgot about that. A custom event could avoid some code, and looks nicer than a dictionary:

autocmd User FileBeagleOpen * nmap <buffer> f <Plug>(FileBeagleFoo) 
  \ | vmap f <Plug>(FileBeagleFoo)
  \ | nmap g <Plug>(FileBeagleBar)

But that's a matter of taste.

jeetsukumaran commented 10 years ago

By the way, none of the above precludes allowing r to take a [count] prefix, if you had any plans to do so (based on your other mappings).

Actually, it already does. E.g., "42r0" will read the contents of the file displayed at line 42 and insert the contents at the beginning of the calling buffer.

jeetsukumaran commented 10 years ago

Up to you. I thought about & but rejected it because of the inconvenience. And filebeagle does not have an indented view or "tree" view so folds are almost certainly YAGNI. Too much inconvenience for a very unlikely use-case.

Putting "&" through its paces as a prefix, and it really is quite a bit clumsier, enough to actually get in the way. "z" is so much nicer. Of course, either way, the potential for user customization via setting the "g:filebeagle_buffer_background_key_map_prefix" exists, But, as you say, sensible defaults are the way to go, and "z" seems so much more seamless that you might have almost convinced me at this point ... and I might convince myself fully if I play around with "z" a little more.

I forgot about that. A custom event could avoid some code, and looks nicer than a dictionary.

Hmm. I agree the custom event approach is cleaner from the point of view of actually using the Vim object model (instead of a bunch of strings masquerading as things). But this would mean that the "<Plug>" definitions would have to be available globally before the FileBeagle buffer was actually created right? And I would then have to have a whole bunch of "if !hasmapto()" statements to provide the default mappings? So maybe we are just redistributing the "un-niceness" instead of improving it with this approach. And, additionally, it does place a somewhat higher burden of responsibility to be careful on the user: e.g. the user might forget the "<buffer>" on some lines (as you did in your quick example), potentially making life really confusing later on.

justinmk commented 10 years ago

But this would mean that the "" definitions would have to be available globally before the FileBeagle buffer was actually created right? And I would then have to have a whole bunch of "if !hasmapto()" statements to provide the default mappings?

I believe so.

user might forget the "<buffer>" on some lines (as you did in your quick example),

Ha :)

jeetsukumaran commented 10 years ago

So the convenience of "z" beats that of "&". But "z" loses out on semantics, not so much because it lacks it in relation to "&", but rather because it has too much meaning in Vim already. Unfortunately, the semantics of "z" in Vim (for me) is too tightly bound with that of folds. Just looking at the FileBeagle help I find the fact that "z" is bound to a non-fold operation conceptually jarring, even if the particular keys overridden (e.g., "zv") are probably part of nobody's repertoire.

So I have decided to go with "b". Of course, "b" is also loaded with meaning, and it is probably one of my top 10 most frequently-used keys in normal mode. But somehow, because it is never used as a initial of a suite of composed sequences (like "z"), its usage as such in the FileBeagle buffer seems to me conceptually distinct enough that it does not create a semantic dissonance in the way that "z" does. Downsides are: (a) preclusion of normal backword-word movement; (b) conflicts with "back to previous directory" of netrw. I think (a) is not an issue because movement in the directory buffer is, again, linewise. For (b), I guess I am hoping that the transition to using "BS" will not be too rough given that "b" by itself does the same thing if users can wait past the delay in the mean time.

justinmk commented 10 years ago

Ok. But FWIW, z does not always mean "fold" in Vim: zz, zt, zb, zh, zl, zs, ze, ctrl-z...

jeetsukumaran commented 10 years ago

Yes, and until you pointed it out, I forgot about some of these even though, believe it or not, I use "zz", "zb" etc . and a few others a lot! Many of the native Vim key maps become so deeply ingrained in muscle memory that they bypass the conscious mind and come under the control of the autonomous nervous system. A couple of days ago, I was totally stumped as to what "m" did in normal mode, even though I use marks all the time: I actually had to look it up in the help, and ironically, dropped a mark at my current line in the code just before doing so.

jeetsukumaran commented 10 years ago

Ok, at the risk of seeming unstable, I have just thought of an alternate for the background prefix key: "p", as in "preview".

The default behavior of, e.g., ":pedit", is exactly that of opening/doing something in the background while keeping focus in the current buffer. The correspondence would be very strong when opening files in splits or tabs in the background, a little less so when opening in-situ.

justinmk commented 10 years ago

at the risk of seeming unstable,

Waffling is the name of the game when it comes to figuring out a good user experience :)

I could live with p. I don't know if the mnemonic works for me, but it's better than b because it's not a motion

ghost commented 9 years ago

Would it be possible to not disable the default keybindings [".", "p", "P", "C", "x", "X", "r", "R", "i", "I", "a", "A", "D", "S", "U"]? Their functionality is already disabled by setting modifiable=off, from what I can tell.

The reason why I'm proposing this: I'm using a weird keyboard layout where t and r are in place of j and k, and because FileBeagle sets the buffer filetype before it creates the mappings, I can't use .vim/after/ftplugin/filebeagle.vim either. Alternatively, one could change the two lines (the call to self.setup_buffer_opts() and the one to self.setup_buffer_keymaps()) around, if that doesn't break anything.

I'm sorry if I was mistaken in posting in this thread; if you think this warrants a separate issue, I can post it as one no problem.

jeetsukumaran commented 9 years ago

Their functionality is not so much disabled as much as blocked with an annoying error message.

Could you try hooking into the FileBeagleReadPre or FileBeagleReadPost autocommand events which fire just before/after the buffer is rendered?

Of the top of my head I cannot think of a reason not to invert the order the setup, and I could do this if the above is not acceptable for some reason.