Vim - LaTeX. How to conditionally open the PDF viewer? - vim

I'm currently trying to write my own setup for autocompiling and previewing LaTeX code using Vim and Zathura. This is the code I have currently have in my tex.vim file:
:map <F6> :w <bar> ! pdflatex -synctex=1 -interaction=nonstopmode %<CR><CR>
:map <F7> :! zathura $(echo % \| sed 's/tex$/pdf/') & disown<CR><CR>
This works fine as long as when I compile the code (from Vim) I am in the directory where my tex files are. However, I would like this to work regardless of my position (since I frequently open files from Vim using the :e command). How do I fix this?
Also, I would like Zathura to open only if the PDF preview is not already open. Does anyone have any suggestions as to how to do such a thing?

You can use :h filename-modifiers instead of your sed hack, so this should do:
nnoremap :<C-u>w <bar> ! pdflatex -synctex=1 -interaction=nonstopmode %:p<CR>
nnoremap :<C-u>! zathura %:p:r.pdf<CR>
I have also changed these mappings to use :nnoremap which you should prefer anyway.
If you want to expand your Vim-fu even further you can check :h compiler and :h compilet-tex which will handle setting proper 'makeprg' for you which will allow you to use :make to build your TeX files.

An alternative would be to simply use vimtex (https://github.com/lervag/vimtex)
Mapping: <localleader>lv

Related

VIM: why do these bindings work only sometimes?

I've got these bindings in my .vimrc. They work most of the time, but sometimes they don't: they will just save the file but not run it.
Then I go into insert mode and exit it back to normal mode, and they work again. What could be the problem?
Thanks!
autocmd FileType python map <C-k> :write <CR> :! python % <CR>
autocmd FileType lisp map <C-k> :write <CR> :! clisp % <CR>
autocmd FileType scala map <C-k> :write <CR> :! scala % <CR>
There are many topics to discuss here so with out further ado:
Mappings
Your current mappings map <c-k> ... will only work in normal, visual, and operator-pending modes. However executing your mappings in visual mode or operator-pending modes would save the buffer with only a range of lines (read not good). I suggest you make your mappings for normal mode only.
Two general rules of thumb:
Always supply a mode like n for normal.
Always use noremp instead of map unless you are mapping to a <Plug> mapping.
So one of your mappings might look something similar to this:
nnoremap <c-k> :w<cr>:!python %<cr>
For more information:
:h :map-modes
:h map-overview
:h :nore
:h map-listing
:h map-verbose
Filetype based mappings
You need to do 2 things:
Create a mapping local to a specific buffer by using the <buffer> option for noremap.
Load the mappings for just a specific filetype.
This can be done via an autocmd and FileType event in your .vimrc like so:
autocmd FileType python nnoremap <buffer> <c-k> :w<cr>:!python %:p<cr>
The other way option is by creating a filetype plugin. (see :h ftplugin for more details)
A simple example is do create a file named, ~/.vim/ftplugin/python.vim and place your mappings inside like so:
nnoremap <buffer> <c-k> :w<cr>:!python %:p<cr>
I personally lean more towards the ftplugin approach but having a everything in your .vimrc file can be nice.
For more help see:
:h :au
:h FileType
:h map-local
:h ftplugin
:make
A more vim like way of doing this would be to use :make. (Assuming you want to lint vs execute your current buffer)
:make will execute the 'makeprg'. It defaults to make which is great of C projects
After running :make the quickfix list will be contain any errors.
Set your compiler via the :compiler command.
Extra parameter can be passed like so :make foo-command
Current filename can be represented by %. e.g. :make %
Often people set do :complier/'makeprg' in side of ftplugins e.g. ~/.vim/ftplugin/perl.vim or autocmd's e.g. autocmd FileType perl compiler perl.
Fop more help see:
:h :make
:h 'makeprg'
:h :compiler
:h c_%
quickfix list
Use :cnext and :cprev to move between your errors.
:copen to open up the quickfix list in a window (:cclose to close)
:cwindow to open quickfix list window only if there are errors
May want to use better mappings for :cnext and friends. I suggest Tim Pope's unimpaired plugin
For more help see the following:
:h quickfix
:h :cnext
:h :cope
Alternatives to using :make
Just use <c-z> to suspend vim and run your build system. (Cons: loose out on the quickfix list)
Use :! to compile. (Same cons as suspending) e.g. :!make
Syntastic is a syntax checking system that checks files on save
Ale (Asynchronous Lint Engine) is a plugin for providing linting in NeoVim and Vim 8 while you edit your text files
Dispatch can be used to run things in the background. Great for test suites
May want to consider terminal multiplexers like tmux or screen.
SingleComplile tries and takes some of the work out of using :make
Conclusion
Personally I would install ALE as this removes the need for your mappings. It is also a great idea to learn how to use the location (or quickfix) list. When you are ready to get your hands dirty then you can learn and use :make.
tl;dr
Install ALE.
The space inserted between <CR> and : could lead to errors, because this space is really entered during the execution of the mapping.
With :noremap or :nnoremap (which should be used most of the times, as Peter Rincker wrote in his answer), it wouldn't be a big problem, since it only would move the cursor one char to the right (see :h <space>).
But with :map, it could trigger a custom mapping (or a partial one).
Then, instead of:
autocmd FileType python map <C-k> :write <CR> :! python % <CR>
the following would be better:
autocmd FileType python map <C-k> :write <CR>:! python % <CR>
and this one, really better:
autocmd FileType python nnoremap <C-k> :write <CR>:! python % <CR>

Vim ftplugin executes c.vim instead of cpp.vim for C++ files

I am learing Vim and I want have set it up as IDE by mapping F5 key to compilation command which is decided by the filetype.
My ~/.vim/ftplugin/c.vim:
map <F5> :w<CR>:!clang % -o %:r.out && ./%:r.out<CR>
My ~/.vim/ftplugin/cpp.vim:
map <F5> :w<CR>:!clang++ -ggdb -pthread -std=c++11 % -o %:r.out && ./%:r.out<CR>
When I open a C++ file (with .cpp extension) and hit F5, the command from c.vim is executed. Why is this happening?
When I remove the file c.vim, then Vim loads cpp.vim and works as expected.
The cpp ftplugin that comes with vim has the following line:
runtime! ftplugin/c.vim ftplugin/c_*.vim ftplugin/c/*.vim
Which means it is going to source the ~/.vim/ftplugin/c.vim.
A way to overcome this is to put your mappings in ~/.vim/after/ftplugin/cpp.vim and ~/.vim/after/ftplugin/c.vim files.
But your problems don't stop there:
You are using :map where you probably want to at least supply a mode. nmap
You also should probably be using noremap so it would become nnoremap
Your mapping is global so it will stomp over your <f5> everytime you open a cpp and switch to a c file. You should make it local to the specific buffer. nnoremap <buffer>
You should probably also be setting makeprg accordingly so you can take advantage of :make and the quickfix list. e.g. setlocal makprg clang\ %\ -o\ %:r.out
For more help see:
:h :map
:h :map-local
:h 'makeprg'
:h :make
:h quickfix
If you move your file into the /after/ftplugin/ folder, your C specific instructions will still be executed on your C++ files. But your C++ instructions will be executed after the C ones.
If you want to avoid this behaviour, add this at the beginning of your ~/.vim/ftplugin/c.vim file
if (&ft != 'c')
finish
endif

vim conditional keymap when a tex file is open

I am trying to make a keymap that will call latexmk when .tex is available (it would be better if .tex is the currently open and active buffer)
I tried :
:nnoremap <Leader>lw :if filereadable('*.tex')<cr>up!<cr>:!latexmk -pdf<cr>endif<cr>
when trying to make latexmk -pdf run, but unfortunately, its just prompting those line in the window, and doing nothing like:
~
:if filereadable('*.tex')
: up!
: :call Tex_RunLaTeX()
: endif
Press ENTER or type command to continue
kindly help.
(it will be great, as told, if this can be done when .tex is the currently open and active buffer.)
NB: this question and its variant has been asked here several time here eg this and this, hence sorry for the repetation. I have failed to solve my problem with those.
You need to do 3 things:
fix your mapping to run the command properly
create a mapping local to a specific buffer by using the <buffer> option for nnoremap.
load the mappings for just a specific filetype.
First fix the mapping by using executing the command as single ex command by using <bar> and removing :'s & <cr>'s. We also remove the filereadable portion because we just wrote the file.
nnoremap <buffer> :up!<bar>!latexmk -pdf<cr>
or you can use an expressing mapping like FDinoff suggested.
Note the <buffer> option on the mapping. This makes the mapping only available to the current buffer not every buffer.
Now we need to make sure this mapping only works for tex filetypes. This can be done via an autocommand in your .vimrc like so:
autocmd FileType tex nnoremap <buffer> :up!<bar>!latexmk -pdf<cr>
The other way option is by creating a filetype plugin. (see :h ftplugin for more details)
A simple example is do create a file named, ~/.vim/ftplugin/text.vim and place your mappings inside like so:
nnoremap <buffer> :up!<bar>!latexmk -pdf<cr>
I personally lean more towards the ftplugin approach but having a everything in your .vimrc file can be nice.
I feel like this could be done with an autocmd.
The autocmd only loads the mapping when the file is a tex file.
autocmd FileType tex nnoremap <leader>lw :up! \| !latexmk -pdf<CR>
If you want to do this filereadable('*.tex') which just checks to see if a file in the directory is a tex file. You could use the expr mapping from the first link. In the else part of the expression we just put an empty string so the mapping will do nothing.
nnoremap <expr> <leader>lw filereadable('*.txt') ? ':up! \| !latexmk -pdf<CR>' : ''

I want to update the file (if necessary) and run the system command on current file

I realize that I can :nmap <leader>rc :!cat %<CR> to provide an easy set of triggers, but I would like to do this instead.
nmap <leader>rc :up :!cat %<CR> but it complains about needing only one filename. How do I get vim to recognize both commands, in series?
You are missing a <CR> after :up. <CR> tells vim you want a carriage return here.
nmap <leader>rc :up<CR> :!cat %<CR>
The reason up is complaining about multiple file names is that it sees :!cat and %<CR> as two arguments to up.
So the new macro executes
:up
:!cat %
instead of
:up :!cat %
(Side Note: you should probably use nnoremap instead of nmap)
ZyX recommends using the following mapping instead.
nnoremap ,rc :up\|execute "!cat" shellescape(#%, 1)<CR>
This uses | to separate commands and escapes the %. Escaping the % leads to a more robust mapping just incase the filename contains special characters.
Help for :h execute and :h shellescape

How to open pdf files under cursor (using 'gf') with external PDF readers in vim

The current gf command will open *.pdf files as ascii text. I want the pdf file opened with external tools (like okular, foxitreader, etc.). I tried to use autocmd to achieve it like this:
au BufReadCmd *.pdf silent !FoxitReader % & "open file under cursor with FoxitReader
au BufEnter *.pdf <Ctrl-O> "since we do not really open the file, go back to the previous buffer
However, the second autocmd failed to work as expected. I could not figure out a way to execute <Ctrl-o> command in a autocmd way.
Could anyone give me a hint on how to <Ctrl-O> in autocmd, or just directly suggest a better way to open pdf files with gf?
Thanks.
That's because what follows an autocmd is an ex command (the ones beginning
with a colon). To simulate the execution of a normal mode command, use the
:normal command. The problem is that you can't pass a <C-O> (and not
<Ctrl-O>) directly to :normal, it will be taken as literal characters (<,
then C, then r) which is not a very meaningful normal command. You have two
options:
1.Insert a literal ^O Character
Use controlvcontrolo to get one:
au BufEnter *.pdf normal! ^O
2.Use :execute to Build Your Command
This way you can get a more readable result with the escaped sequence:
au BufEnter *.pdf exe "normal! \<c-o>"
Anyway, this is not the most appropriate command. <C-O> just jumps to the
previous location in the jump list, so your buffer remains opened. I would do
something like:
au BufEnter *.pdf bdelete
Instead. Still I have another solution for you.
Create another command with a map, say gO. Then use your PDF reader
directly, or a utility like open if you're in MacOS X or Darwin (not sure if
other Unix systems have it, and how it's called). It's just like double clicking
the icon of the file passed as argument, so it will open your default PDF reader
or any other application configured to open any file by default, like images or
so.
:nnoremap gO :!open <cfile><CR>
This <cfile> will be expanded to the file under the cursor. So if you want to
open the file in Vim, use gf. If you want to open it with the default
application, use gO.
If you don't have this command or prefer a PDF-only solution, create a map to
your preferred command:
:nnoremap gO :!FoxitReader <cfile> &<CR>
If the default app is acceptable, then simply using :!open % in command mode works. You can always map this to a suitable leader combination in your vim config file etc.
If you want something that works with normal mode, then you could try something like the following (i use this too for opening HTML files), and modify to your own needs:
if has('win32') || has ('win64')
autocmd FileType html nmap <Leader>g :silent ! start firefox "%"<cr>
elseif has('mac')
autocmd FileType html nmap <Leader>g :!open "%"<cr><cr>
endif

Resources