aboutsummaryrefslogtreecommitdiff
path: root/vim/indent/perl.vim
blob: 9a8f30e152d3763d4489da1ee6c3bfeb8ae85cb0 (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
" 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()
setlocal indentkeys=o,O,0},0),0]

" 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()

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

  " Heredoc detection; start at top of buffer
  let l:hn = 0
  while l:hn < v:lnum
    let l:hl = getline(l:hn)

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

      " Line opens with a heredoc
      let l:hm = matchstr(l:hl, s:heredoc_open)

      " Store the heredoc word and make this our indent reference
      if strlen(l:hm)
        let l:hw = matchstr(l:hm, s:heredoc_word)
        let l:pn = l:hn
      endif

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

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

  endwhile

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

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

  " 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 = exists('*shiftwidth')
        \ ? shiftwidth()
        \ : &shiftwidth

  " 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

  " Preserve base indent after a semicolon or a hash element assignment
  elseif l:pl =~# '[;,]\s*$'
        \ || l:pl =~# '^\s=>.*,\s*$'
        \ || l:pl =~# '^\s*sub\>.*{.*}\s+$'
    return l:pb

  " Line-continuation: indent half a 'shiftwidth'
  else
    return l:pb + l:sw / 2

  endif

endfunction

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