1071d4279SBram Moolenaar" vim: set sw=3 sts=3: 2071d4279SBram Moolenaar 3071d4279SBram Moolenaar" Awk indent script. It can handle multi-line statements and expressions. 4071d4279SBram Moolenaar" It works up to the point where the distinction between correct/incorrect 5071d4279SBram Moolenaar" and personal taste gets fuzzy. Drop me an e-mail for bug reports and 6071d4279SBram Moolenaar" reasonable style suggestions. 7071d4279SBram Moolenaar" 8071d4279SBram Moolenaar" Bugs: 9071d4279SBram Moolenaar" ===== 10071d4279SBram Moolenaar" - Some syntax errors may cause erratic indentation. 11071d4279SBram Moolenaar" - Same for very unusual but syntacticly correct use of { } 12071d4279SBram Moolenaar" - In some cases it's confused by the use of ( and { in strings constants 13071d4279SBram Moolenaar" - This version likes the closing brace of a multiline pattern-action be on 14071d4279SBram Moolenaar" character position 1 before the following pattern-action combination is 15071d4279SBram Moolenaar" formatted 16071d4279SBram Moolenaar 17071d4279SBram Moolenaar" Author: 18071d4279SBram Moolenaar" ======= 19071d4279SBram Moolenaar" Erik Janssen, [email protected] 20071d4279SBram Moolenaar" 21071d4279SBram Moolenaar" History: 22071d4279SBram Moolenaar" ======== 23071d4279SBram Moolenaar" 26-04-2002 Got initial version working reasonably well 24071d4279SBram Moolenaar" 29-04-2002 Fixed problems in function headers and max line width 25071d4279SBram Moolenaar" Added support for two-line if's without curly braces 265302d9ebSBram Moolenaar" Fixed hang: 2011 Aug 31 27071d4279SBram Moolenaar 28071d4279SBram Moolenaar" Only load this indent file when no other was loaded. 29071d4279SBram Moolenaarif exists("b:did_indent") 30071d4279SBram Moolenaar finish 31071d4279SBram Moolenaarendif 32071d4279SBram Moolenaar 33071d4279SBram Moolenaarlet b:did_indent = 1 34071d4279SBram Moolenaar 35071d4279SBram Moolenaarsetlocal indentexpr=GetAwkIndent() 36071d4279SBram Moolenaar" Mmm, copied from the tcl indent program. Is this okay? 37071d4279SBram Moolenaarsetlocal indentkeys-=:,0# 38071d4279SBram Moolenaar 39071d4279SBram Moolenaar" Only define the function once. 40071d4279SBram Moolenaarif exists("*GetAwkIndent") 41071d4279SBram Moolenaar finish 42071d4279SBram Moolenaarendif 43071d4279SBram Moolenaar 44071d4279SBram Moolenaar" This function contains a lot of exit points. It checks for simple cases 45071d4279SBram Moolenaar" first to get out of the function as soon as possible, thereby reducing the 46071d4279SBram Moolenaar" number of possibilities later on in the difficult parts 47071d4279SBram Moolenaar 48071d4279SBram Moolenaarfunction! GetAwkIndent() 49071d4279SBram Moolenaar 50*a6c27c47SBram Moolenaar " Find previous line and get its indentation 51071d4279SBram Moolenaar let prev_lineno = s:Get_prev_line( v:lnum ) 52071d4279SBram Moolenaar if prev_lineno == 0 53071d4279SBram Moolenaar return 0 54071d4279SBram Moolenaar endif 55071d4279SBram Moolenaar let prev_data = getline( prev_lineno ) 56071d4279SBram Moolenaar let ind = indent( prev_lineno ) 57071d4279SBram Moolenaar 58071d4279SBram Moolenaar " Increase indent if the previous line contains an opening brace. Search 59071d4279SBram Moolenaar " for this brace the hard way to prevent errors if the previous line is a 60071d4279SBram Moolenaar " 'pattern { action }' (simple check match on /{/ increases the indent then) 61071d4279SBram Moolenaar 62071d4279SBram Moolenaar if s:Get_brace_balance( prev_data, '{', '}' ) > 0 633ec574f2SBram Moolenaar return ind + shiftwidth() 64071d4279SBram Moolenaar endif 65071d4279SBram Moolenaar 66071d4279SBram Moolenaar let brace_balance = s:Get_brace_balance( prev_data, '(', ')' ) 67071d4279SBram Moolenaar 68071d4279SBram Moolenaar " If prev line has positive brace_balance and starts with a word (keyword 69071d4279SBram Moolenaar " or function name), align the current line on the first '(' of the prev 70071d4279SBram Moolenaar " line 71071d4279SBram Moolenaar 72071d4279SBram Moolenaar if brace_balance > 0 && s:Starts_with_word( prev_data ) 73071d4279SBram Moolenaar return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) 74071d4279SBram Moolenaar endif 75071d4279SBram Moolenaar 76071d4279SBram Moolenaar " If this line starts with an open brace bail out now before the line 77071d4279SBram Moolenaar " continuation checks. 78071d4279SBram Moolenaar 79071d4279SBram Moolenaar if getline( v:lnum ) =~ '^\s*{' 80071d4279SBram Moolenaar return ind 81071d4279SBram Moolenaar endif 82071d4279SBram Moolenaar 83071d4279SBram Moolenaar " If prev line seems to be part of multiline statement: 84071d4279SBram Moolenaar " 1. Prev line is first line of a multiline statement 85071d4279SBram Moolenaar " -> attempt to indent on first ' ' or '(' of prev line, just like we 86071d4279SBram Moolenaar " indented the positive brace balance case above 87071d4279SBram Moolenaar " 2. Prev line is not first line of a multiline statement 88071d4279SBram Moolenaar " -> copy indent of prev line 89071d4279SBram Moolenaar 90071d4279SBram Moolenaar let continue_mode = s:Seems_continuing( prev_data ) 91071d4279SBram Moolenaar if continue_mode > 0 92071d4279SBram Moolenaar if s:Seems_continuing( getline(s:Get_prev_line( prev_lineno )) ) 93071d4279SBram Moolenaar " Case 2 94071d4279SBram Moolenaar return ind 95071d4279SBram Moolenaar else 96071d4279SBram Moolenaar " Case 1 97071d4279SBram Moolenaar if continue_mode == 1 98071d4279SBram Moolenaar " Need continuation due to comma, backslash, etc 99071d4279SBram Moolenaar return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) 100071d4279SBram Moolenaar else 101071d4279SBram Moolenaar " if/for/while without '{' 1023ec574f2SBram Moolenaar return ind + shiftwidth() 103071d4279SBram Moolenaar endif 104071d4279SBram Moolenaar endif 105071d4279SBram Moolenaar endif 106071d4279SBram Moolenaar 107071d4279SBram Moolenaar " If the previous line doesn't need continuation on the current line we are 108071d4279SBram Moolenaar " on the start of a new statement. We have to make sure we align with the 109071d4279SBram Moolenaar " previous statement instead of just the previous line. This is a bit 110071d4279SBram Moolenaar " complicated because the previous statement might be multi-line. 111071d4279SBram Moolenaar " 112071d4279SBram Moolenaar " The start of a multiline statement can be found by: 113071d4279SBram Moolenaar " 114071d4279SBram Moolenaar " 1 If the previous line contains closing braces and has negative brace 115071d4279SBram Moolenaar " balance, search backwards until cumulative brace balance becomes zero, 116071d4279SBram Moolenaar " take indent of that line 117071d4279SBram Moolenaar " 2 If the line before the previous needs continuation search backward 118071d4279SBram Moolenaar " until that's not the case anymore. Take indent of one line down. 119071d4279SBram Moolenaar 120071d4279SBram Moolenaar " Case 1 121071d4279SBram Moolenaar if prev_data =~ ')' && brace_balance < 0 1225302d9ebSBram Moolenaar while brace_balance != 0 && prev_lineno > 0 123071d4279SBram Moolenaar let prev_lineno = s:Get_prev_line( prev_lineno ) 124071d4279SBram Moolenaar let prev_data = getline( prev_lineno ) 125071d4279SBram Moolenaar let brace_balance=brace_balance+s:Get_brace_balance(prev_data,'(',')' ) 126071d4279SBram Moolenaar endwhile 127071d4279SBram Moolenaar let ind = indent( prev_lineno ) 128071d4279SBram Moolenaar else 129071d4279SBram Moolenaar " Case 2 130071d4279SBram Moolenaar if s:Seems_continuing( getline( prev_lineno - 1 ) ) 131071d4279SBram Moolenaar let prev_lineno = prev_lineno - 2 132071d4279SBram Moolenaar let prev_data = getline( prev_lineno ) 133071d4279SBram Moolenaar while prev_lineno > 0 && (s:Seems_continuing( prev_data ) > 0) 134071d4279SBram Moolenaar let prev_lineno = s:Get_prev_line( prev_lineno ) 135071d4279SBram Moolenaar let prev_data = getline( prev_lineno ) 136071d4279SBram Moolenaar endwhile 137071d4279SBram Moolenaar let ind = indent( prev_lineno + 1 ) 138071d4279SBram Moolenaar endif 139071d4279SBram Moolenaar endif 140071d4279SBram Moolenaar 141071d4279SBram Moolenaar " Decrease indent if this line contains a '}'. 142071d4279SBram Moolenaar if getline(v:lnum) =~ '^\s*}' 1433ec574f2SBram Moolenaar let ind = ind - shiftwidth() 144071d4279SBram Moolenaar endif 145071d4279SBram Moolenaar 146071d4279SBram Moolenaar return ind 147071d4279SBram Moolenaarendfunction 148071d4279SBram Moolenaar 149071d4279SBram Moolenaar" Find the open and close braces in this line and return how many more open- 150071d4279SBram Moolenaar" than close braces there are. It's also used to determine cumulative balance 151071d4279SBram Moolenaar" across multiple lines. 152071d4279SBram Moolenaar 153071d4279SBram Moolenaarfunction! s:Get_brace_balance( line, b_open, b_close ) 154071d4279SBram Moolenaar let line2 = substitute( a:line, a:b_open, "", "g" ) 155071d4279SBram Moolenaar let openb = strlen( a:line ) - strlen( line2 ) 156071d4279SBram Moolenaar let line3 = substitute( line2, a:b_close, "", "g" ) 157071d4279SBram Moolenaar let closeb = strlen( line2 ) - strlen( line3 ) 158071d4279SBram Moolenaar return openb - closeb 159071d4279SBram Moolenaarendfunction 160071d4279SBram Moolenaar 161071d4279SBram Moolenaar" Find out whether the line starts with a word (i.e. keyword or function 162071d4279SBram Moolenaar" call). Might need enhancements here. 163071d4279SBram Moolenaar 164071d4279SBram Moolenaarfunction! s:Starts_with_word( line ) 165071d4279SBram Moolenaar if a:line =~ '^\s*[a-zA-Z_0-9]\+\s*(' 166071d4279SBram Moolenaar return 1 167071d4279SBram Moolenaar endif 168071d4279SBram Moolenaar return 0 169071d4279SBram Moolenaarendfunction 170071d4279SBram Moolenaar 171071d4279SBram Moolenaar" Find the length of the first word in a line. This is used to be able to 172071d4279SBram Moolenaar" align a line relative to the 'print ' or 'if (' on the previous line in case 173071d4279SBram Moolenaar" such a statement spans multiple lines. 174071d4279SBram Moolenaar" Precondition: only to be used on lines where 'Starts_with_word' returns 1. 175071d4279SBram Moolenaar 176071d4279SBram Moolenaarfunction! s:First_word_len( line ) 177071d4279SBram Moolenaar let white_end = matchend( a:line, '^\s*' ) 178071d4279SBram Moolenaar if match( a:line, '^\s*func' ) != -1 179071d4279SBram Moolenaar let word_end = matchend( a:line, '[a-z]\+\s\+[a-zA-Z_0-9]\+[ (]*' ) 180071d4279SBram Moolenaar else 181071d4279SBram Moolenaar let word_end = matchend( a:line, '[a-zA-Z_0-9]\+[ (]*' ) 182071d4279SBram Moolenaar endif 183071d4279SBram Moolenaar return word_end - white_end 184071d4279SBram Moolenaarendfunction 185071d4279SBram Moolenaar 186071d4279SBram Moolenaar" Determine if 'line' completes a statement or is continued on the next line. 187071d4279SBram Moolenaar" This one is far from complete and accepts illegal code. Not important for 188071d4279SBram Moolenaar" indenting, however. 189071d4279SBram Moolenaar 190071d4279SBram Moolenaarfunction! s:Seems_continuing( line ) 191071d4279SBram Moolenaar " Unfinished lines 192d960d76dSBram Moolenaar if a:line =~ '\(--\|++\)\s*$' 193d960d76dSBram Moolenaar return 0 194d960d76dSBram Moolenaar endif 195071d4279SBram Moolenaar if a:line =~ '[\\,\|\&\+\-\*\%\^]\s*$' 196071d4279SBram Moolenaar return 1 197071d4279SBram Moolenaar endif 198071d4279SBram Moolenaar " if/for/while (cond) eol 199071d4279SBram Moolenaar if a:line =~ '^\s*\(if\|while\|for\)\s*(.*)\s*$' || a:line =~ '^\s*else\s*' 200071d4279SBram Moolenaar return 2 201071d4279SBram Moolenaar endif 202071d4279SBram Moolenaar return 0 203071d4279SBram Moolenaarendfunction 204071d4279SBram Moolenaar 205071d4279SBram Moolenaar" Get previous relevant line. Search back until a line is that is no 206071d4279SBram Moolenaar" comment or blank and return the line number 207071d4279SBram Moolenaar 208071d4279SBram Moolenaarfunction! s:Get_prev_line( lineno ) 209071d4279SBram Moolenaar let lnum = a:lineno - 1 210071d4279SBram Moolenaar let data = getline( lnum ) 211071d4279SBram Moolenaar while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') 212071d4279SBram Moolenaar let lnum = lnum - 1 213071d4279SBram Moolenaar let data = getline( lnum ) 214071d4279SBram Moolenaar endwhile 215071d4279SBram Moolenaar return lnum 216071d4279SBram Moolenaarendfunction 217071d4279SBram Moolenaar 218071d4279SBram Moolenaar" This function checks whether an indented line exceeds a maximum linewidth 219071d4279SBram Moolenaar" (hardcoded 80). If so and it is possible to stay within 80 positions (or 220071d4279SBram Moolenaar" limit num of characters beyond linewidth) by decreasing the indent (keeping 221071d4279SBram Moolenaar" it > base_indent), do so. 222071d4279SBram Moolenaar 223071d4279SBram Moolenaarfunction! s:Safe_indent( base, wordlen, this_line ) 224071d4279SBram Moolenaar let line_base = matchend( a:this_line, '^\s*' ) 225071d4279SBram Moolenaar let line_len = strlen( a:this_line ) - line_base 226071d4279SBram Moolenaar let indent = a:base 227071d4279SBram Moolenaar if (indent + a:wordlen + line_len) > 80 228071d4279SBram Moolenaar " Simple implementation good enough for the time being 229071d4279SBram Moolenaar let indent = indent + 3 230071d4279SBram Moolenaar endif 231071d4279SBram Moolenaar return indent + a:wordlen 232071d4279SBram Moolenaarendfunction 233