2

I am trying to define a keymapping in vimrc to compile my markdown file with Pandoc. So I am using this:

autocmd FileType markdown nmap <F9> :!pandoc % -o %:r.pdf <cr>

But, in markdown file when I am pressing F9 nothing is happening. I am not sure if this is the line I need to add in my vimrc file. Right now I don't want to install any plugins like vim-pandoc or something like that.

  • You may need the :S filename modifier, to prevent some special characters in the filename to interfere. Do one of these work?: autocmd FileType markdown nnoremap <F9> :<c-u>!pandoc %:p:S -o %:p:r:S.pdf<cr>, or autocmd FileType markdown nnoremap <F9> :<c-u>sil call system('pandoc '.expand('%:p:S').' -o '.expand('%:p:r:S').'.pdf')<cr> – user938271 Oct 2 '18 at 11:32
  • yes!, I checked both and it works. Thanks a lot. – Galilean Oct 2 '18 at 11:38
  • 2
    To create your mapping properly you really should add <buffer> to your mapping command (see :h :map-<buffer>) this way your mapping will only exist in the appropriate buffers (with the markdown filetype). You also should read :h ftplugin: don't use an autocommand to create the mapping, use a ftplugin, they are made for that :) – statox Oct 2 '18 at 11:42
5

Your mapping has a few issues:

autocmd FileType markdown nmap <F9> :!pandoc % -o %:r.pdf <cr>

You use a recursive command :nmap, but you don't need recursiveness, so you should prefer :nnoremap:

autocmd FileType markdown nnoremap <F9> :!pandoc % -o %:r.pdf <cr>
                          ^^^^^^^^

There's an unneeded space at the end:

autocmd FileType markdown nnoremap <F9> :!pandoc % -o %:r.pdf <cr>
                                                             ^

You use the :! bang command, without prefixing it with the keycode <c-u> (see :h c^u).
As a result, if you press a count before <F9>, it will be automatically translated into a range.
And a range before :! means that Vim will filter some lines in your buffer (see :h :range!), which you probably don't want here.

autocmd FileType markdown nnoremap <F9> :<c-u>!pandoc % -o %:r.pdf<cr>
                                         ^^^^^

Your filename may contain some special characters which are automatically expanded on Vim's (see :h cmdline-special) or the shell's command-line.

To prevent Vim and the shell from interpreting them, you could use the :S filename modifier (see :h filename-modifiers), and just to be safe you could also add the :p modifier so that the paths passed to $ pandoc are absolute:

autocmd FileType markdown nnoremap <F9> :<c-u>!pandoc %:p:S -o %:p:r:S.pdf<cr>
                                                       ^^^^     ^^  ^^

Note that if you use multiple filename modifiers, :S must be the last one.


:! is good for an interactive use, but usually in a script you use system().

autocmd FileType markdown nnoremap <F9> :<c-u>silent call system('pandoc '.expand('%:p:S').' -o '.expand('%:p:r:S').'.pdf')<cr>

In this form, Vim doesn't expand the special character % so you have to invoke expand() (see :h expand()).
The purpose of :silent is to avoid stray characters showing up on your screen (see :h system()):

When prepended by |:silent| the terminal will not be set to cooked mode. This is meant to be used for commands that do not need the user to type. It avoids stray characters showing up on the screen which require |CTRL-L| to remove.


You should wrap your autocmds into a self-clearing augroup so that they aren't duplicated if you resource your vimrc (see :h autocmd-remove):

augroup my_markdown
    autocmd!
    autocmd FileType markdown nnoremap <F9> :<c-u>silent call system('pandoc '.expand('%:p:S').' -o '.expand('%:p:r:S').'.pdf')<cr>
augroup END

As @statox explained, your mapping is global, but you probably want it only in a markdown buffer, so you should pass the <buffer> argument to :nnoremap.

Also, autocmds in your vimrc don't scale well passed a certain point, so you could use a filetype plugin. For markdown, you could use the file ~/.vim/after/ftplugin/markdown.vim.

Note that by using a filetype plugin you don't need an autocmd anymore.

And in addition to <buffer>, you probably want to pass the arguments <nowait> and <silent> to :nnoremap.

nnoremap <buffer><nowait><silent> <F9> :<c-u>silent call system('pandoc '.expand('%:p:S').' -o '.expand('%:p:r:S').'.pdf')<cr>
                 ^^^^^^^^^^^^^^^^

<silent> is to prevent Vim from displaying the rhs of your mapping on the command-line when you press F9.
<nowait> is to prevent Vim from waiting a few seconds if another mapping begin with F9. Usually, when that happens, it's a conflict between a local and global mapping, and you probably want to give the priority to the local one.


In your filetype plugin, you could tell Vim to remove the mapping if you change the filetype of your buffer by including a :nunmap command in the buffer-local variable b:undo_ftplugin (see :h undo_ftplugin):

let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
    \ . (empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
    \ . "
    \       exe 'nunmap <buffer> <F9>'
    \ "
  • 3
    Just to add to this great answer: the last step to really improve this mapping is to get rid of it. First define the makeprg to use pandoc properly for markdown and then optionnally create a mapping which calls make (this mapping could be global since makeprg should be changed for each filetype. (:h 'makeprg') – statox Oct 2 '18 at 12:24
  • 1
    Note that repeated string concatenation can be made a little clearer with printf (it may be sprintf—one of these should return a string). – D. Ben Knoble Oct 2 '18 at 16:38

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.