Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vim: complete depending on previous character

Tags:

vim

I want to create a mapping that will changed the ins-completion depending on the character just before the cursor. If the character is { then I want tag completion, if its : i want normal completion (that depends on the complete option) and if the characters is a backslash plus some word (\w+) I want dictionary completion. I have the following in my ftplugin/tex/latex_settings.vim file:

setlocal dictionary=$DOTVIM/ftplugin/tex/tex_dictionary
setlocal complete=.,k
setlocal tags=./bibtags;

function! MyLatexComplete()
    let character = strpart(getline('.'), col('.') - 1, col('.'))

    if character == '{'
        return "\<C-X>\<C-]>"
    elseif character == ':'
        return "\<C-X>\<C-N>"
    else
        return "\<C-X>\<C-K>"
    endif
endfunction

inoremap <C-n> <c-r>=MyLatexComplete()<CR>

This doesn't work and I don't know how to fix it.

Edit: This seems to work but I'm want a conditional that checks for \w+ (backslash plus any word) and a final one that gives a message "No match found".

function! MyLatexComplete()
    let line = getline('.')
    let pos = col('.') - 1

    " Citations (comma for multiple ones)
    if line[pos - 1] == '{' || line[pos - 1] == ','
        return "\<C-X>\<C-]>"
    " Sections, equations, etc
    elseif line[pos - 1] == ':'
        return "\<C-X>\<C-N>"
    else
    " Commands (such as \delta)
        return "\<C-X>\<C-K>"
    endif
endfunction
like image 605
petobens Avatar asked Oct 03 '22 01:10

petobens


2 Answers

In your original function you have mistakes:

  1. strpart() takes string, offset and length arguments, while you supplied two offsets.
  2. col('.') is one character past the end-of-line. I.e. len(getline('.'))==col('.')+1 meaning that strpart(getline('.'), col('.')-1) is always empty.

You have fixed these issues in the second variant. But if you want conditional check for \command you need not just last character. Thus I would suggest matching slice

let line=getline('.')[:col('.')-2]
if line[-1:] is# '{' || line[-1:] is# ','
   return "\<C-X>\<C-]>"
elseif line[-1:] is# ':'
   return "\<C-X>\<C-N>"
elseif line =~# '\v\\\w+$'
   return "\<C-X>\<C-K>"
else
   echohl ErrorMsg
       echomsg 'Do not know how to complete: use after {, comma or \command'
   echohl None
   return ''
endif

. Note some things:

  1. Never use == for string comparison without # or ? attached. This does not matter in this case, but you should make yourself used. ==# and ==? both ignore value of 'ignorecase' setting (first acts as if 'noignorecase' was set, second as if 'ignorecase' was set). I use even stricter is#: a is# b is like type(a)==type(b) && a ==# b.
  2. Same for =~: use =~#.
  3. Due to backwards compatibility string[-1] (string[any_negative_integer]) is always empty. Thus I have to use line[-1:].

  4. Never use plain :echoerr. It is unstable: in terms that you cannot say for sure whether or not this will break execution flaw (:echoerr breaks execution if put inside :try block and does not do so otherwise). echohl ErrorMsg|echomsg …|echohl None never breaks execution, throw … and try|echoerr …|endtry break always.

like image 144
ZyX Avatar answered Oct 13 '22 09:10

ZyX


To spot preceding LaTeX commands you can use the following regular expression on your line variable:

line =~ '\\\w\+$'

(as you can see, the regex is similar to the Perl expression you guessed at, but requires some the characters to be escaped).

To echo a "No match found" message, you could return an appropriate :echoerr command:

return "\<C-o>:echoerr 'No match found'\<CR>"

But this has the side-effect of hijacking insert-mode for a moment... maybe it's cleaner just to return no matches as an empty string?

So your final function would look something like this:

function! MyLatexComplete()
    let line = getline('.')
    let pos = col('.') - 1

    " Citations (comma for multiple ones)
    if line[pos - 1] == '{' || line[pos - 1] == ','
        return "\<C-X>\<C-]>"
    " Sections, equations, etc
    elseif line[pos - 1] == ':'
        return "\<C-X>\<C-N>"
    elseif line =~ '\\\w\+$'
    " Commands (such as \delta)
        return "\<C-X>\<C-K>"
    else
    " Echo an error:
        return "\<C-o>:echoe 'No match found'\<CR>"
    endif
endfunction
like image 34
Prince Goulash Avatar answered Oct 13 '22 10:10

Prince Goulash