1

I often nest one programming language within another (e.g. HTML inside python). This can be done per-language, and written items in ~/.vim/after/syntax/ for that purpose, but I don't have the patience to implement it for every combination I use. This means I press :seUpUp a lot.

One way to script this would simply be

nmap <F12> :set syn=<up><up><cr>

This almost works. The problem is that it's not literal enough; vim doesn't actually put this command in my history, so there's no "toggling" going on. (Actually, this is preferable; I don't want to pollute my history with all of this toggling.)

I'm pretty sure what I need is a custom function that searches my history for the most recent syntax specification (excluding the current syntax) and sets it. I know how to bind a function to a key, but I do not know how to implement the needed vimscript function.

Could somebody provide me some pointers here? Searching the web for this sort of thing merely turns up tricks for searching the command history manually (without vimscript).

2 Answers 2

2

Searching the web for this sort of thing merely turns up tricks for searching the command history manually (without vimscript).

I believe you're overthinking something. In Vim "key macros" are the "first-class citizens". They are stable and repeatable (if written well). There's no need to turn them into the "true" VimScript statements other than some minor style and performance issues.

So searching over the command-window is quite doable:

nnoremap <F12> q:2?^set syn=<CR><CR>

That is, open cmdwindow, backward find the 2nd occurence of the pattern, and then execute it. You may also want to set lazyredraw to reduce flickering. Also, make sure to use nnoremap instead of nmap, so all the keystrokes on the right-hand side have the default meaning.

UPD. Now suppose we want the following: find the last set syntax=xxx command except the currently active syntax. Then this regexp could be used:

nnoremap <silent><F12> q::call search('^set\s\+syn\(tax\)\?\s*=\(' .
    \ getwinvar(winnr("#"), "&syntax", "nosyntax") . '\)\@!', "b")<CR><CR>
  1. q: --- Open the command window
  2. :call search(..., "b")<CR> --- backward find the pattern
  3. <CR> --- execute the line in the command window

The regexp:

  1. ^set --- the "set" command at the beginning of the line
  2. \s\+ --- followed by at least one space
  3. syn\(tax\)\? --- "syn" or "syntax"
  4. \s*= --- optional spaces followed by the equal sign
  5. '\(' . getwinvar(...) . '\)' --- the current syntax value
  6. \@! --- negative match of the previous atom (i.e. any other syntax value)

Also to note: getwinvar(winnr("#"), "&syntax", "nosyntax") returns &syntax value of the previous active window/buffer, because it's the command window an active one at this point; "nosyntax" is simply a fake value used in case there is no syntax set at all.

6
  • 1
    macros are also VimScript. Sep 14, 2019 at 7:48
  • 1
    @ChristianBrabandt Well, this is the OP trying to differentiate between the parts of Vim scripting. Yet as he still has some point in it, I'm trying to stick to this "terminology".
    – Matt
    Sep 14, 2019 at 8:52
  • @‌Matt & @ChristianBrabandt – Point taken. My "terminology" was simply referring to vimscript as a function versus a (functionless) configuration-style macro.
    – Adam Katz
    Sep 14, 2019 at 15:59
  • This is close and likely usable for me, but I'd love to be able to prevent off-by-one issues (like running :set syn=html when HTML syntax is already in effect).
    – Adam Katz
    Sep 14, 2019 at 16:09
  • @AdamKatz I updated the answer.
    – Matt
    Sep 15, 2019 at 6:41
1

I believe this happens, because you are executing a normal mode command that resolves to a ex command but that is not put into the history as you noted. Now you could throw into an extra histadd() call, but I believe this is more or less a hack.

I would suggest a different, more robust approach, that does not depend on the order of :set syn= commands you issued previously. Basically, check the current syntax value and toggle depending on its value:

:nnoremap <f12> :set syntax=<c-r>=&syntax=='val1'?'val2':'val1'<cr><cr>

what this does is it exexutes the :set syntax= command and returns the value from evaluating the expression (<c-r>=, see :h c_CTRL-R-=):

 &syntax=='val1'?'val2':'val1'. 

This checks the value of the current syntax option, whether it is val1 and will return val2 in that case, otherwise it returns val1. This return value will then be used for the :set syntax command.

Note, you need two carriage returns (<cr>), because the first finishes the c_CTRL-R-= command while the second executes the resulting :set syntax command.

3
  • 1
    I'd rather do :let &syntax = (&syntax ==# 'val1' ? 'val2' : 'val1')<CR>, although it's a matter of taste. But the real problem is that the OP may be using more than two syntaxes (maybe in different files). At least, that's how I understand his sentence about "nesting programming languages".
    – Matt
    Sep 14, 2019 at 8:58
  • ah yes of course this is also possible Sep 14, 2019 at 15:26
  • Right, I don't always toggle between two specific languages. There are unfortunately a lot of languages that I have to deal with in this fashion, otherwise I'd just write syntax swapping triggers in ~/.vim/after/syntax/ as noted in my question (I've done this for my more common nested syntax needs). This means I don't want to hard-code 'val1' and 'val2'.
    – Adam Katz
    Sep 14, 2019 at 16:08

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.