Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim action inside LaTeX environment

Tags:

vim

latex

I would like to know how I could trigger a certain action when I'm within a certain environment when writing code/ latex using vim. For instance, suppose I'm writing tex and inside the align environment, then I'd like every equals sign I enter to be converted to &=.

What I'm thinking of now is rather cumbersome. This is pseudo code for what is in my vimrc. Here's what it looks like

Shortcut for environment opens start and end tag for align environment (Using imap)
The above action also initiates a counter variable, sets it to 1
If counter is 1, every equals sign I enter should be entered as an &=.
Shortcut to force counter to zero when I move out of the environment. (Using imap)

I would like to know if there is an easier way to get around this. This is just an example. The main idea here is for vim to have environment-awareness.

Looking forward to your suggestions.

like image 928
WiFO215 Avatar asked Aug 06 '12 16:08

WiFO215


2 Answers

I generally tackle this sort of problem after the fact. For instance, I write it with the normal = signs and then visually select it the area and use :s/=/\&=/g. If you want to do multiple things to the "environment" after the fact you can create a function and trigger a key to execute it. For example:

vnoremap <leader>la :call <SID>latexAlign()<cr>

function! s:latexAlign() range
  exe ":'<,'>s/=/\&=/g"
  " add more stuff here...
endfunction

However, I think your solution is better in case you go back and edit it later. The htmlcomplete file in vim handles this situation like this:

let stylestart = searchpair('<style\>', '', '<\/style\>', "bnW")
let styleend   = searchpair('<style\>', '', '<\/style\>', "nW")
if stylestart != 0 && styleend != 0
   if stylestart <= curline && styleend >= curline
      let start = col('.') - 1
      let b:csscompl = 1
      while start >= 0 && line[start - 1] =~ '\(\k\|-\)'
         let start -= 1
      endwhile
   endif
endif

You can see how it uses searchpair to determine if it is inside of the <style></style> tags. See :help searchpair() for more information.

like image 196
Conner Avatar answered Oct 02 '22 23:10

Conner


This is a very interesting question, but I'm not sure that your suggested approach is the best way! I have attempted an answer, but this first iteration may not be perfect. I am assuming that a LaTeX environment is simply delimited by \begin and \end commands (my LaTeX is a bit rusty!).

Firstly you need a function to return the environment of the cursor position. My function below jumps to the previous \begin command, then jumps forward to the matching \end command, calculates whether the cursor is inside this region, and continues the process until a containing environment is found. It should handle nested environments. If not in a LaTeX environment, an empty string is returned.

function! GetTeXEnv()
    let pos = getpos('.')
    let win = winsaveview()
    let env = ''
    let carry_on = 1
    let search_ops = 'bWc'
    let b_start = line('.')
    while carry_on
        keepjumps let b_start = search('\\begin{',search_ops)
        let search_ops = 'bW'
        " Only accept a match at the cursor position on the
        " first cycle, otherwise we wouldn't go anywhere!
        let env = matchstr(getline('.'),'\\begin{\zs.\{-}\ze}')
        let env_esc = escape(env,'*')
        keepjumps let b_end = search('\\end{\s*' . env_esc . '\s*}','Wn')
        if b_start == 0
            " finished searching; stop
            let carry_on = 0
        elseif b_end > b_start && b_end < pos[1]
            " not inside this env; carry on
            let env = ''
        elseif b_end > b_start && b_end >= pos[1] && b_start <= pos[1]
            " found it; stop
            let carry_on = 0
        endif
    endwhile
    call setpos('.',pos)
    call winrestview(win)
    return env
endfunction

Now you can enquire the environment of the cursor using :echo GetTeXEnv().

To change the behaviour of the = key, you need to create another function which returns &= in the align environment, and = otherwise:

function! TeXEquals()
    return GetTeXEnv() =~ 'align\*\?' ? "&=" : "="
endfunction

Then you can remap = in insert mode (for TeX files only)

autocmd FileType tex inoremap <silent> = <c-r>=TeXEquals()<CR>

This seems to work in my example LaTeX file. Let me know if you find any problems, or have any ideas for improvements.

like image 38
Prince Goulash Avatar answered Oct 02 '22 23:10

Prince Goulash