11160
|
1 " Vim indent file
|
|
2 " Language: SAS
|
|
3 " Maintainer: Zhen-Huan Hu <wildkeny@gmail.com>
|
|
4 " Version: 3.0.1
|
|
5 " Last Change: Mar 13, 2017
|
|
6
|
|
7 if exists("b:did_indent")
|
|
8 finish
|
|
9 endif
|
|
10 let b:did_indent = 1
|
|
11
|
|
12 setlocal indentexpr=GetSASIndent()
|
|
13 setlocal indentkeys+=;,=~data,=~proc,=~macro
|
|
14
|
|
15 if exists("*GetSASIndent")
|
|
16 finish
|
|
17 endif
|
|
18
|
|
19 let s:cpo_save = &cpo
|
|
20 set cpo&vim
|
|
21
|
|
22 " Regex that captures the start of a data/proc section
|
|
23 let s:section_str = '\v%(^|;)\s*%(data|proc)>'
|
|
24 " Regex that captures the end of a run-processing section
|
|
25 let s:section_run = '\v%(^|;)\s*run\s*;'
|
|
26 " Regex that captures the end of a data/proc section
|
|
27 let s:section_end = '\v%(^|;)\s*%(quit|enddata)\s*;'
|
|
28
|
|
29 " Regex that captures the start of a control block (anything inside a section)
|
|
30 let s:block_str = '\v<%(do>%([^;]+<%(to|over)>[^;]+)=|%(define|layout|method|select)>[^;]+|begingraph)\s*;'
|
|
31 " Regex that captures the end of a control block (anything inside a section)
|
|
32 let s:block_end = '\v<%(end|endlayout|endgraph)\s*;'
|
|
33
|
|
34 " Regex that captures the start of a macro
|
|
35 let s:macro_str = '\v%(^|;)\s*\%macro>'
|
|
36 " Regex that captures the end of a macro
|
|
37 let s:macro_end = '\v%(^|;)\s*\%mend\s*;'
|
|
38
|
|
39 " Regex that defines the end of the program
|
|
40 let s:program_end = '\v%(^|;)\s*endsas\s*;'
|
|
41
|
|
42 " List of procs supporting run-processing
|
|
43 let s:run_processing_procs = [
|
|
44 \ 'catalog', 'chart', 'datasets', 'document', 'ds2', 'plot', 'sql',
|
|
45 \ 'gareabar', 'gbarline', 'gchart', 'gkpi', 'gmap', 'gplot', 'gradar', 'greplay', 'gslide', 'gtile',
|
|
46 \ 'anova', 'arima', 'catmod', 'factex', 'glm', 'model', 'optex', 'plan', 'reg',
|
|
47 \ 'iml',
|
|
48 \ ]
|
|
49
|
|
50 " Find the line number of previous keyword defined by the regex
|
|
51 function! s:PrevMatch(lnum, regex)
|
|
52 let prev_lnum = prevnonblank(a:lnum - 1)
|
|
53 while prev_lnum > 0
|
|
54 let prev_line = getline(prev_lnum)
|
|
55 if prev_line =~ a:regex
|
|
56 break
|
|
57 else
|
|
58 let prev_lnum = prevnonblank(prev_lnum - 1)
|
|
59 endif
|
|
60 endwhile
|
|
61 return prev_lnum
|
|
62 endfunction
|
|
63
|
|
64 " Main function
|
|
65 function! GetSASIndent()
|
|
66 let prev_lnum = prevnonblank(v:lnum - 1)
|
|
67 if prev_lnum ==# 0
|
|
68 " Leave the indentation of the first line unchanged
|
|
69 return indent(1)
|
|
70 else
|
|
71 let prev_line = getline(prev_lnum)
|
|
72 " Previous non-blank line contains the start of a macro/section/block
|
|
73 " while not the end of a macro/section/block (at the same line)
|
|
74 if (prev_line =~ s:section_str && prev_line !~ s:section_run && prev_line !~ s:section_end) ||
|
|
75 \ (prev_line =~ s:block_str && prev_line !~ s:block_end) ||
|
|
76 \ (prev_line =~ s:macro_str && prev_line !~ s:macro_end)
|
|
77 let ind = indent(prev_lnum) + &sts
|
|
78 elseif prev_line =~ s:section_run && prev_line !~ s:section_end
|
|
79 let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str)
|
|
80 let prev_section_end_lnum = max([
|
|
81 \ s:PrevMatch(v:lnum, s:section_end),
|
|
82 \ s:PrevMatch(v:lnum, s:macro_end ),
|
|
83 \ s:PrevMatch(v:lnum, s:program_end)])
|
|
84 " Check if the section supports run-processing
|
|
85 if prev_section_end_lnum < prev_section_str_lnum &&
|
|
86 \ getline(prev_section_str_lnum) =~ '\v%(^|;)\s*proc\s+%(' .
|
|
87 \ join(s:run_processing_procs, '|') . ')>'
|
|
88 let ind = indent(prev_lnum) + &sts
|
|
89 else
|
|
90 let ind = indent(prev_lnum)
|
|
91 endif
|
|
92 else
|
|
93 let ind = indent(prev_lnum)
|
|
94 endif
|
|
95 endif
|
|
96 " Re-adjustments based on the inputs of the current line
|
|
97 let curr_line = getline(v:lnum)
|
|
98 if curr_line =~ s:program_end
|
|
99 " End of the program
|
|
100 " Same indentation as the first non-blank line
|
|
101 return indent(nextnonblank(1))
|
|
102 elseif curr_line =~ s:macro_end
|
|
103 " Current line is the end of a macro
|
|
104 " Match the indentation of the start of the macro
|
|
105 return indent(s:PrevMatch(v:lnum, s:macro_str))
|
|
106 elseif curr_line =~ s:block_end && curr_line !~ s:block_str
|
|
107 " Re-adjust if current line is the end of a block
|
|
108 " while not the beginning of a block (at the same line)
|
|
109 " Returning the indent of previous block start directly
|
|
110 " would not work due to nesting
|
|
111 let ind = ind - &sts
|
|
112 elseif curr_line =~ s:section_str || curr_line =~ s:section_run || curr_line =~ s:section_end
|
|
113 " Re-adjust if current line is the start/end of a section
|
|
114 " since the end of a section could be inexplicit
|
|
115 let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str)
|
|
116 " Check if the previous section supports run-processing
|
|
117 if getline(prev_section_str_lnum) =~ '\v%(^|;)\s*proc\s+%(' .
|
|
118 \ join(s:run_processing_procs, '|') . ')>'
|
|
119 let prev_section_end_lnum = max([
|
|
120 \ s:PrevMatch(v:lnum, s:section_end),
|
|
121 \ s:PrevMatch(v:lnum, s:macro_end ),
|
|
122 \ s:PrevMatch(v:lnum, s:program_end)])
|
|
123 else
|
|
124 let prev_section_end_lnum = max([
|
|
125 \ s:PrevMatch(v:lnum, s:section_end),
|
|
126 \ s:PrevMatch(v:lnum, s:section_run),
|
|
127 \ s:PrevMatch(v:lnum, s:macro_end ),
|
|
128 \ s:PrevMatch(v:lnum, s:program_end)])
|
|
129 endif
|
|
130 if prev_section_end_lnum < prev_section_str_lnum
|
|
131 let ind = ind - &sts
|
|
132 endif
|
|
133 endif
|
|
134 return ind
|
|
135 endfunction
|
|
136
|
|
137 let &cpo = s:cpo_save
|
|
138 unlet s:cpo_save
|