" LaTeX plugin for Vim
"
" Maintainer: Karl Yngve LervÄg
" Email:      karl.yngve@gmail.com
"

function! latex#change#init(initialized) " {{{1
  if g:latex_mappings_enabled
    nnoremap <silent><buffer> dse :call latex#change#env('')<cr>
    nnoremap <silent><buffer> cse :call latex#change#env_prompt()<cr>

    nnoremap <silent><buffer> dsc vaBom`o<esc>xg``xdF\
    nnoremap <silent><buffer> csc :call latex#change#command()<cr>

    nnoremap <silent><buffer> tse :call latex#change#toggle_env_star()<cr>
    nnoremap <silent><buffer> tsd :call latex#change#toggle_delim()<cr>

    nnoremap <silent><buffer> <F7> :call latex#change#to_command()<cr>i
    inoremap <silent><buffer> <F7> <c-r>=latex#change#to_command()<cr>

    inoremap <silent><buffer> ]]   <c-r>=latex#change#close_environment()<cr>
  endif
endfunction

function! latex#change#command() " {{{1
  let pos_save = getpos('.')
  let savereg = @a

  " This is a hack to make undo restore the correct position
  normal! ix
  normal! x

  normal! F\lve"ay
  let old = @a
  
  let new = input('Change ' . old . ' for: ')
  if empty(new)
    let new = old
  endif
  let pos_save[2] += len(new) - len(old)

  let @a = new
  normal! F\lcea

  let @a = savereg
  call setpos('.', pos_save)
endfunction

function! latex#change#close_environment() " {{{1
  " Close delimiters
  let [lnum, cnum] = searchpairpos('\C\\left\>', '', '\C\\right\>', 'bnW',
        \ 'latex#util#in_comment()')
  if lnum > 0
    let line = strpart(getline(lnum), cnum - 1)
    let bracket = matchstr(line, '^\\left\zs\((\|\[\|\\{\||\|\.\)\ze')
    for [open, close] in [
          \ ['(', ')'],
          \ ['\[', '\]'],
          \ ['\\{', '\\}'],
          \ ['|', '|'],
          \ ['\.', '|'],
          \ ]
      let bracket = substitute(bracket, open, close, 'g')
    endfor
    return '\right' . bracket
  endif

  " Close environment
  let env = latex#util#get_env()
  if env == '\['
    return '\]'
  elseif env == '\('
    return '\)'
  elseif env != ''
    return '\end{' . env . '}'
  endif
endfunction

function! latex#change#delim(open, close) " {{{1
  let [d1, l1, c1, d2, l2, c2] = latex#util#get_delim()

  let line = getline(l1)
  let line = strpart(line,0,c1 - 1) . a:open . strpart(line, c1 + len(d1) - 1)
  call setline(l1, line)

  if l1 == l2
    let n = len(a:open) - len(d1)
    let c2 += n
    let pos = getpos('.')
    let pos[2] += n
    call setpos('.', pos)
  endif

  let line = getline(l2)
  let line = strpart(line,0,c2 - 1) . a:close . strpart(line, c2 + len(d2) - 1)
  call setline(l2, line)
endfunction

function! latex#change#env(new_env) " {{{1
  let [env, l1, c1, l2, c2] = latex#util#get_env(1)

  if a:new_env == ''
    let beg = ''
    let end = ''
  elseif a:new_env == '\[' || a:new_env == '['
    let beg = '\['
    let end = '\]'
  elseif a:new_env == '\(' || a:new_env == '('
    let beg = '\('
    let end = '\)'
  else
    let beg = '\begin{' . a:new_env . '}'
    let end = '\end{' . a:new_env . '}'
  endif

  let n1 = len(env) - 1
  let n2 = len(env) - 1
  if env != '\[' && env != '\('
    let n1 += 8
    let n2 += 6
  endif

  let line = getline(l1)
  let line = strpart(line, 0, c1 - 1) . l:beg . strpart(line, c1 + n1)
  call setline(l1, line)
  let line = getline(l2)
  let line = strpart(line, 0, c2 - 1) . l:end . strpart(line, c2 + n2)
  call setline(l2, line)
endfunction

function! latex#change#env_prompt() " {{{1
  let new_env = input('Change ' . latex#util#get_env() . ' for: ', '',
        \ 'customlist,' . s:sidwrap('input_complete'))
  if empty(new_env)
    return
  else
    call latex#change#env(new_env)
  endif
endfunction

function! latex#change#to_command() " {{{1
  " Get current line
  let line = getline('.')

  " Get cursor position
  let pos = getpos('.')

  " Return if there is no word at cursor
  if mode() == 'n'
    let column = pos[2] - 1
  else
    let column = pos[2] - 2
  endif
  if column <= 1 || line[column] =~ '\s'
    return ''
  endif

  " Prepend a backslash to beginning of the current word
  normal! B
  let column = getpos('.')[2]
  if line[column - 1] != '\'
    let line = strpart(line, 0, column - 1) . '\' . strpart(line, column - 1)
    call setline('.', line)
  endif

  " Append opening braces to the end of the current word
  normal! E
  let column = getpos('.')[2]
  let pos[2] = column + 1
  if line[column - 1] != '{'
    let line = strpart(line, 0, column) . '{' . strpart(line, column)
    call setline('.', line)
    let pos[2] += 1
  endif

  " Restore cursor position
  call setpos('.', pos)
  return ''
endfunction

function! latex#change#toggle_delim() " {{{1
  "
  " Toggle \left and \right variants of delimiters
  "
  let [d1, l1, c1, d2, l2, c2] = latex#util#get_delim()

  if d1 == ''
    return 0
  elseif d1 =~ 'left'
    let newd1 = substitute(d1, '\\left', '', '')
    let newd2 = substitute(d2, '\\right', '', '')
  elseif d1 !~ '\cbigg\?'
    let newd1 = '\left' . d1
    let newd2 = '\right' . d2
  else
    return
  endif

  let line = getline(l1)
  let line = strpart(line, 0, c1 - 1) . newd1 . strpart(line, c1 + len(d1) - 1)
  call setline(l1, line)

  if l1 == l2
    let n = len(newd1) - len(d1)
    let c2 += n
    let pos = getpos('.')
    let pos[2] += n
    call setpos('.', pos)
  endif

  let line = getline(l2)
  let line = strpart(line, 0, c2 - 1) . newd2 . strpart(line, c2 + len(d2) - 1)
  call setline(l2, line)
endfunction

function! latex#change#toggle_env_star() " {{{1
  let env = latex#util#get_env()

  if env == '\('
    return
  elseif env == '\['
    let new_env = equation
  elseif env[-1:] == '*'
    let new_env = env[:-2]
  else
    let new_env = env . '*'
  endif

  call latex#change#env(new_env)
endfunction


function! latex#change#wrap_selection(wrapper) " {{{1
  keepjumps normal! `>a}
  execute 'keepjumps normal! `<i\' . a:wrapper . '{'
endfunction

function! latex#change#wrap_selection_prompt(...) " {{{1
  let env = input('Environment: ', '',
        \ 'customlist,' . s:sidwrap('input_complete'))
  if empty(env)
    return
  endif

  " Make sure custom indentation does not interfere
  let ieOld = &indentexpr
  setlocal indentexpr=""

  if visualmode() ==# 'V'
    execute 'keepjumps normal! `>o\end{' . env . '}'
    execute 'keepjumps normal! `<O\begin{' . env . '}'
    " indent and format, if requested.
    if a:0 && a:1
      normal! gv>
      normal! gvgq
    endif
  else
    execute 'keepjumps normal! `>a\end{' . env . '}'
    execute 'keepjumps normal! `<i\begin{' . env . '}'
  endif

  exe "setlocal indentexpr=" . ieOld
endfunction
" }}}1

function! s:sidwrap(func) " {{{1
  return s:SID . a:func
endfunction

let s:SID = matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$')

function! s:input_complete(lead, cmdline, pos) " {{{1
  let suggestions = []
  for entry in g:latex_complete_environments
    let env = entry.word
    if env =~ '^' . a:lead
      call add(suggestions, env)
    endif
  endfor
  return suggestions
endfunction

function! s:search_and_skip_comments(pat, ...) " {{{1
  " Usage: s:search_and_skip_comments(pat, [flags, stopline])
  let flags             = a:0 >= 1 ? a:1 : ''
  let stopline  = a:0 >= 2 ? a:2 : 0
  let saved_pos = getpos('.')

  " search once
  let ret = search(a:pat, flags, stopline)

  if ret
    " do not match at current position if inside comment
    let flags = substitute(flags, 'c', '', 'g')

    " keep searching while in comment
    while latex#util#in_comment()
      let ret = search(a:pat, flags, stopline)
      if !ret
        break
      endif
    endwhile
  endif

  if !ret
    " if no match found, restore position
    call setpos('.', saved_pos)
  endif

  return ret
endfunction
" }}}1

" vim: fdm=marker sw=2