| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  | " Vim indent file | 
					
						
							|  |  |  | " Language:     SAS | 
					
						
							|  |  |  | " Maintainer:   Zhen-Huan Hu <wildkeny@gmail.com> | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  | " Version:      3.0.3 | 
					
						
							| 
									
										
										
										
											2022-04-08 17:45:08 +01:00
										 |  |  | " Last Change:  2022 Apr 06 | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | if exists("b:did_indent") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | let b:did_indent = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setlocal indentexpr=GetSASIndent() | 
					
						
							|  |  |  | setlocal indentkeys+=;,=~data,=~proc,=~macro | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-08 17:45:08 +01:00
										 |  |  | let b:undo_indent = "setl inde< indk<" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  | if exists("*GetSASIndent") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | let s:cpo_save = &cpo | 
					
						
							|  |  |  | set cpo&vim | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Regex that captures the start of a data/proc section | 
					
						
							|  |  |  | let s:section_str = '\v%(^|;)\s*%(data|proc)>' | 
					
						
							|  |  |  | " Regex that captures the end of a run-processing section | 
					
						
							|  |  |  | let s:section_run = '\v%(^|;)\s*run\s*;' | 
					
						
							|  |  |  | " Regex that captures the end of a data/proc section | 
					
						
							|  |  |  | let s:section_end = '\v%(^|;)\s*%(quit|enddata)\s*;' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Regex that captures the start of a control block (anything inside a section) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  | let s:block_str = '\v<%(do>%([^;]+<%(to|over|while)>[^;]+)=|%(compute|define\s+%(column|footer|header|style|table|tagset|crosstabs|statgraph)|edit|layout|method|select)>[^;]+|begingraph)\s*;' | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  | " Regex that captures the end of a control block (anything inside a section) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  | let s:block_end = '\v<%(end|endcomp|endlayout|endgraph)\s*;' | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | " Regex that captures the start of a macro | 
					
						
							|  |  |  | let s:macro_str = '\v%(^|;)\s*\%macro>' | 
					
						
							|  |  |  | " Regex that captures the end of a macro | 
					
						
							|  |  |  | let s:macro_end = '\v%(^|;)\s*\%mend\s*;' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Regex that defines the end of the program | 
					
						
							|  |  |  | let s:program_end = '\v%(^|;)\s*endsas\s*;' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " List of procs supporting run-processing | 
					
						
							|  |  |  | let s:run_processing_procs = [ | 
					
						
							|  |  |  |       \ 'catalog', 'chart', 'datasets', 'document', 'ds2', 'plot', 'sql', | 
					
						
							|  |  |  |       \ 'gareabar', 'gbarline', 'gchart', 'gkpi', 'gmap', 'gplot', 'gradar', 'greplay', 'gslide', 'gtile', | 
					
						
							|  |  |  |       \ 'anova', 'arima', 'catmod', 'factex', 'glm', 'model', 'optex', 'plan', 'reg', | 
					
						
							|  |  |  |       \ 'iml', | 
					
						
							|  |  |  |       \ ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Find the line number of previous keyword defined by the regex | 
					
						
							|  |  |  | function! s:PrevMatch(lnum, regex) | 
					
						
							|  |  |  |   let prev_lnum = prevnonblank(a:lnum - 1) | 
					
						
							|  |  |  |   while prev_lnum > 0 | 
					
						
							|  |  |  |     let prev_line = getline(prev_lnum) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |     if prev_line =~? a:regex | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |       break | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       let prev_lnum = prevnonblank(prev_lnum - 1) | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  |   return prev_lnum | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Main function | 
					
						
							|  |  |  | function! GetSASIndent() | 
					
						
							|  |  |  |   let prev_lnum = prevnonblank(v:lnum - 1) | 
					
						
							|  |  |  |   if prev_lnum ==# 0 | 
					
						
							|  |  |  |     " Leave the indentation of the first line unchanged | 
					
						
							|  |  |  |     return indent(1) | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     let prev_line = getline(prev_lnum) | 
					
						
							|  |  |  |     " Previous non-blank line contains the start of a macro/section/block | 
					
						
							|  |  |  |     " while not the end of a macro/section/block (at the same line) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |     if (prev_line =~? s:section_str && prev_line !~? s:section_run && prev_line !~? s:section_end) || | 
					
						
							|  |  |  |           \ (prev_line =~? s:block_str && prev_line !~? s:block_end) || | 
					
						
							|  |  |  |           \ (prev_line =~? s:macro_str && prev_line !~? s:macro_end) | 
					
						
							|  |  |  |       let ind = indent(prev_lnum) + shiftwidth() | 
					
						
							|  |  |  |     elseif prev_line =~? s:section_run && prev_line !~? s:section_end | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |       let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str) | 
					
						
							|  |  |  |       let prev_section_end_lnum = max([ | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:section_end), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:macro_end  ), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:program_end)]) | 
					
						
							|  |  |  |       " Check if the section supports run-processing | 
					
						
							|  |  |  |       if prev_section_end_lnum < prev_section_str_lnum && | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |             \ getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' . | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |             \ join(s:run_processing_procs, '|') . ')>' | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |         let ind = indent(prev_lnum) + shiftwidth() | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |       else | 
					
						
							|  |  |  |         let ind = indent(prev_lnum) | 
					
						
							|  |  |  |       endif | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       let ind = indent(prev_lnum) | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  |   " Re-adjustments based on the inputs of the current line | 
					
						
							|  |  |  |   let curr_line = getline(v:lnum) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |   if curr_line =~? s:program_end | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |     " End of the program | 
					
						
							|  |  |  |     " Same indentation as the first non-blank line | 
					
						
							|  |  |  |     return indent(nextnonblank(1)) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |   elseif curr_line =~? s:macro_end | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |     " Current line is the end of a macro | 
					
						
							|  |  |  |     " Match the indentation of the start of the macro | 
					
						
							|  |  |  |     return indent(s:PrevMatch(v:lnum, s:macro_str)) | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |   elseif curr_line =~? s:block_end && curr_line !~? s:block_str | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |     " Re-adjust if current line is the end of a block | 
					
						
							|  |  |  |     " while not the beginning of a block (at the same line) | 
					
						
							|  |  |  |     " Returning the indent of previous block start directly | 
					
						
							|  |  |  |     " would not work due to nesting | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |     let ind = ind - shiftwidth() | 
					
						
							|  |  |  |   elseif curr_line =~? s:section_str || curr_line =~? s:section_run || curr_line =~? s:section_end | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |     " Re-adjust if current line is the start/end of a section | 
					
						
							|  |  |  |     " since the end of a section could be inexplicit | 
					
						
							|  |  |  |     let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str) | 
					
						
							|  |  |  |     " Check if the previous section supports run-processing | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |     if getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' . | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |           \ join(s:run_processing_procs, '|') . ')>' | 
					
						
							|  |  |  |       let prev_section_end_lnum = max([ | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:section_end), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:macro_end  ), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:program_end)]) | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       let prev_section_end_lnum = max([ | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:section_end), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:section_run), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:macro_end  ), | 
					
						
							|  |  |  |             \ s:PrevMatch(v:lnum, s:program_end)]) | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  |     if prev_section_end_lnum < prev_section_str_lnum | 
					
						
							| 
									
										
										
										
											2018-07-03 16:42:19 +02:00
										 |  |  |       let ind = ind - shiftwidth() | 
					
						
							| 
									
										
										
										
											2017-03-16 17:41:02 +01:00
										 |  |  |     endif | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  |   return ind | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | let &cpo = s:cpo_save | 
					
						
							|  |  |  | unlet s:cpo_save |