home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / runtime / dos / indent / awk.vim < prev    next >
Encoding:
Text File  |  2012-05-31  |  7.8 KB  |  233 lines

  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.  
  28. " Only load this indent file when no other was loaded.
  29. if exists("b:did_indent")
  30.     finish
  31. endif
  32.  
  33. let b:did_indent = 1
  34.  
  35. setlocal indentexpr=GetAwkIndent()
  36. " Mmm, copied from the tcl indent program. Is this okay?
  37. setlocal indentkeys-=:,0#
  38.  
  39. " Only define the function once.
  40. if exists("*GetAwkIndent")
  41.     finish
  42. endif
  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.  
  48. function! GetAwkIndent()
  49.  
  50.    " Find previous line and get it's 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 + &sw
  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 + &sw
  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 - &sw
  144.    endif
  145.  
  146.    return ind
  147. endfunction
  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.  
  153. function! 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
  159. endfunction
  160.  
  161. " Find out whether the line starts with a word (i.e. keyword or function
  162. " call). Might need enhancements here.
  163.  
  164. function! s:Starts_with_word( line )
  165.   if a:line =~ '^\s*[a-zA-Z_0-9]\+\s*('
  166.      return 1
  167.   endif
  168.   return 0
  169. endfunction
  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.  
  176. function! 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
  184. endfunction
  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.  
  190. function! 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
  203. endfunction
  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.  
  208. function! 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
  216. endfunction
  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.  
  223. function! 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
  232. endfunction
  233.