views:

2750

answers:

6

I'm getting burned repeatedly by unmatched parentheses while writing python code in vim. I like how they're handled for C code - vim highlights in red all of the curly braces following the unmatched paren. I looked at the c.vim syntax file briefly to try to understand it, but the section that handles bracket errors is very complex. Can anyone explain how that code works and suggest how I might write something similar for python code?

Example C code with unmatched parens:

int main(void
{  /* brace highlighted in red */
}  /* brace highlighted in red */

Since python code doesn't have curly braces to highlight, we'll have to choose something else (perhaps other parentheses).

BTW, I tried out this vim plugin but I wasn't happy with the behavior.

Edit:

I'm using python to generate C++ code (a language that likes parentheses and semicolons). I have a nasty habit of leaving the trailing paren off a file.write() method call. It would be nice if I could get vim to make that mistake more visually obvious.

Update:

Ok, here's what I've tried so far.

:syn region pParen transparent start="(" end=")" contains=ALL
:syn match pError display ")"
:hi def link pError Error

Unfortunately, all this does is highlight as an error the right paren of all balanced parentheses, the opposite of what I want to do. I really don't understand what I'm doing here (just copied off of the existing C syntax file). If anyone could explain what I did (wrong), I would appreciate it.

+3  A: 

Stop gap solution:

:imap ( ()<C-[>i

This will make it so every time you type a left paren it will automatically put in the right and put you in the position of typing in between.

Whaledawg
what about simple adding '(' symbol in some already existed "string"?
Mykola Golubyev
+5  A: 

You can get vim to do the opposite: do a

:set showmatch

and it will highlight matching parens. You'll know when you're unbalanced when it doesn't highlight something.

I'm also assuming you're familiar with the '%' command, which bounces you to the matching element.

Andrew Barnett
showmatch matches only if the pairing parenthesis is on screen. It will not jump.
ldigas
+2  A: 

Not sure if it'll be more or less confusing for you, but you could look at the lisp.vim syntax file (especially the part where g:lisp_rainbow is handled) to see how you can highlight matching parens.

If you manage to highlight all the matching parens, you could have the leftover parens (i.e. unmatched parens) have default Error highlighting. This is what the lisp file seems to be doing.

EDIT: How about this:

syn match parenError ")"
syn region matchingParens transparent start="(" end=")" contains=matchingParens
hi parenError guifg=red

If you :syn clear and run those, it seems to work. Note that the order the syn commands are executed matters. Per :h :syn-priority, the rule matched last is the one that takes effect, which may be why your rules highlighted all the end-parens in the file.

EDIT #2:

What c.vim is actually doing is highlighting any {} inside of (), whether everything is properly closed or not. Try typing ({}) in C mode, it still highlights the {} as an error.

I don't think this approach can be used to test directly for a ( with an unmatched ), because :syn region doesn't care whether the end-pattern is there or not.

So you have to find something Python-specific that should never belong inside (). Then match against "(\_[^)]*the_forbidden_something". I don't know Python enough to know what that might be.

If nothing else, you can do:

syn match openParen "(\_[^)]*\%$"

which matches an open paren with no closing parens before the end-of-file. This fails if it finds any closing paren at all, which means it won't even catch (()<EOF>.

Brian Carper
Yeah, lisp.vim and c.vim look like good references to use. Unfortunately, I don't really understand what I'm reading. :-)
Kristo
Vim's syn command is a bit cryptic. Does my edit work?
Brian Carper
That highlights all *extra* right parens in red. It does have the side effect of removing all highlighting inside parentheses. I don't get my highlighting back until I balance the parens. It's a good start. Can we improve on it?
Kristo
A: 

As a workaround, I found this indent script on the vim website that supposedly does a better job of indenting Python code. When you end a line with unbalanced parens, it indents the next line to line up with the open paren.

Kristo
+2  A: 

If I understand correctly and you are trying to look at non-matching parenthesis in C code (that was generated in python), I would recommend you install rainbow.vim from Dr Chip's Site. This will highlight braces in different colours depending on the levels of indentation and will highlight unmatching braces in red as you have requested. A screenshot is at http://img294.imageshack.us/img294/8586/rainbow.jpg.

To install, download rainbow.vim and place in vimfiles/after/syntax/c/ (create this directory if it is not present).

On Linux, this will be ~/.vim/after/syntax/c/rainbow.vim

On Windows, it may be c:\vim\vimfiles\after\syntax\c\rainbow.vim or possibly somewhere else, see ":help runtimepath".

Note that there are some plugins that conflict with rainbow.vim, but it's not too hard to make them cooperate.

If you are trying to highlight non-matching parenthesis in the python code, you could modify rainbow.vim to use the python syntax clusters instead of the C ones, but this is a little more involved, but you could use something along the lines of (modified version of Dr Chip's rainbow code):

syn cluster pyParenGroup contains=pythonString,pythonRawString,pythonEscape,pythonNumber,pythonBuiltin,pythonException
syn match pyParenError display ')'
syn region  pyParen     transparent matchgroup=hlLevel0 start='(' end=')' contains=@pyParenGroup,pyParen1
syn region  pyParen1        transparent matchgroup=hlLevel1 start='(' end=')' contains=@pyParenGroup,pyParen2
syn region  pyParen2        transparent matchgroup=hlLevel2 start='(' end=')' contains=@pyParenGroup,pyParen3
syn region  pyParen3        transparent matchgroup=hlLevel3 start='(' end=')' contains=@pyParenGroup,pyParen4
syn region  pyParen4        transparent matchgroup=hlLevel4 start='(' end=')' contains=@pyParenGroup,pyParen5
syn region  pyParen5        transparent matchgroup=hlLevel5 start='(' end=')' contains=@pyParenGroup,pyParen6
syn region  pyParen6        transparent matchgroup=hlLevel6 start='(' end=')' contains=@pyParenGroup,pyParen7
syn region  pyParen7        transparent matchgroup=hlLevel7 start='(' end=')' contains=@pyParenGroup,pyParen8
syn region  pyParen8        transparent matchgroup=hlLevel8 start='(' end=')' contains=@pyParenGroup,pyParen9
syn region  pyParen9        transparent matchgroup=hlLevel9 start='(' end=')' contains=@pyParenGroup,pyParen
hi link pyParenError Error

if &bg == "dark"
    hi default   hlLevel0 ctermfg=red         guifg=red1
    hi default   hlLevel1 ctermfg=yellow      guifg=orange1      
    hi default   hlLevel2 ctermfg=green       guifg=yellow1      
    hi default   hlLevel3 ctermfg=cyan        guifg=greenyellow  
    hi default   hlLevel4 ctermfg=magenta     guifg=green1       
    hi default   hlLevel5 ctermfg=red         guifg=springgreen1 
    hi default   hlLevel6 ctermfg=yellow      guifg=cyan1        
    hi default   hlLevel7 ctermfg=green       guifg=slateblue1   
    hi default   hlLevel8 ctermfg=cyan        guifg=magenta1     
    hi default   hlLevel9 ctermfg=magenta     guifg=purple1
else
    hi default   hlLevel0 ctermfg=red         guifg=red3
    hi default   hlLevel1 ctermfg=darkyellow  guifg=orangered3
    hi default   hlLevel2 ctermfg=darkgreen   guifg=orange2
    hi default   hlLevel3 ctermfg=blue        guifg=yellow3
    hi default   hlLevel4 ctermfg=darkmagenta guifg=olivedrab4
    hi default   hlLevel5 ctermfg=red         guifg=green4
    hi default   hlLevel6 ctermfg=darkyellow  guifg=paleturquoise3
    hi default   hlLevel7 ctermfg=darkgreen   guifg=deepskyblue4
    hi default   hlLevel8 ctermfg=blue        guifg=darkslateblue
    hi default   hlLevel9 ctermfg=darkmagenta guifg=darkviolet
endif

EDIT:

As a test, I downloaded gvim70.zip and vim70rt.zip from ftp://ftp.vim.org/pub/vim/pc/ (these are the Windows versions of Vim 7.0). I unzipped the two files into a new directory and ran gvim.exe from vim/vim70/gvim.exe. I do not have any vim configuration stored in "C:\Documents and Settings", so running this vim is the same as running a 'vanilla' configuration. I then downloaded pyprint.py from http://www.amk.ca/python/simple/pyprint.html as a piece of sample code and copied the above code into a file called code.vim. In gVim, I entered ":e pyprint.py". It opened in the white-background window, with no syntax highlighting. I then entered ":syntax on", which switched the default syntax highlighting on. I added a second ')' character on line 8. Finally, I entered ":source code.vim", which made the second ')' character be highlighted in red.

I've also carried out this test on Linux (with Vim 7.2), by entering the following command sequence:

cd ~
mv .vimrc old_dot_vimrc
mv .gvimrc old_dot_gvimrc
mv .vim old_dot_vim
vim pyprint.py
:e pyprint.py
" Add extra bracket here!
:syntax on
:source code.vim

Again, the second bracket is highlighted and everything else seems normal.

Al
Do you have a good definition for pyParenGroup?
Kristo
Ah! Good point. It's not strictly necessary as it essentially defines which matches are highlighted within the parentheses. However, I've had a go at making a sample one and it seems to work with my test code.
Al
This seems to break the highlighting for correct python code. Everything after the first '(' is not highlighted and there's no error highlighting when I add unmatched parens. I get some highlighting back when I add items to pyParenGroup.
Kristo
I've just tried this with some sample code from the web and created the following screenshot (http://img401.imageshack.us/img401/6887/pyprint.png). It seems to be working, but I guess it may be something that I'm missing about the style of code that you're using or your set-up generally.
Al
I tried the sample code in the picture and I do not see the same highlighting you have. The only thing highlighted after the first '(' is the string on line 9. I'm using gvim version 7.0.237.
Kristo
The comment box isn't long enough, so I've added an "EDIT" above with details. I can't reproduce this problem. Can you try with a vanilla configuration (no .vimrc etc) and see whether it works in that situation? After that, we can try to identify what's conflicting.
Al
A second test (if the vanilla configuration works) would be to load the file in your normal vim, enter ":syn clear", then ":source /path/to/python.vim", where /path/to is (e.g.) /usr/share/vim/syntax/ on Linux or c:/vim/vim70/syntax/ on Windows. Then run ":source code.vim".
Al
Ok I tried this sample code at home on gvim 7.2 and saw the same results you did. It must be some quirk of the version I'm stuck with at work. Thanks for all your help.
Kristo
My pleasure! I'm sure it'd be possible to overcome the problems you have on the work machine: what operating system is it and how much customisation is there (i.e. vimrc/.vim)? Possibly a little optimistic, but couldn't you just install (or just run) a different version?
Al
It's problably possible to install another version, but I'd have to go through official channels to do it. It's not worth the effort.
Kristo
A: 

Have you tried using matchit.vim? It supports all sorts of matches, and it should work in Python.

Nathan Fellman