xref: /vim-8.2.3635/runtime/indent/awk.vim (revision a6c27c47)
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