One-off equivalent to g:ctrlp_user_command #273

tpope opened this Issue Sep 6, 2012 · 11 comments

2 participants


Here's my current ctrp_user_command:

let g:ctrlp_user_command = ['.git/', 'git --git-dir=%s/.git ls-files -oc --exclude-standard']

For a while now, I've been wanting to make some form of this accessible from Fugitive, but I've been stumped as to how to do it uninvasively. Clobbering g:ctrlp_user_command would of course be the height of rudeness. Even if I could figure out how to carefully mutate it, that's still potentially frustrating a user who depends on the existing behavior.

Today, as I was experimenting with giving the buffer that :Gstatus creates a larger role, one solution came to me. What if I could activate this behavior only in said buffer? Surely nobody would find that offensive. In fact, the existing <C-p> is obliviously clobbered, and nobody has complained, so this seems like to perfect sandbox to experiment in.

Best I can tell, though, there's no external API that lets me accomplish this, so I am requesting that you add one. The absolute perfect interface (allowing maximum flexibility for things like submodules, with their weird Git dir), would be a way to tell ctrlp.vim "use these files and consider them relative to that directory". For example:

let files = split(system('git --git-dir=/tmp/repo/.git/modules/submodule ls-files -oc --exclude-standard'), "\n")
call ctrlp#custom({'root': '/tmp/repo/submodule',  'files': files})

But don't let me tell you how to design your own interface. I'm sure I can make use of whatever bone you throw me.


Hmm, can you be more specific? What exactly do you want to implement and how do you plan to implement it in Fugitive? I'm curious as to how something can possibly clobber the option, or more importantly, why does it even have to do that? Since ctrlp runs in its own buffer and has its own local working directory, pretty much having its own world.

I'll certainly consider this but to have that particular ctrlp#custom(), there'll need to be a lot of changes. So what exactly do you want it for? Something to use in Fugitive to get the user_command feature for :Gstatus? I don't think I follow...

Also, <c-p> in Normal mode is a duplicate of k, so it's not hard to see why nobody has complained about that default (which can be very easily changed anyway, and the option to do this is plastered everywhere).


Well that was my attempt at a concise explanation, so I guess I'll have to go for a verbose one. Bear with me.

I'll start my saying that some time ago, before I knew about ctrlp.vim, I mapped <C-n> and <C-p> in the :Gstatus window to mean "next file" and "previous file". I later became aware of the conflict, but it didn't seem like that big of deal, because :Gstatus is a preview window and who needs a fuzzy finder in a preview window?

But it's also possible to open that status in a regular window with :Gedit : (or even :edit .git/index). Right now, I'm probably the only one to do that, but it's such a handy springboard, I'm debating ways to make it far more prominent. For example, remapping <C-W><C-N> to open that window rather than a blank one. Nobody really wants a blank window, right? Except now I've blocked people from doing <C-W><C-N><C-P> to start finding in a new window.

Easy solution: make my <C-p> map invoke :CtrlP on the first line (and consider deprecating it long term). This works, but it misses the submodule case, plus things like .gitignore aren't respected (although I suppose you could argue that's a feature). I could ask ctrlp to be smarter about that (and you are more than welcome to pursue that route instead), but since I'm maintaining a Git plugin, why not let the Git plugin be responsible for that?

Beyond this, I can see some more advanced use cases, such as making it work when editing history (:Gedit HEAD~3:README) or giving higher precedence to dirty files. Both of those require a bit more control over how ctrlp seeds its file list.


Oh I see. I thought you were talking about ctrlp's <c-p> mapping. I didn't know :Gstatus's <c-p> clobbered ctrlp's. But I still don't get exactly what this will be used for, and in which situation... I'm down with allowing more control, in any case.

So what if you could use a b:var which could be modified freely, with the git ls-files command that would be used in let files = split(system(...), "\n")?

let b:ctrlp_user_command = ['.git', 'git --git-dir=/tmp/repo/.git/modules/submodule ls-files -oc --exclude-standard']

And instead of using call ctrlp#custom({ 'root': '/tmp/repo/submodule', ... }) to open up ctrlp, just use :CtrlP /tmp/repo/submodule, since this only matters for find file mode.
An alternative to this is let b:ctrlp_working_path_mode = 0, lcd /tmp/repo/submodule, then :CtrlP.

