aboutsummaryrefslogtreecommitdiff
path: root/vim/indent/perl.vim
blob: 14a211f5fe071f2365aaf1404579bf3507a5162b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
" Custom Vim indent file for Perl5; the stock one didn't suit me.

" Only load this indent file when no other was loaded.
if exists('b:did_indent') || &compatible
  finish
endif
let b:did_indent = 1

" Indent settings
setlocal indentexpr=GetPerlIndent(v:lnum)
setlocal indentkeys=o,O,0=,0=},0=),0=],0=&&,0=\|\|,0=//,0=?,0=:,<Space>

" Build patterns for heredoc indenting; note that we detect indented heredocs
" with tildes like <<~EOF, but we don't treat them any differently; note also
" a semicolon is required
let s:heredoc_word = '\I\i*'
let s:heredoc_open = '<<\~\?'
      \ . '\('
      \ . '\\\?' . s:heredoc_word
      \ . '\|'
      \ . "'" . s:heredoc_word . "'"
      \ . '\|'
      \ . '"' . s:heredoc_word . '"'
      \ . '\|'
      \ . '`' . s:heredoc_word . '`'
      \ . '\)'
      \ . '.*;\s*$'

" Define indent function
function! GetPerlIndent(lnum)

  " Get previous line, bail if none
  let l:pn = prevnonblank(a:lnum - 1)
  if !l:pn
    return 0
  endif

  " Heredoc and POD detection; this is expensive, so limit it to 512 lines of
  " context
  let l:lim = 512
  let l:hpn = line('$') > l:lim
        \ ? line('$') - l:lim
        \ : 0
  let l:pod = 0
  while l:hpn < a:lnum
    let l:hpl = getline(l:hpn)

    " If we're not in a heredoc and not in a comment ...
    if !exists('l:hpw') && l:hpl !~# '^\s*#'

      " POD switching; match any section so that we can handle long PODs
      if !l:pod && l:hpl =~# '^=\l\+\d\?\>'
        let l:pod = 1
      elseif l:pod && stridx(l:hpl, '=cut') == 0
        let l:pod = 0

      " Heredoc switch on
      else
        let l:hpm = matchstr(l:hpl, s:heredoc_open)
        if strlen(l:hpm)
          let l:hpw = matchstr(l:hpm, s:heredoc_word)
          let l:pn = l:hpn
        endif
      endif

    " If we are in a heredoc and we found the token word, finish it
    elseif exists('l:hpw') && l:hpl =~# '^'.l:hpw.'\>'
      unlet l:hpw
    endif

    " Bump the loop index
    let l:hpn = l:hpn + 1

  endwhile

  " If we ended up in a heredoc, return 0 for the indent.
  if exists('l:hpw')
    return 0
  endif

  " If we're in POD, just autoindent; simple and good enough.
  if l:pod
    return indent(a:lnum - 1)
  endif

  " Get data of previous non-blank and non-heredoc line
  let l:pl = getline(l:pn)
  let l:pi = indent(l:pn)

  " Get value of 'shiftwidth'
  let l:sw = &shiftwidth
        \ ? &shiftwidth
        \ : &tabstop

  " Get current line properties
  let l:cl = getline(a:lnum)

  " Base indent with any fractional indent removed
  let l:pb = l:pi - l:pi % l:sw

  " Just follow comment indent
  if l:pl =~# '^\s*#'
    return l:pi

  " Move out with closing brace
  elseif l:cl =~# '^\s*[])}]'
    return l:pb >= l:sw ? l:pb - l:sw : 0

  " Move in after opening brace
  elseif l:pl =~# '[{([]\s*$'
    return l:pb + l:sw

  " Never continue after a semicolon or a double-underscore
  elseif l:pl =~# '\;\s*$'
        \ || l:pl =~# '__DATA__'
        \ || l:pl =~# '__END__'
    return l:pb

  " Line continuation hints
  elseif l:pl =~# '[^])},]\s*$'
        \ || l:cl =~# '^\s*\(and\|or\|xor\)'
        \ || l:cl =~# '^\s*\(&&\|||\|//\)'
        \ || l:cl =~# '^\s*[?:=]'
    return l:pb + l:sw / 2

  " Default to indent of previous line
  else
    return l:pb

  endif

endfunction

" How to undo all of that
let b:undo_indent = 'setlocal indentexpr<'
      \ . '|setlocal indentkeys<'
      \ . '|delfunction GetPerlIndent'