2

I am looking for a way to easily compile whole latex projects, based in several files through the following set of folders:

.
|- MyProjectsMainFile.tex
|- img/
|  |- image1.png
|  |- image2.jpg
|  `- etc.whatever
`- tex/
   |- part1.tex
   |- part2.tex
   `- conclusion.tex

The tricky bit is to do this even if I am editing a file in the tex folder.

I will explain a bit my usage first: In larger projects I use this structure, and I reference pictures using \includegraphics{./img/name} and insert code from the other .tex files by \input{.tex/file.tex}, as is the way to go for modular documents.

Up until now, this structure has only been needed in projects I have made with other people, and my own documents have always been so small that I didn't need the tex folder. For this reason, while using Vim to write my own stuff, I compiled using the following in my .vimrc:

" LaTeX macros for compiling and viewing
augroup latex_macros " {
    autocmd!
    autocmd FileType tex :nnoremap <leader>c :w<CR>:!pdflatex %<CR>
    autocmd FileType tex :nnoremap <leader>v :!open %:r.pdf &<CR><CR>
augroup END " }

<leader> c to compile, <leader> v to view. This works well until you start going down into the folders, and the file that needs to be compiled is not the one you are working on.

I don't rely on any LaTeX-extensions, and I would like to keep things that way for the most part (I use Ultisnips as my tab-complete tool with quite personalized snippets, and I don't want any extension to mess with my clearly superior completions!). I know there are extension out there that take care of this worry, but I don't want to a whole world of stuff just because of this little thing.

A few assumptions that can be used as starting point:

  • I will always either be in the very file to be compiled, or in a file that is immediately inside tex/. That is to say, MyProjectsMainFile.tex will always either be in . or ...

  • MyProjectsMainFile.tex will be the only .tex file in my project main folder.

  • I prefer using the extension .tex for the files in the tex/ folder as well.

It might be sufficient to include som comment at the top of every inserted file, and improve the above macro to check for this line and create a special case (I have seen extensions using this solution). I wouldn't know how to do that in Vimscript though, and perhaps there are better ways of solving this problem.

  • 3
    You don't want plugins, but vimtex seems to fit the bill - it doesn't mess with snippets, and it provides multiple ways to specify the master tex file. – muru Apr 16 '16 at 11:09
5

I would say that you are approaching this the wrong way. If you want to use a very minimalistic solution without any plugins, I would claim that you should not have anything in your vimrc file for compiling. Instead, you should use latexmk. This is a very good automatic compilation tool for LaTeX, which will compile as many times as necessary, including bibliography files and similar. It is very customizible, for those who need that. In addition, with the option -pvc, it can run in the background and compile your document whenever any relevant files change (including e.g. graphic files). Thus, my suggested solution is to simply keep a terminal open at the root of your project, and to run latexmk -pvc MyMainFile.tex. This will allow you to keep your vimrc file clean, and to work on your LaTeX document without worrying about compiling.

However, I will also suggest that my plugin, vimtex, might suit your needs. It does not mingle with your other plugins, and it provides e.g. the ability to compile your document through latexmk with the <leader>ll mapping (continuous mode by default, but this can be changed). It also provides several other features that I think are quite nice, e.g. toggling of math delimiter modifiers (for example \left ... \right pairs).

  • Thanks! I will look into this for sure, if my snippets will remain untouched. It seems a bit strange though, and I am unnerved by the thought of not being in control of when my document is compiled, and for that reason less certain of when errors pop up. But I am sure these things are dealt with just fine by the extension. – Frode TB Apr 18 '16 at 6:53
  • I can only say that I try to deal with these things, and that I think I have dealt with a lot of them in a good manner. However, I also actively maintain the project, and I try to solve issues as they are raised. Note, though, that you can use vimtex also with single shot compilation, but this will not provide a direct feedback when the compilation is completed, unless you let it compile in the foreground. Don't hesitate to ask if there is anything you're wondering about. – Karl Yngve Lervåg Apr 18 '16 at 8:54
2

Under the additional assumptions that there is always exactly one .tex file in your project's root directory, the following should work:

function! GetProjectRoot()
  if expand("%:p:h:t") == "tex"
    let l:dir=split(system("ls .."), "\n")
    return fnamemodify("../" . filter(l:dir, "v:val =~ \".*\.tex\"")[0],  ":p")
  else
    return expand("%:p")
  endif
endfunction

This seems still pretty hackish, but lacking experience, this is all I could do in short time.

Now you can adjust your maps to look like this:

nnoremap <leader>v :execute ":!open " . fnamemodify(GetProjectRoot(), ":r") . ".pdf &"<CR>
nnoremap <leader>c :w<CR>:!pdflatex GetProjectRoot()<CR>
  • That's not an additional assumption, it's the second bullet point. :) – muru Apr 16 '16 at 12:32
  • I was referring to the fact that it might be missing, which would result in the list being empty, I just wanted to be on the safe side ;) – Ingo Apr 16 '16 at 12:34
  • Ah, yes. I should check that too. But I'm throwing an exception anyway, so … – muru Apr 16 '16 at 12:35
2

Given the conditions in the question, you could use this function to determine the main TeX file:

function! GetMainTeX()
    " Start with the current file
    let l:main_file = expand('%')
    " If it's in a tex folder
    if expand('%:p:h:t') == 'tex'
        " Look for files in parent folder
        let l:files = glob(expand('%:p:h:h') . '/*.tex', 0, 1)
        " There should be only one such file
        if len(l:files) > 1
            throw "Multiple files found"
        endif
        let l:main_file = l:files[0] 
    endif
    return l:main_file
endfunction 

The glob function can be used to look for files matching wildcard patterns. If your main file's name is fixed, you could also use the findfile function instead:

findfile({name}[, {path}[, {count}]])                           findfile()
                Just like finddir(), but find a file instead of a directory.
                Uses 'suffixesadd'.
                Example: 
                        :echo findfile("tags.vim", ".;")
               Searches from the directory of the current file upwards until
                it finds the file "tags.vim".

So, something like the following could replace the entire function written above:

findfile('main.tex', '.;')

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.