Would that work/be enough?


I actually didn't realize :CtrlP took a path. (As I recall, the last time I looked it took an integer, but who knows how long ago that was.) Neat. I'll go ahead and hook that up.

The b:ctrlp_user_command does seem like it would address my other concern, but after thinking about it I'm on the fence about whether it's worth the trouble for fugitive to monkey with it. I would like to have it if it's easy to implement. I would consider it more than sufficient for resolving this issue.

Bummer I failed to convey my grand vision, but that might have something to with it not being fleshed out. Or grand. I may be back (with new requests) once I figure out what the hell I'm doing.


Well, fugitive would have to mess around with let files and call ctrlp#custom() otherwise, right? With a b:var, it wouldn't have to run another system(), while the git ls-files command would still be needed in any case.

The :CtrlP command started to take a path very early on, about a year ago today I think... Also, the fact that I mainly use git from console probably contributes to me not getting this. I don't have a clear idea of what :Gedit does, for example.


I guess I just felt tweaking the <C-p> map in a buffer was less invasive than tweaking, b:ctrlp_user_command, because the map wouldn't affect the user's use of :CtrlP etc. For example, if I set b:ctrlp_user_command to use git ls-files, and the use does :CtrlP /path/to/mecurial/repo, are they going to hate me? I have no idea.

:Gedit : opens a buffer containing the output of git status. It's got all sorts of maps for staging and diffing and committing, but that's stuff that I (like you) mostly use the command line for. What I do use it for frequently is jumping to changed files, as I can often navigate to where I want I want faster than my fingers can even remember the name of the file.

:Gedit's more general purpose is to edit commits and blobs and whatnot (:Gedit master~7:README.) It would be pretty cool to provide a ctrlp interface to that, but that would require some monumental changes to ctrlp.vim.


Then just do the usual saving & restoring for g:ctrlp_user_command, like this for example:

func! CustomUserCmd()
    if exists('g:ctrlp_user_command')
        let saved_user_command = g:ctrlp_user_command

    unl! g:ctrlp_user_command

    let g:ctrlp_user_command = ['.git', '...']
    CtrlP /tmp/repo/submodule

    unl! g:ctrlp_user_command

    if exists('saved_user_command')
        let g:ctrlp_user_command = saved_user_command

" Mapping for the status window
nn <buffer> <silent> <c-p> :call CustomUserCmd()<cr>

What do you think? It beats me having to modify ctrlp so that it takes options' values via function arguments, which I suspect very few people (if not nobody else) will probably ever need.

I'll still proceed with adding support for using b:var, though, since among other things, it'll allow people to (more easily) overrule the above behavior should they want to.


Only in VimL does that pass for anything other than an antipattern. :) But yeah, it's good enough for what I'm doing. A buffer local variable would still be nice.


Yeah, it's very often used to workaround problematic Vim's options or to create a more suitable enviroment. Like this particular one, for example:

let s:keepcpo = &cpo
set cpo&vim


let &cpo = s:keepcpo
unlet s:keepcpo

For the b:var change, I'll push it out soon after I can do some testing.

@kien kien added a commit that referenced this issue Sep 15, 2012
@kien Add b:var support for some options
Refs #273
@kien kien added a commit that referenced this issue Sep 15, 2012
@kien Support using a one-time value for some options
Refs #273

So I decided to bite the bullet and add support for overriding some options when calling ctrlp. Here's what the function call should look like:

call ctrlp#init(0, { 'dir': '...', 'opts': { 'user_command': ['.git', '...'] } })

If you find any problem with these changes, let me know. Thanks!

@kien kien closed this Sep 15, 2012
@raindev raindev added a commit to raindev/dotfiles that referenced this issue May 5, 2015
@raindev raindev Use only Git-tracked files for CtrlP search
The solution was found at kien/ctrlp.vim#273
@bwbaugh bwbaugh added a commit to bwbaugh/dotfiles that referenced this issue Feb 16, 2016
@bwbaugh bwbaugh Only list files tracked by git with ctrlp f5b5891
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment