Here I want to minify my code:
let s:next_col = getline('.')[col('.') - 1]
if s:next_col is# "'" || s:next_col is# '"' || s:next_col is# '`' || ')' || s:next_col is# ']' || s:next_col is# '}' || s:next_col is# '>'
return "\<right>"
endif
The above code work as expected. But when I try convert it to this:
let s:next_col = getline('.')[col('.') - 1]
" I also try to add / before \V, not sure if its correct but that also won't work
if s:next_col is# '\V\("\|`\|)\|]\|}\|>\)' || s:next_col is# "'"
return "\<right>"
endif
Now, the only it work will be if the next column is '.
I wonder how I can grouping these character ' " ) ] } > and check if one of those matching with my variable?
is and its variants are only useful for comparing lists or dictionaries. If you want to compare strings, then the right operators would be ==, !=, =~, !~ and their case variants.
Here, you can simply use =~ to compare with a regular expression pattern and, since punctuation marks don't have casing, there is no need for # or ?:
let s:next_col = getline('.')[col('.') - 1]
if s:next_col =~ "['\"`)\\]}>]"
return "\<right>"
endif
[...] is a collection. See :help /[].
', `, ), }, and > are all used as-is because they have no special meaning in Vim's regex dialect.
" is escaped with a \ because double quotes are used for quoting the pattern.
] is escaped once because ] closes the collection, which makes it special in Vim's regex dialect, and a second time because \ is special in double quotes. See :help expr-".
Related
I see some viml code like
if &foo =~# '%[MRHWY]' && &foo !~# '%[mrhwy]'
What do those strings '%[MRHWY]' and '%[mrhwy]' mean? Do they mean something in context of the =~# comparison that may be different from another context?
Case sensitive (=~#) pattern matching to a collection, []. In this instance, it is matching on % and a collection of letters which may show up in one's statusline. Specifically:
%[MRHWY] can match '%M', '%R', '%H', '%W', or '%Y'.
What may distract you, as it did for me, is that there is also a sequence pattern match, %[], which matches as much of the collection as it can; its routine stops at the first atom that doesn't match (see help %[] vs. [] for more examples and details). However, this is just not the case here, since there is a quote before %, in front of a collection.
With a quick search, I see this line of code is used in the fugitive.vim package where it checks the status line for the sequence of letters 'MRHWY':
function! fugitive#statusline(...) abort
if !exists('b:git_dir')
return ''
endif
let status = ''
if s:buffer().commit() != ''
let status .= ':' . s:buffer().commit()[0:7]
endif
let status .= '('.fugitive#head(7).')'
if &statusline =~# '%[MRHWY]' && &statusline !~# '%[mrhwy]'
return ',GIT'.status
else
return '[Git'.status.']'
endif
endfunction
(BTW: If you want to see what these letters each mean, see help :statusline. An example: M is modified text flag, R is readonly flag, etc.)
Based on AutoComplPop documentation it uses text before the cursor to selects the completion method. for example if the text before cursor is a filename character + a path separator + 0 or more filename character the filename completion is used, i. e. <c-x><c-f> is feeded.
What if we want to extend this behaviour to when text before the cursor is for example href=" or src="?
I think AutoComplPop uses the following function to match text before the cursor and triggers filename completion :
function acp#meetsForFile(context)
if g:acp_behaviorFileLength < 0
return 0
endif
if has('win32') || has('win64')
let separator = '[/\\]'
else
let separator = '\/'
endif
if a:context !~ '\f' . separator . '\f\{' . g:acp_behaviorFileLength . ',}$'
return 0
endif
return a:context !~ '[*/\\][/\\]\f*$\|[^[:print:]]\f*$'
endfunction
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
In your original function you have mistakes:
strpart() takes string, offset and length arguments, while you supplied two offsets.
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:
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.
Same for =~: use =~#.
Due to backwards compatibility string[-1] (string[any_negative_integer]) is always empty. Thus I have to use line[-1:].
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.
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
I would like to indent every line with 5 spaces when all the next conditions are matched:
1) after an empty line
2) when line starts with a Uppercase letter
3) when the line has more then 80 characters when the file has no textwidth set
4) when the line has more then (textwidth-10) characters when the file has a textwidth set
and the next line must not start with a Uppercase letter.
Can anyone help me?
This is completely untested, and I'm sure there are more elegant methods, but this should give you a rough idea. I scan every line in the file one at a time, and indent it if one of your conditions is met.
function! Indenter()
let winview=winsaveview()
try
let this_line_num=1
let tw = &textwidth
while this_line_num <= line("$")
let thisline=getline(this_line_num)
let lastline=getline(this_line_num-1)
let firstchar=substitute(matchstr(thisline,"^ *.")," ","","g")
if ( matchstr(lastline,"^.") == "" && this_line_num > 1 )
\ || firstchar =~# "[A-Z]"
\ || ( tw == 0 && strlen(thisline) > 80 )
\ || ( tw != 0 && strlen(thisline) > tw-10
\ && firstchar !~# "[A-Z]" )
sil exe this_line_num . "s#^# #"
endif
let this_line_num+=1
endwhile
finally
call winrestview(winview)
endtry
endfunction
I am assuming that "empty lines" means no whitespace (hence matchstr(lastline,"^.")) and that lines containing text can have leading whitespace (hence the substitute(matchstr()) command to get the first non-whitespace character.
Hope this helps. Let me know if it falls flat on its face.
I downloaded Scala 2.8, installed the vim scripts included and tried to type in some Scala code. When I typed in val x = 1 + 2 and hit ENTER, the indentation goes to below the v. When I type in val x = (1 + 2), the indentation is below the x!
If VIM is used by anyone at all for Scala, this bug should've been seen long ago. Or am I the only one seeing this?
With the indent/scala.vim from the current 2.8.0.final release I have the same outcome... But I know, that it worked in a earlier release, because I have one file here where it works. Here it is:
" Vim indent file
" Language : Scala (http://scala-lang.org/)
" Maintainer : Stefan Matthias Aust
" Last Change: 2006 Apr 13
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
setlocal indentexpr=GetScalaIndent()
setlocal indentkeys=0{,0},0),!^F,<>>,<CR>
setlocal autoindent sw=2 et
if exists("*GetScalaIndent")
finish
endif
function! CountParens(line)
let line = substitute(a:line, '"\(.\|\\"\)*"', '', 'g')
let open = substitute(line, '[^(]', '', 'g')
let close = substitute(line, '[^)]', '', 'g')
return strlen(open) - strlen(close)
endfunction
function! GetScalaIndent()
" Find a non-blank line above the current line.
let lnum = prevnonblank(v:lnum - 1)
" Hit the start of the file, use zero indent.
if lnum == 0
return 0
endif
let ind = indent(lnum)
let prevline = getline(lnum)
"Indent html literals
if prevline !~ '/>\s*$' && prevline =~ '^\s*<[a-zA-Z][^>]*>\s*$'
return ind + &shiftwidth
endif
"### Taken from mail on scala mailing list
"### -------------------------------------
" Add a 'shiftwidth' after lines that start a block
" If if, for or while end with ), this is a one-line block
" If val, var, def end with =, this is a one-line block
"if prevline =~ '^\s*\<\(\(else\s\+\)\?if\|for\|while\|va[lr]\|def\)\>.*[)=]\s*$'
"\ || prevline =~ '^\s*\<else\>\s*$'
"\ || prevline =~ '{\s*$'
"let ind = ind + &shiftwidth
"endif
" Add a 'shiftwidth' after lines that start a block
" If if, for or while end with ), this is a one-line block
" If val, var, def end with =, this is a one-line block
if prevline =~ '^\s*\<\(\(else\s\+\)\?if\|for\|while\)\>.*[)]\s*$'
\ || prevline =~ '^\s*\<\(\(va[lr]\|def\)\>.*[=]\s*$'
\ || prevline =~ '^\s*\<else\>\s*$'
\ || prevline =~ '{\s*$'
let ind = ind + &shiftwidth
endif
" If parenthesis are unbalanced, indent or dedent
let c = CountParens(prevline)
echo c
if c > 0
let ind = ind + &shiftwidth
elseif c < 0
let ind = ind - &shiftwidth
endif
"### Taken from mail on scala mailing list
"### -------------------------------------
" Dedent after if, for, while and val, var, def without block
"let pprevline = getline(prevnonblank(lnum - 1))
"if pprevline =~ '^\s*\<\(\(else\s\+\)\?if\|for\|while\|va[lr]\|def\)\>.*[)=]\s*$'
"\ || pprevline =~ '^\s*\<else\>\s*$'
"let ind = ind - &shiftwidth
"endif
" Dedent after if, for, while and val, var, def without block
"let pprevline = getline(prevnonblank(lnum - 1))
if pprevline =~ '^\s*\<\(\(else\s\+\)\?if\|for\|while\)\>.*[)]\s*$'
\ || pprevline =~ '^\s*\<\(\va[lr]\|def\)\>.*[=]\s*$'
\ || pprevline =~ '^\s*\<else\>\s*$'
let ind = ind - &shiftwidth
endif
" Align 'for' clauses nicely
if prevline =~ '^\s*\<for\> (.*;\s*$'
let ind = ind - &shiftwidth + 5
endif
" Subtract a 'shiftwidth' on '}' or html
let thisline = getline(v:lnum)
if thisline =~ '^\s*[})]'
\ || thisline =~ '^\s*</[a-zA-Z][^>]*>'
let ind = ind - &shiftwidth
endif
return ind
endfunction
But I have no clue, where the change was introduced... Tried to find it in the SVN history at https://codereview.scala-lang.org/fisheye/browse/scala-svn/scala-tool-support/trunk/src/vim/indent/scala.vim