VIM and Scala -- indentation problems? - vim

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

Related

How to grouping some character in atom in vimscript?

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-".

pass vim argument in completefunc

I am writing a vimscript that uses completefunc as:
" GetComp: Menu and Sunroutine Completion {{{1
function! GetComp(arg, findstart, base)
if a:findstart
" locate the start of the word
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
else
echomsg '**** completing' a:base
python << EOF
import vim
import os
flsts = [' ']
path = "."
for dirs, subdirs, files in os.walk(path):
for tfile in files:
if tfile.endswith(('f90', 'F90', 'f', 'F')):
ofile = open(dirs+'/'+tfile)
for line in ofile:
if line.lower().strip().startswith(vim.eval("a:arg")):
modname = line.split()[1]
flsts.append(modname)
vim.command("let flstsI = %s"%flsts)
EOF
if eval("a:arg") = "module"
for m in ["ieee_arithmatic", "ieee_exceptions", "ieee_features", "iso_c_bindings", "iso_fortran_env", "omp_lib", "omp_lib_kinds"]
if m =~ "^" . a:base
call add(flstsI, m)
endif
endfor
elseif eval("a:arg") = "subroutine"
for m in ["alarm()", "date_and_time()", "backtrace", "c_f_procpointer()", "chdir()", "chmod()", "co_broadcast()", "get_command()", "get_command_argument()", "get_environment_variable()", "mvbits()", "random_number()", "random_seed()"]
if m =~ "^" . a:base
call add(flstsI, m)
endif
endfor
endif
return flstsI
endif
endfunction
I will call it for 2 different argument as:
inoremap <leader>call call <C-o>:set completefunc=GetComp("subroutine", findstart, base)<CR><C-x><C-u>
inoremap <leader>use use <C-o>:set completefunc=GetComp("module", findstart, base)<CR><C-x><C-u>
But trying so, gives error: Unknown function GetComp(
I don't know how to call them.
If I don't use arg, then, using this reply, I can call this perfectly.
Kindly help.
You'll have to attach a context to your completion. If you look closely at my message on vi.se, you'll see a framework that permits to bind data to a user-completion. From there in your mapping, it just becomes a question of which context to attach.
A simplified way would be to execute a (preparation) function from the mappings and have the function set script global variables that will be used in your completion function.

How to write `tabline` function in vim?

I would like tabs in Vim (not the gVim) look as follows:
Explanation:
Sequence number of tab (1, 2, 3, 4 etc)
Name of file (no path, no shortened path)
If there are more than one file opened, list them in a tab.
If there are duplicate tabs (hence the same file opened in several tabs) they should be highlighted.
If buffer is modified add + at the end of filename.
Could anybody help? I want to have something like this within my .vimrc:
set tabline=%!MyTabLine()
function! MyTabLine()
...
endfunction
I've already wrote my desired tabline function. The behaviour is almost the same, except:
the + sign appears after tab number if any of buffer inside the tab is modified
tab contains only modifiable buffers (it don't clog the line with buffers of netrw file browser, help and read-only ones), but you can change this, just uncomment the desired lines
Here is the code:
set tabline=%!MyTabLine() " custom tab pages line
function! MyTabLine()
let s = ''
" loop through each tab page
for i in range(tabpagenr('$'))
if i + 1 == tabpagenr()
let s .= '%#TabLineSel#'
else
let s .= '%#TabLine#'
endif
if i + 1 == tabpagenr()
let s .= '%#TabLineSel#' " WildMenu
else
let s .= '%#Title#'
endif
" set the tab page number (for mouse clicks)
let s .= '%' . (i + 1) . 'T '
" set page number string
let s .= i + 1 . ''
" get buffer names and statuses
let n = '' " temp str for buf names
let m = 0 " &modified counter
let buflist = tabpagebuflist(i + 1)
" loop through each buffer in a tab
for b in buflist
if getbufvar(b, "&buftype") == 'help'
" let n .= '[H]' . fnamemodify(bufname(b), ':t:s/.txt$//')
elseif getbufvar(b, "&buftype") == 'quickfix'
" let n .= '[Q]'
elseif getbufvar(b, "&modifiable")
let n .= fnamemodify(bufname(b), ':t') . ', ' " pathshorten(bufname(b))
endif
if getbufvar(b, "&modified")
let m += 1
endif
endfor
" let n .= fnamemodify(bufname(buflist[tabpagewinnr(i + 1) - 1]), ':t')
let n = substitute(n, ', $', '', '')
" add modified label
if m > 0
let s .= '+'
" let s .= '[' . m . '+]'
endif
if i + 1 == tabpagenr()
let s .= ' %#TabLineSel#'
else
let s .= ' %#TabLine#'
endif
" add buffer names
if n == ''
let s.= '[New]'
else
let s .= n
endif
" switch to no underlining and add final space
let s .= ' '
endfor
let s .= '%#TabLineFill#%T'
" right-aligned close button
" if tabpagenr('$') > 1
" let s .= '%=%#TabLineFill#%999Xclose'
" endif
return s
endfunction
:help setting-tabline contains a lengthy description, including an example function that sort-of emulates Vim's default tabline. You can use this as a starting point. See :help functions for a complete list of available functions.
Learn how to look up commands and navigate the built-in :help; it is comprehensive and offers many tips. You won't learn Vim as fast as other editors, but if you commit to continuous learning, it'll prove a very powerful and efficient editor.

Making silent function non silent in vim

I use the the template-file-loader vim script to load a template file when I create p.x. a new latex file.
The template-file-loader script is able to execute a custom TemplateFileFunction_tex when I edit a new tex file..
fun! TemplateFileFunc_tex()
let tex_templates = "$HOME/.vim/templates/tex/"
let choice = confirm("Which template should i load",
\ "&presentation\n" .
\ "&hd-presentation\n" .
\ "&paper\n" .
\ "hd-pape&r\n" .
\ "&xelatex-default\n")
if choice == 1 " presentation
execute "0r " . expand(tex_templates . "presentation.tex")
" [...]
endfun
The problem is the template-file-loader plugins calls the function with silent.
How do I without "unsilent" my custom function without changing the plugin?
I don't see a way to make confirm receive user input in silenced mode. But you can use getchar instead:
let variants=['&presentation', '&hd-presentation', ...]
echohl MoreMsg
unsilent echo "Which template should I load\n".join(variants, "\n")
echohl None
let reply=getchar()
if type(reply)==type(0)
let reply=nr2char(reply)
endif
if reply is# "\n"
let choice=1
else
let replkeys=map(copy(variants), 'tolower(v:val[stridx(v:val, "&")+1])')
let choice=index(replkeys, reply)+1
endif
if choice==1
...
This is another template-expander, but still, mu-template handles correctly questions to the end-user. e.g.
VimL: " #file {rtp}/templates/tex.template
VimL: " way 1:
VimL: " instructions continued on several lines are not supported
VimL: let s:tex_kind = CONFIRM("Which template should i load", "&presentation\n&hd-presentation\n&paper\nhd-pape&r\n&xelatex-default\n", 1)
VimL: " include {rtp}/templates/tex/internals/presentation.template ?
VimL: if s:tex_kind == 1 | call s:Include('presentation', 'tex/internals') | endif
VimL: " include {rtp}/templates/tex/internals/hd-presentation.template ?
VimL: if s:tex_kind == 2 | call s:Include('hd-presentation', 'tex/internals') | endif
...
VimL: " Way 2
VimL: let s:tex_kind = WHICH("CONFIRM", "Which template should i load", "&presentation\n&hd-presentation\n&paper\nhd-pape&r\n&xelatex-default\n", 1)
VimL: let s:tex_kind = substitute(s:tex_kind, '&', '', 'g')
VimL: call s:Include(s:tex_kind, 'tex/internals')

Vim: Cucumber indentation for "And" lines

In Cucumber's Gherkin language, it's allowed to indent And lines, like so:
Scenario:
Given there is a user named Chris
And I am logged in as Chris
When I go to the home page
Then I should see "hi Chris"
And I should not see "log in"
I like this indentation style much better than the equally-indented style, but with Tim's Cucumber scripts for Vim, I have to manually indent the And lines and manually dedent the following lines, and sometimes Vim will automatically indent lines and it ends up all wrong.
What's the best way to work with indented And lines in Vim? Or is it easiest to just give up on it?
I think you could customize indent/cucumber.vim (online here) to increase the indent on lines starting with ^\s*And.
Here's a diff for indent/cucumber.vim based on Andy's answer:
--- .vim/indent/cucumber.vim.bak 2011-03-24 18:44:27.000000000 +0100
+++ .vim/indent/cucumber.vim 2011-03-24 19:09:41.000000000 +0100
## -47,6 +47,10 ##
return indent(prevnonblank(v:lnum-1)) + &sw
elseif cline =~# '^\s*[^|# \t]' && line =~# '^\s*|'
return indent(prevnonblank(v:lnum-1)) - &sw
+ elseif cline =~# '^\s*\%(And\|But\)' && line !~# '^\s*\%(And\|But\)'
+ return indent(prevnonblank(v:lnum-1)) + &sw
+ elseif cline !~# '^\s*\%(And\|But\)' && line =~# '^\s*\%(And\|But\)'
+ return indent(prevnonblank(v:lnum-1)) - &sw
elseif cline =~# '^\s*$' && line =~# '^\s*|'
let in = indent(prevnonblank(v:lnum-1))
return in == indent(v:lnum) ? in : in - &sw

Resources