lifepillar / vim-mucomplete

Chained completion that works the way you want!
MIT License
913 stars 18 forks source link

add mappings to enter subdirectory in path completion #20

Closed Konfekt closed 7 years ago

Konfekt commented 8 years ago

Perhaps this mapping is useful when completing file paths, and could become an option or make it into the docs:

When completing a file path (say /home/ and hitting <tab>) , and after having chosen a directory by iteratively hitting <tab> (say /home/user) , hitting / allows you to continue completing the file names inside it (that is, /home/user/...:

let s:slash = has('win32') ? '\' : '/'
exec 'inoremap <expr> ' . s:slash . ' '
      \ . '(pumvisible() && <SID>isBehindDir()) ?'
      \ . '"<C-Y><C-X><C-F>" : "' . s:slash . '"'

let s:escaped_slash = escape(s:slash, '\')
function! s:isBehindDir()
  return getline('.') =~# '\f\+` . s:escaped_slash . '$' 
endfunction
josefson commented 8 years ago

In your example, when i hit /home/ and press tab until i select the user subfolder i already get a / at the end: /home/user/. I can't hit tab nor / to try another nested folder, what i have been doing is to accept/finish completion and hit another tab to keep going. This behavior that @Konfekt is talking about is the same YCM uses and i think it's more intuitive. So we shouldn't get the latest / and get the tab trigger when we hit / like in the example bellow. I think that would be a good enhancement. Image PathCompletion

lifepillar commented 8 years ago

So we shouldn't get the latest /

MUcomplete uses Vim's default file completion, so that is not possible.

Konfekt commented 8 years ago

Yes, Vim uses a trailing slash / to indicate a Directory whereas YCM adds an extra column to indicate either directory or file. So the slash is already there.

With these mappings, Vim works like ZSH, for example, where a trailing slash indicates a Directory as well,but sill hitting / enters the selected subfolder.

lifepillar commented 8 years ago

The following setting may help, too:

let g:mucomplete#trigger_auto_pattern = { 'default' : g:mucomplete#pathsep . '$\|\k\k$' }

After completing part of a path, you may type any character followed by backspace to have the pop-up menu appear again with a list of subdirectories. Not ideal, but it works without any changes in µcomplete.

lifepillar commented 8 years ago

I've tried @Konfekt's idea and it seems a good compromise to me, well fit to the way Vim's file completion works (and it may be familiar to zsh users, too). I am a bit reluctant, though, to remap such a common character such as a (back)slash. It could be offered as an option.

On the other hand, it shouldn't be difficult to implement YCM's file completion in a function, which could be used as an additional completion method, similar to the way 'ulti' is implemented. YCM does it in Python, I think, but it could be done in pure Vim, too. The tricky part would be making it portable and robust: the Dirvish plugin might be a source of inspiration. Pull requests are welcome :)

lifepillar commented 8 years ago

the Dirvish plugin might be a source of inspiration

On a second thought, this might not be a good idea, because µcomplete is in the public domain, while Dirvish is licensed through the GPL.

Konfekt commented 7 years ago

It would have to reimplement the completion list from scratch, by systemlist( has('win32') ? 'ls' : 'dir')? Because Vim's file completion is robust, and mu-complete already supports it, it seems overkill to add another, untested, file completion to avoid mapping slash in insert mode.

But making it a documented option, is a good compromise.

lifepillar commented 7 years ago

It would have to reimplement the completion list from scratch

Yes, but I'd rather do something along these lines:

fun! PathCompleteDraft() abort
  let l:prefix = matchstr(strpart(getline('.'), 0, col('.') - 1), '\f\+$')
  if strlen(l:prefix) > 0
    let l:candidates = map(glob(l:prefix.'*', 0, 1, 1),
          \  '{
          \      "word": v:val,
          \      "menu": (isdirectory(v:val) ? "[dir]" : "[file]")
          \   }')
    if !empty(l:candidates)
      call complete(col('.') - len(l:prefix), l:candidates)
    endif
  endif
  return ''
endf

This is not devoid of its own difficulties, though: to make it work like YCM, some code should be executed at CompleteDone, too. Complicating µcomplete only for this use case seems overkill for me, too.

lifepillar commented 7 years ago

@Konfekt @josefson Would you mind trying the path-completion branch? I have added an experimental 'path' method, which behaves similarly to what @josefson has suggested. The 'file' method is unchanged for now.

josefson commented 7 years ago

As i see the branch is behaving like this:

I have added an experimental 'path' method, which behaves similarly to what @josefson has suggested.

I think you are being modest @Konfekt. From what i have tested so far, it behaves exactly like that.

Let me take this opportunity and thank you again for the work input you have been putting in this amazing plugin. A simplistic, yet fresh concept in autocomplete land for vim, which brought together some advantages that i didn't see put together before. I hope vimland get to test it, and maybe enjoy it like i am. I know you deserve some love for the great work you have been putting into it. Thanks man.

lifepillar commented 7 years ago

I have also implemented @Konfekt's suggestion, but a bit differently. Now 'file' completion works as follows: when accepting a menu entry with <c-y>, file completion is triggered again. Using <c-y> is standard Vim's mapping, and there is no need to remap / (of course, a user may still do it, if desired).

Please try the path-completion branch and let me know how it works for you. I'd like feedback especially from Windows users, since I cannot test in Windows.

lifepillar commented 7 years ago

@josefson Thanks for the kind words. I've got much from Vim and the Vim community, giving something back is only fair. Plus, it's fun!

lifepillar commented 7 years ago

Just wondering: can YCM deal with paths containing spaces? Vim's file completion does not.

josefson commented 7 years ago
  1. I tested the behavior on file completion-method(path-completion branch) and it is also very satisfying. I could have lived with it.
    • Oh i see what is happening, whenever a spaced [path|file]name is inserted, either on file or / on path get out of the scope method. That should be a pain, for windows users mainly, i think most unix users avoid the use of spaces on filenaming. Good catch.
  2. About YCM, i went through the pain of installing it to test your request, and yes it recognizes and keep completing over spaced directories: Image ycm_spaced_path
lifepillar commented 7 years ago

@Konfekt @josefson Both the features you have requested have been implemented and are available in the current master, so I'm closing this. Please open new issues if you need to report bugs.

lifepillar commented 7 years ago

…and don't forget to read the docs!

Konfekt commented 7 years ago

Well done!

Konfekt commented 7 years ago

The documentation is off: The mapping of the thread is a différent one. The check if a file path is being completed is missing. Otherwise / ends completion in général. That's not intended.

lifepillar commented 7 years ago

I have updated the documentation: do you still find that there is something unclear?

Re the unintended behaviour, can you please elaborate?

Konfekt commented 7 years ago

Intended, no problem with indentation. The point was that

inoremap <expr> / pumvisible() ? "\<c-y>" : '/'

does not check if one is completing a file path. Thus, / will always end completion. That's only intended, that is, wanted, when completing file paths.

The original mapping

let s:slash = has('win32') ? '\' : '/'
exec 'inoremap <expr> ' . s:slash . ' '
      \ . '(pumvisible() && <SID>isBehindDir()) ?'
      \ . '"<C-Y><C-X><C-F>" : "' . s:slash . '"'

let s:escaped_slash = escape(s:slash, '\')
function! s:isBehindDir()
  return getline('.') =~# '\f\+` . s:escaped_slash . '$' 
endfunction

makes this additional check. (Note that now <c-x><c-f> is superfluous.)

lifepillar commented 7 years ago

Ah, right. I have updated the example in the documentation, adding your code (slightly modified to make it more general).

Konfekt commented 7 years ago

Perfect!