1" vim: set sw=3 sts=3: 2 3" Awk indent script. It can handle multi-line statements and expressions. 4" It works up to the point where the distinction between correct/incorrect 5" and personal taste gets fuzzy. Drop me an e-mail for bug reports and 6" reasonable style suggestions. 7" 8" Bugs: 9" ===== 10" - Some syntax errors may cause erratic indentation. 11" - Same for very unusual but syntacticly correct use of { } 12" - In some cases it's confused by the use of ( and { in strings constants 13" - This version likes the closing brace of a multiline pattern-action be on 14" character position 1 before the following pattern-action combination is 15" formatted 16 17" Author: 18" ======= 19" Erik Janssen, ejanssen@itmatters.nl 20" 21" History: 22" ======== 23" 26-04-2002 Got initial version working reasonably well 24" 29-04-2002 Fixed problems in function headers and max line width 25" Added support for two-line if's without curly braces 26 27" Only load this indent file when no other was loaded. 28if exists("b:did_indent") 29 finish 30endif 31 32let b:did_indent = 1 33 34setlocal indentexpr=GetAwkIndent() 35" Mmm, copied from the tcl indent program. Is this okay? 36setlocal indentkeys-=:,0# 37 38" Only define the function once. 39if exists("*GetAwkIndent") 40 finish 41endif 42 43" This function contains a lot of exit points. It checks for simple cases 44" first to get out of the function as soon as possible, thereby reducing the 45" number of possibilities later on in the difficult parts 46 47function! GetAwkIndent() 48 49 " Find previous line and get it's indentation 50 let prev_lineno = s:Get_prev_line( v:lnum ) 51 if prev_lineno == 0 52 return 0 53 endif 54 let prev_data = getline( prev_lineno ) 55 let ind = indent( prev_lineno ) 56 57 " Increase indent if the previous line contains an opening brace. Search 58 " for this brace the hard way to prevent errors if the previous line is a 59 " 'pattern { action }' (simple check match on /{/ increases the indent then) 60 61 if s:Get_brace_balance( prev_data, '{', '}' ) > 0 62 return ind + &sw 63 endif 64 65 let brace_balance = s:Get_brace_balance( prev_data, '(', ')' ) 66 67 " If prev line has positive brace_balance and starts with a word (keyword 68 " or function name), align the current line on the first '(' of the prev 69 " line 70 71 if brace_balance > 0 && s:Starts_with_word( prev_data ) 72 return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) 73 endif 74 75 " If this line starts with an open brace bail out now before the line 76 " continuation checks. 77 78 if getline( v:lnum ) =~ '^\s*{' 79 return ind 80 endif 81 82 " If prev line seems to be part of multiline statement: 83 " 1. Prev line is first line of a multiline statement 84 " -> attempt to indent on first ' ' or '(' of prev line, just like we 85 " indented the positive brace balance case above 86 " 2. Prev line is not first line of a multiline statement 87 " -> copy indent of prev line 88 89 let continue_mode = s:Seems_continuing( prev_data ) 90 if continue_mode > 0 91 if s:Seems_continuing( getline(s:Get_prev_line( prev_lineno )) ) 92 " Case 2 93 return ind 94 else 95 " Case 1 96 if continue_mode == 1 97 " Need continuation due to comma, backslash, etc 98 return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) 99 else 100 " if/for/while without '{' 101 return ind + &sw 102 endif 103 endif 104 endif 105 106 " If the previous line doesn't need continuation on the current line we are 107 " on the start of a new statement. We have to make sure we align with the 108 " previous statement instead of just the previous line. This is a bit 109 " complicated because the previous statement might be multi-line. 110 " 111 " The start of a multiline statement can be found by: 112 " 113 " 1 If the previous line contains closing braces and has negative brace 114 " balance, search backwards until cumulative brace balance becomes zero, 115 " take indent of that line 116 " 2 If the line before the previous needs continuation search backward 117 " until that's not the case anymore. Take indent of one line down. 118 119 " Case 1 120 if prev_data =~ ')' && brace_balance < 0 121 while brace_balance != 0 122 let prev_lineno = s:Get_prev_line( prev_lineno ) 123 let prev_data = getline( prev_lineno ) 124 let brace_balance=brace_balance+s:Get_brace_balance(prev_data,'(',')' ) 125 endwhile 126 let ind = indent( prev_lineno ) 127 else 128 " Case 2 129 if s:Seems_continuing( getline( prev_lineno - 1 ) ) 130 let prev_lineno = prev_lineno - 2 131 let prev_data = getline( prev_lineno ) 132 while prev_lineno > 0 && (s:Seems_continuing( prev_data ) > 0) 133 let prev_lineno = s:Get_prev_line( prev_lineno ) 134 let prev_data = getline( prev_lineno ) 135 endwhile 136 let ind = indent( prev_lineno + 1 ) 137 endif 138 endif 139 140 " Decrease indent if this line contains a '}'. 141 if getline(v:lnum) =~ '^\s*}' 142 let ind = ind - &sw 143 endif 144 145 return ind 146endfunction 147 148" Find the open and close braces in this line and return how many more open- 149" than close braces there are. It's also used to determine cumulative balance 150" across multiple lines. 151 152function! s:Get_brace_balance( line, b_open, b_close ) 153 let line2 = substitute( a:line, a:b_open, "", "g" ) 154 let openb = strlen( a:line ) - strlen( line2 ) 155 let line3 = substitute( line2, a:b_close, "", "g" ) 156 let closeb = strlen( line2 ) - strlen( line3 ) 157 return openb - closeb 158endfunction 159 160" Find out whether the line starts with a word (i.e. keyword or function 161" call). Might need enhancements here. 162 163function! s:Starts_with_word( line ) 164 if a:line =~ '^\s*[a-zA-Z_0-9]\+\s*(' 165 return 1 166 endif 167 return 0 168endfunction 169 170" Find the length of the first word in a line. This is used to be able to 171" align a line relative to the 'print ' or 'if (' on the previous line in case 172" such a statement spans multiple lines. 173" Precondition: only to be used on lines where 'Starts_with_word' returns 1. 174 175function! s:First_word_len( line ) 176 let white_end = matchend( a:line, '^\s*' ) 177 if match( a:line, '^\s*func' ) != -1 178 let word_end = matchend( a:line, '[a-z]\+\s\+[a-zA-Z_0-9]\+[ (]*' ) 179 else 180 let word_end = matchend( a:line, '[a-zA-Z_0-9]\+[ (]*' ) 181 endif 182 return word_end - white_end 183endfunction 184 185" Determine if 'line' completes a statement or is continued on the next line. 186" This one is far from complete and accepts illegal code. Not important for 187" indenting, however. 188 189function! s:Seems_continuing( line ) 190 " Unfinished lines 191 if a:line =~ '[\\,\|\&\+\-\*\%\^]\s*$' 192 return 1 193 endif 194 " if/for/while (cond) eol 195 if a:line =~ '^\s*\(if\|while\|for\)\s*(.*)\s*$' || a:line =~ '^\s*else\s*' 196 return 2 197 endif 198 return 0 199endfunction 200 201" Get previous relevant line. Search back until a line is that is no 202" comment or blank and return the line number 203 204function! s:Get_prev_line( lineno ) 205 let lnum = a:lineno - 1 206 let data = getline( lnum ) 207 while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') 208 let lnum = lnum - 1 209 let data = getline( lnum ) 210 endwhile 211 return lnum 212endfunction 213 214" This function checks whether an indented line exceeds a maximum linewidth 215" (hardcoded 80). If so and it is possible to stay within 80 positions (or 216" limit num of characters beyond linewidth) by decreasing the indent (keeping 217" it > base_indent), do so. 218 219function! s:Safe_indent( base, wordlen, this_line ) 220 let line_base = matchend( a:this_line, '^\s*' ) 221 let line_len = strlen( a:this_line ) - line_base 222 let indent = a:base 223 if (indent + a:wordlen + line_len) > 80 224 " Simple implementation good enough for the time being 225 let indent = indent + 3 226 endif 227 return indent + a:wordlen 228endfunction 229