When I'm in a python file and I go to the imports, put the cursor on one of them, and type gf, it opens the corresponding file in a new buffer in the same window, even if the file is already open in another tab/split.

Is there any way to make it behave like the :drop command, i.e., to make it go to the window in which the file is already open instead of editing it in the current window? Ideally, it would default to opening the file in a new tab if the file is not already open.


One option would be the other gf commands: <C-w>f opens in a new split and <C-w>gf in a new tab.

You could try to :set switchbuf=useopen and see if that helps.

Lastly, you could use an ftplugin to map gf to :drop as follows:

" ~/.vim/after/ftplugin/python.vim
nnoremap <buffer> gf :execute 'drop' expand('<cfile>').'.py'<CR>

(If you go for this route, you may want to adjust b:undo_ftplugin as well.)

This last solution does suffer from the fact that :drop does not open in a new tab if the buffer doesn't exist (it uses the current window). You could fix this with

nnoremap <buffer> gf :execute (bufloaded(expand('<cfile>').'.py') ? 'drop' : 'tabedit') expand('<cfile>').'.py'<CR>

There are further caveats to either mapping solution:

  • expand('<cfile>') is not an exact replica of the filename-resolution system used by gf, which is far more intelligent (it involves using include/includeexpr and suffixesadd, at the least). We can use .'.py' or .&l:suffixesadd as a workaround, but it’s not perfect
  • I made the mappings buffer-local since I put them in an ftplugin. In general I recommend not overriding vim’s builtins unless it’s strictly a superset of the behavior, but YMMV.
  • if you consider tab pages to be more like window layouts, it may be more advantageous to envision a workflow where you are ok with having the same file open in multiple windows across multiple tab pages. I may have a tab for the main entry points of a project, a tab for each of successive stages of a processing pipeline, etc. Each of those tabs may have different or the same files; sometimes, I have the same file in multiple windows just for different views! This depends on the complexity of the project though, as for many projects I don't need more than 2 or 3 windows and am often only in 1 or 2.
  • Thank you for your answer! Just a couple of things to add. I had originally thought that the second nnoremap for gf would not work, because the imports don't have .py extensions, but the corresponding file does, so the command would edit a new file instead of the correct existing one. But the help page for suffixesadd seems to indicate that it's the right option. How might I add that to the mapping you provided? – Thaddaeus Markle Jul 28 '20 at 1:41
  • 1
    @ThaddaeusMarkle crud, it’s tabedit. Edited. (Obviously I don’t use tabs much.) as for suffixesadd, you just need to make sure it’s set for the python filetype (using setlocal). It may already be set, which you can check by opening a python file and running :setlocal suffixesadd? – D. Ben Knoble Jul 28 '20 at 1:54
  • 1
    Here's the finished mapping: nnoremap <silent><buffer> gf :execute (bufloaded(expand('<cfile>' . '.py')) ? 'drop' : 'tabedit') expand('<cfile>') . '.py'<CR>. You'll notice that I just concatenated '.py' onto the strings instead of the contents of the &suffixesadd option, mainly because I know that this is only for python and the extension will only ever by '.py'. Also, I added <silent> as I always do for mappings that run a command (faster and less visual noise). I will accept your answer, and I would appreciate it if you would edit it to include the new mapping, just for posterity's sake. – Thaddaeus Markle Jul 28 '20 at 2:35
  • 1
    @ThaddaeusMarkle youre welcome! this meta SO is a good starting point. We’re not bound by meta.so (we have our own meta and can make our own policies), but I haven’t heard good reasons to change this one yet. Personally, I think code edits can often be addressed with a quick comment or a new answer, and most of the code edits I’ve seen do change something. I’ve had pushback before when the change concerned correcting a typo in the code, which I rejected and left a note for the OP to fix their code... – D. Ben Knoble Jul 29 '20 at 0:38
  • 1
    @ThaddaeusMarkle you’re correct; split(&l:suffixesadd, ',')[0] gets closer, but they’re all fallible. That’s why I don’t really recommend the mapping :) – D. Ben Knoble Jul 29 '20 at 15:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.