4

I'm trying to set up an autocommand so that new, unnamed buffers are created with ft=markdown. Someone on SO suggested the following:

autocmd BufEnter * if &filetype == "" | setlocal ft=markdown | endif

which works great, but with which I have the following gripes:

  1. this will set ft=markdown on named buffers, too (e.g., :e foo), and
  2. this will run every time I switch to (enter) the buffer, not just when it's first created — not a problem in practice, but not what I want to do in principle.

I've been working through the problem myself, but I can only get halfway there.

Success: the right autocmd {pat}

I've figured out that I can specify unnamed buffers by using a pair of empty curly braces ({}) in place of a filename pattern. That is,

autocmd VimEnter {} setlocal ft=markdown

will run on $ vim, but not on $ vim foo.

Failure: the right autocmd {event}

So now I'm looking for the right autocmd event: one that is triggered on :enew or any other time a blank, unnamed buffer is added to the buffer list. I've tried:

  • BufAdd
  • BufCreate
  • BufNew

but none of these are triggered on :enew. I've tested this with the following autocommands:

autocmd BufAdd    * let b:foo = 'foo'
autocmd BufCreate * let b:foo = 'foo'
autocmd BufNew    * let b:foo = 'foo'

When I edit an existing file, :echo exists('b:foo') returns 1. When I :enew, it returns 0.

(To be clear, BufEnter still works, but I'm looking for one that only runs once.)

Is there an appropriate autocmd here, or should I file a bug?

0

1 Answer 1

2

At first I was thinking of listening for BufNewFile, but this is not the right event. My mistake.

I had the confirmation thanks to a plugin given in another answer: http://vi.stackexchange.com/questions/4493/what-is-the-order-of-winenter-bufenter-bufread-syntax-filetype-events

->

- BufNew -- 
- BufAdd -- 
- BufCreate -- 
- BufLeave -- /tmp/vim_log_autocommands
- BufEnter -- 
- BufWinEnter -- 

So, I've tested with your events:

aug Test
  au!
  au BufAdd    * :echom "Add ".expand('<amatch>')."--".bufnr('%') | let b:test = 1
  au BufCreate * :echom "Create ".expand('<amatch>')."--".bufnr('%') | let b:test = 1
  au BufNew    * :echom "New ".expand('<amatch>')."--".bufnr('%') | let b:test = 1
aug END

If you look closely, we see, first that the events are correctly triggered, and then that the buffer created isn't bufnr('%'). Hence <amatch> that is the proper way to proceed in many autocommands. This is hinted in the documentation:

NOTE: When this autocommand is executed, the current buffer % may be different from the buffer being created <afile>.

If you go back to the buffer (corresponding to the number printed), you'll see that b:test is set to 1 over there. Actually you can expect the number of the new buffer to be bufnr('$')+1. Still, I wouldn't use setbufvar() as getbufinfo(bufnr('%')+1) is still empty.

That's why in the other answer, you were pointing to, they said to listen for BufEnter I guess. Instead, you'll have to mark the buffers you've already tested.

  • Either you use an internal variable at your script level

    let s:been_there = {}
    aug FTOverride
      au! 
      " untested
      au BufEnter * if empty(&ft) && !get(s:been_there, bufnr('%'), 0) | set ft=markdown | let s:been_there[bufnr('%')] = 1 | endif
    aug END
    
  • Or you use a buffer variable which will be visible by everyone

    ...
      " untested
      au BufEnter * if empty(&ft) && !get(b:, 'ft_overridden', 0) | set ft=markdown | let b:ft_overridden = 1 | endif
    

EDIT2: In a personal plugin I'm maintaining, I've found another way to proceed. This time I listen for the root events. When triggered, I register an one-time-autocommand on BufEnter:

augroup auto_jump_to_line
  au!
  au BufNewFile,BufWinEnter *:* nested
        \ call lh#event#register_for_one_execution_at('BufEnter', function(s:getSNR('FixFilename')), 'auto_jump_to_line_1', expand('<afile>:t'))
augroup END

Where lh#event#register_for_one_execution_at() registers an autocommand that'll start by un-registering itself. I haven't checked in your case, but it may work. But choose only one autocommand among the 3 you've found.

5
  • Am I missing something? BufNewFile is only triggered when "starting to edit a file that doesn't exist" (as in :e foo but not :enew). Like the events mentioned above, autocmd BufNewFile * let b:foo = 'foo' does not set the b:foo variable on :enew — at least in my experimentation. Is it different for you?
    – Ryan Lue
    Aug 8, 2017 at 9:22
  • My mistake. I've though the events weren't triggered, while they do are. The problem is that the current buffer is not yet the buffer you've just created. I've completely rewritten my answer. Aug 8, 2017 at 11:28
  • Whoa! Saw much of that in the helpdocs, but just wasn't getting it. Awesome explanation; definitely learned something new today.
    – Ryan Lue
    Aug 8, 2017 at 13:52
  • 1
    @RyanLue, there be another way, see my new EDIT. Aug 9, 2017 at 15:26
  • that's a mouthful, but very clever!
    – Ryan Lue
    Aug 9, 2017 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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