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