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