2017-11-05 21:26:57 -07:00
|
|
|
" Vim indent file
|
|
|
|
" Language: SAS
|
|
|
|
" Maintainer: Zhen-Huan Hu <wildkeny@gmail.com>
|
2018-10-29 01:37:48 -07:00
|
|
|
" Version: 3.0.3
|
2022-04-08 10:53:41 -07:00
|
|
|
" Last Change: 2022 Apr 06
|
2017-11-05 21:26:57 -07:00
|
|
|
|
|
|
|
if exists("b:did_indent")
|
|
|
|
finish
|
|
|
|
endif
|
|
|
|
let b:did_indent = 1
|
|
|
|
|
|
|
|
setlocal indentexpr=GetSASIndent()
|
|
|
|
setlocal indentkeys+=;,=~data,=~proc,=~macro
|
|
|
|
|
2022-04-08 10:53:41 -07:00
|
|
|
let b:undo_indent = "setl inde< indk<"
|
|
|
|
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07: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-11-05 21:26:57 -07:00
|
|
|
" Regex that captures the end of a control block (anything inside a section)
|
2018-10-29 01:37:48 -07:00
|
|
|
let s:block_end = '\v<%(end|endcomp|endlayout|endgraph)\s*;'
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
if prev_line =~? a:regex
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07: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-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
\ getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' .
|
2017-11-05 21:26:57 -07:00
|
|
|
\ join(s:run_processing_procs, '|') . ')>'
|
2018-10-29 01:37:48 -07:00
|
|
|
let ind = indent(prev_lnum) + shiftwidth()
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
if curr_line =~? s:program_end
|
2017-11-05 21:26:57 -07:00
|
|
|
" End of the program
|
|
|
|
" Same indentation as the first non-blank line
|
|
|
|
return indent(nextnonblank(1))
|
2018-10-29 01:37:48 -07:00
|
|
|
elseif curr_line =~? s:macro_end
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
elseif curr_line =~? s:block_end && curr_line !~? s:block_str
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
let ind = ind - shiftwidth()
|
|
|
|
elseif curr_line =~? s:section_str || curr_line =~? s:section_run || curr_line =~? s:section_end
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
if getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' .
|
2017-11-05 21:26:57 -07: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-10-29 01:37:48 -07:00
|
|
|
let ind = ind - shiftwidth()
|
2017-11-05 21:26:57 -07:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
return ind
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let &cpo = s:cpo_save
|
|
|
|
unlet s:cpo_save
|