Dear Vim developers,

I noticed certain cases which cause Vim's indentation for awk to go into
an infinite loop. The problem appears when there is a negative brace
balance, i.e. more closing braces than opening braces. This can easily
happen if one uses a regexp to match a closing brace. Vim then moves
upward through the file to find the corresponding opening brace, which
does not exist. The loop is missing a break condition for the case that
line 0 has been reached. I added it in my patch.

You see for yourself by opening the attached demo.awk and inserting a
line below "match".

Additionally, I think that strings and computed regexps should not be
scanned for matching braces at all when it comes to determining the
indentation. I changed the Get_brace_balance function to strip "..." and
/.../ while taking into account escape characters. I am novice in
writing Vim script and I'd be glad if somebody found a more elegant way
of doing this but at least it seems to give proper indentation now.

The attached patch should apply to the current version of indent/awk.vim.


Cheers,
Philipp
{
        match($0, /[^)]/)
}

--- old/awk.vim	2011-04-02 00:42:52.251063401 +0200
+++ new/awk.vim	2011-04-02 01:49:50.172678039 +0200
@@ -120,6 +120,12 @@
    if prev_data =~ ')' && brace_balance < 0
       while brace_balance != 0
 	 let prev_lineno = s:Get_prev_line( prev_lineno )
+	 if prev_lineno <= 0
+	    " If we reach the start of the file, some parentheses were
+	    " misinterpreted probably. We should leave the otherwise infinite
+	    " loop and do a failsafe indentation.
+	    return 0
+	 endif
 	 let prev_data = getline( prev_lineno )
 	 let brace_balance=brace_balance+s:Get_brace_balance(prev_data,'(',')' )
       endwhile
@@ -150,8 +156,12 @@
 " across multiple lines.
 
 function! s:Get_brace_balance( line, b_open, b_close )
-   let line2 = substitute( a:line, a:b_open, "", "g" )
-   let openb = strlen( a:line ) - strlen( line2 )
+   " Remove escaped \, /, and "
+   let line  = substitute( a:line, '\\\\\|\\/\|\\\"', '', 'g')
+   " Remove strings and computed regexps first to prevent incorrect matches.
+   let line  = substitute( line, '/[^/]*/\|"[^/]*"', '', 'g')
+   let line2 = substitute( line, a:b_open, "", "g" )
+   let openb = strlen( line ) - strlen( line2 )
    let line3 = substitute( line2, a:b_close, "", "g" )
    let closeb = strlen( line2 ) - strlen( line3 )
    return openb - closeb

Attachment: signature.asc
Description: Digital signature

Reply via email to