view runtime/indent/sas.vim @ 34625:ad1b0609b2f8 v9.1.0201

patch 9.1.0201: gM not working correctly with virt text Commit: https://github.com/vim/vim/commit/366c81a2005370ac738618d889ec0337397a9f96 Author: Dylan Thacker-Smith <dylan.ah.smith@gmail.com> Date: Sun Mar 24 09:46:56 2024 +0100 patch 9.1.0201: gM not working correctly with virt text Problem: `gM` would include outer virtual text and its padding when getting the line length used to calculate the middle of the line, putting the cursor much closer to virtual text lines. Solution: Exclude outer virtual text in getting the line length for `gM`, so that virtual text doesn't influence where the cursor is moved to (Dylan Thacker-Smith). closes: #14262 Signed-off-by: Dylan Thacker-Smith <dylan.ah.smith@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sun, 24 Mar 2024 10:00:07 +0100
parents 6dd88e45d47d
children
line wrap: on
line source

" Vim indent file
" Language:     SAS
" Maintainer:   Zhen-Huan Hu <wildkeny@gmail.com>
" Version:      3.0.3
" Last Change:  2022 Apr 06

if exists("b:did_indent")
  finish
endif
let b:did_indent = 1

setlocal indentexpr=GetSASIndent()
setlocal indentkeys+=;,=~data,=~proc,=~macro

let b:undo_indent = "setl inde< indk<"

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)
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*;'
" Regex that captures the end of a control block (anything inside a section)
let s:block_end = '\v<%(end|endcomp|endlayout|endgraph)\s*;'

" 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)
    if prev_line =~? a:regex
      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)
    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
      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 &&
            \ getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' .
            \ join(s:run_processing_procs, '|') . ')>'
        let ind = indent(prev_lnum) + shiftwidth()
      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)
  if curr_line =~? s:program_end
    " End of the program
    " Same indentation as the first non-blank line
    return indent(nextnonblank(1))
  elseif curr_line =~? s:macro_end
    " 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))
  elseif curr_line =~? s:block_end && curr_line !~? s:block_str
    " 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
    let ind = ind - shiftwidth()
  elseif curr_line =~? s:section_str || curr_line =~? s:section_run || curr_line =~? s:section_end
    " 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
    if getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' .
          \ 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
      let ind = ind - shiftwidth()
    endif
  endif
  return ind
endfunction

let &cpo = s:cpo_save
unlet s:cpo_save