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
|
" 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. We don't
" strictly match the quotes either, in an effort to keep this fast.
let s:heredoc_word = '\I\i*'
let s:heredoc_open = '<<\~\?'
\ . '\('
\ . '\\\?' . 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 flags
let l:heredoc = 0
let l:pod = 0
" Start loop back through up to 512 lines of context
let l:lim = 512
let l:hpn = a:lnum > l:lim ? a:lnum - l:lim : 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 !l:heredoc && l:hpl !~# '^\s*#'
" POD switching; match any section so that we can handle long PODs
if stridx(l:hpl, '=') == 0
let l:pod = stridx(l:hpl, '=cut') != 0
" Heredoc switch on
else
let l:hpm = matchstr(l:hpl, s:heredoc_open)
if strlen(l:hpm)
let l:heredoc = 1
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 l:heredoc && l:hpl =~# '^'.l:hpw.'\>'
let l:heredoc = 0
unlet l:hpw
endif
" Bump the loop index
let l:hpn = l:hpn + 1
endwhile
" If we ended up in a heredoc, never indent.
if l:heredoc
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'
|