1" Vim indent file
2" Language:		Meson
3" License:		VIM License
4" Maintainer:		Nirbheek Chauhan <nirbheek.chauhan@gmail.com>
5" Original Authors:	David Bustos <bustos@caltech.edu>
6"			Bram Moolenaar <Bram@vim.org>
7" Last Change:		2015 Feb 23
8
9" Only load this indent file when no other was loaded.
10if exists("b:did_indent")
11  finish
12endif
13let b:did_indent = 1
14
15" Some preliminary settings
16setlocal nolisp		" Make sure lisp indenting doesn't supersede us
17setlocal autoindent	" indentexpr isn't much help otherwise
18
19setlocal indentexpr=GetMesonIndent(v:lnum)
20setlocal indentkeys+==elif,=else,=endforeach,=endif,0)
21
22" Only define the function once.
23if exists("*GetMesonIndent")
24  finish
25endif
26let s:keepcpo= &cpo
27setlocal cpo&vim
28
29" Come here when loading the script the first time.
30
31let s:maxoff = 50	" maximum number of lines to look backwards for ()
32
33function GetMesonIndent(lnum)
34  echom getline(line("."))
35
36  " If this line is explicitly joined: If the previous line was also joined,
37  " line it up with that one, otherwise add two 'shiftwidth'
38  if getline(a:lnum - 1) =~ '\\$'
39    if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$'
40      return indent(a:lnum - 1)
41    endif
42    return indent(a:lnum - 1) + (exists("g:mesonindent_continue") ? eval(g:mesonindent_continue) : (shiftwidth() * 2))
43  endif
44
45  " If the start of the line is in a string don't change the indent.
46  if has('syntax_items')
47	\ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$"
48    return -1
49  endif
50
51  " Search backwards for the previous non-empty line.
52  let plnum = prevnonblank(v:lnum - 1)
53
54  if plnum == 0
55    " This is the first non-empty line, use zero indent.
56    return 0
57  endif
58
59  " If the previous line is inside parenthesis, use the indent of the starting
60  " line.
61  " Trick: use the non-existing "dummy" variable to break out of the loop when
62  " going too far back.
63  call cursor(plnum, 1)
64  let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW',
65	  \ "line('.') < " . (plnum - s:maxoff) . " ? dummy :"
66	  \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')"
67	  \ . " =~ '\\(Comment\\|Todo\\|String\\)$'")
68  if parlnum > 0
69    let plindent = indent(parlnum)
70    let plnumstart = parlnum
71  else
72    let plindent = indent(plnum)
73    let plnumstart = plnum
74  endif
75
76
77  " When inside parenthesis: If at the first line below the parenthesis add
78  " a 'shiftwidth', otherwise same as previous line.
79  " i = (a
80  "       + b
81  "       + c)
82  call cursor(a:lnum, 1)
83  let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW',
84	  \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :"
85	  \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')"
86	  \ . " =~ '\\(Comment\\|Todo\\|String\\)$'")
87  if p > 0
88    if p == plnum
89      " When the start is inside parenthesis, only indent one 'shiftwidth'.
90      let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW',
91	  \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :"
92	  \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')"
93	  \ . " =~ '\\(Comment\\|Todo\\|String\\)$'")
94      if pp > 0
95	return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth())
96      endif
97      return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : shiftwidth())
98    endif
99    if plnumstart == p
100      return indent(plnum)
101    endif
102    return plindent
103  endif
104
105
106  " Get the line and remove a trailing comment.
107  " Use syntax highlighting attributes when possible.
108  let pline = getline(plnum)
109  let pline_len = strlen(pline)
110  if has('syntax_items')
111    " If the last character in the line is a comment, do a binary search for
112    " the start of the comment.  synID() is slow, a linear search would take
113    " too long on a long line.
114    if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$"
115      let min = 1
116      let max = pline_len
117      while min < max
118	let col = (min + max) / 2
119	if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$"
120	  let max = col
121	else
122	  let min = col + 1
123	endif
124      endwhile
125      let pline = strpart(pline, 0, min - 1)
126    endif
127  else
128    let col = 0
129    while col < pline_len
130      if pline[col] == '#'
131	let pline = strpart(pline, 0, col)
132	break
133      endif
134      let col = col + 1
135    endwhile
136  endif
137
138  " If the previous line ended the conditional/loop
139  if getline(plnum) =~ '^\s*\(endif\|endforeach\)\>\s*'
140    " Maintain indent
141    return -1
142  endif
143
144  " If the previous line ended with a builtin, indent this line
145  if pline =~ '^\s*\(foreach\|if\|else\|elif\)\>\s*'
146    return plindent + shiftwidth()
147  endif
148
149  " If the current line begins with a header keyword, deindent
150  if getline(a:lnum) =~ '^\s*\(else\|elif\|endif\|endforeach\)'
151
152    " Unless the previous line was a one-liner
153    if getline(plnumstart) =~ '^\s*\(foreach\|if\)\>\s*'
154      return plindent
155    endif
156
157    " Or the user has already dedented
158    if indent(a:lnum) <= plindent - shiftwidth()
159      return -1
160    endif
161
162    return plindent - shiftwidth()
163  endif
164
165  " When after a () construct we probably want to go back to the start line.
166  " a = (b
167  "       + c)
168  " here
169  if parlnum > 0
170    return plindent
171  endif
172
173  return -1
174
175endfunction
176
177let &cpo = s:keepcpo
178unlet s:keepcpo
179
180" vim:sw=2
181