4

I have a couple of autocmds in my vim config along the lines of this (the actual commands are quite long):

    au BufWritePost *.latex.md  silent! ...
    au BufWritePost *.md  silent! ...

where writing files with .md or .latex.md extensions runs pandoc to convert my markdown files into the corresponding format. The problem I have (which you have probably guessed by looking at the example above) is that globbing for *.md subsumes files with a .latex.md extension. This means that files with that extension produce both HTML and a PDF, which is unnecessary. Is there any way to make the two autocommands mutually exlusive, or do I need to create a command or function that runs the correct command instead?

myc3lium
  • 95
  • 3

2 Answers2

3

You can examine a match and then decide what to do. Whether to use multiple autocommands or "bar-newline-backslash" is a matter of style.

augroup mywritepost | au!
    autocmd BufWritePost *.md if expand("<afile>") =~? '\.latex\.md$'
    autocmd BufWritePost *.md     call s:makepdf()
    autocmd BufWritePost *.md else
    autocmd BufWritePost *.md     call s:makehtml()
    autocmd BufWritePost *.md endif
augroup end
Matt
  • 20,685
  • 1
  • 11
  • 24
1

There's more to the autocmd pattern than *. Using other wildcards should give more flexibility in crafting patterns that don't overlap.

The problem is that the pattern matching for autocmd is not standard/vim regex and it's not shell-like globbing. It's a modified version of vim regex. Example: . represents a period not "any character except newline". :h file-pattern gives the details. From there we can try to come up with appropriate translations from standard to modified regex.

At first I thought the translation rules were too restrictive to allow use of some of the more advanced regex atoms (e.g lookahead/lookbehind). But a comment from @user938271 made me rethink that and after some testing we come to:

autocmd BufWritePost *.latex.md ...
autocmd BufWritePost *{.latex}\@<!.md ...

The negative lookbehind should ensure that the two autocmds are mutually exclusive (only one will be triggered for a given filename).

So, all I had to do was replace \(...\) with {...}. Overthinking made this much harder than it should have been.

B Layer
  • 19,834
  • 2
  • 30
  • 57
  • Looks like this is probably the most concise way to handle the problem. Thanks! – myc3lium Dec 13 '19 at 16:45
  • 1
    @BLayer Honestly, your regexp is too dirty (e.g. "foo.md" does not match at all), but that is not me who voted it down. – Matt Dec 13 '19 at 16:59
  • 1
    @BLayer Alas, It is even worse, as even simple lemon.md will not match here either. – Matt Dec 13 '19 at 17:31
  • @BLayer That cannot be helped. The problem requires a negative condition, while file-pattern has only "negative" [^x]. And you can't make "not" from "and" and "or" - the Boolean algebra for freshmen. – Matt Dec 13 '19 at 17:41
  • @BLayer I mean "freshman" = "first year (University) student (math)". – Matt Dec 13 '19 at 17:49
  • 1
    Not sure it helps but you can use a negative lookbehind inside an autocmd file pattern: It is possible to use |pattern| items, but they may not work as expected, because of the translation done for the above. So for example, you could try: au BufWritePost *\(.latex\)\@<!.md ... – user938271 Dec 14 '19 at 08:33
  • @user938271 The pattern you mention won't work because these are not standard regexes. There are certain changes made just like what you quoted says ("the translation"). However! You opened my eyes up to the fact that I might be able to translate some of the more advanced atoms like lookahead/lookbehind...something I didn't think was possible. Thanks! – B Layer Dec 14 '19 at 09:32
  • 2
    FWIW, it seems to work on my machine (https://0x0.st/zUnI.txt). For the file /tmp/foo.latex.md, only the first autocmd is triggered. And for the file /tmp/foo.bar.md, only the second autocmd is triggered. Then again, maybe I misunderstood something. As for the issue about the translation, I don't think \(.latex\)\@<! is affected. The only character which should be translated is the dot, which in a usual regex stands for any character except the newline, while here it stands for a literal dot. But it's true that in general you have to be careful about which characters you use. – user938271 Dec 14 '19 at 10:01
  • @user938271 Check out my updated answer. I translated the \(...\) based on what I read. But maybe even that was unnecessary. I'll have to look closer. Regardless, you saved my bacon. Thanks again. – B Layer Dec 14 '19 at 10:09
  • 1
    I checked your code and it works on my machine. – user938271 Dec 14 '19 at 10:15
  • Awesome. Appreciate it. – B Layer Dec 14 '19 at 10:15