aboutsummaryrefslogtreecommitdiff
path: root/vim/bundle/vim-snipmate/autoload/snipmate/legacy.vim
blob: 7ff39cbe9b737fa5dada8eee03c465b07e5a2374 (plain)
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
138
139
let s:sigil = nr2char(31)
let snipmate#legacy#sigil = s:sigil

" Prepare snippet to be processed by s:BuildTabStops
function! snipmate#legacy#process_snippet(snip) abort
	let snippet = a:snip
	let esc_bslash = '\%(\\\@<!\%(\\\\\)*\)\@<='

	if exists('b:snipmate_visual')
		let visual = substitute(b:snipmate_visual, "\n$", '', '')
		unlet b:snipmate_visual
	else
		let visual = ''
	endif
        let snippet = s:substitute_visual(snippet, visual)

	" Evaluate eval (`...`) expressions.
	" Backquotes prefixed with a backslash "\" are ignored.
	" And backslash can be escaped by doubling it.
	" Using a loop here instead of a regex fixes a bug with nested "\=".
	if stridx(snippet, '`') != -1
		let new = []
		let snip = split(snippet, esc_bslash . '`', 1)
		let isexp = 0
		for i in snip
			if isexp
				call add(new, substitute(snipmate#util#eval(i),
                                            \ "\n\\%$", '', ''))
			else
				call add(new, i)
			endif
			let isexp = !isexp
		endfor
		let snippet = join(new, '')
		let snippet = substitute(snippet, "\r", "\n", 'g')
		let snippet = substitute(snippet, '\\`', "`", 'g')
	endif

	" Place all text after a colon in a tab stop after the tab stop
	" (e.g. "${#:foo}" becomes "${:foo}foo").
	" This helps tell the position of the tab stops later.
	let snippet = substitute(snippet, esc_bslash . '\$\({\d\+:\(.\{-}\)}\|{\d\+}\)', s:sigil . '\1\2', 'g')
	let snippet = substitute(snippet, esc_bslash . '\$\(\d\+\)', s:sigil . '\1', 'g')
	let snippet = substitute(snippet, esc_bslash . '\\\$', '$', 'g')
	let snippet = substitute(snippet, '\\\\', "\\", 'g')

	" Update the a:snip so that all the $# become the text after
	" the colon in their associated ${#}.
	" (e.g. "${1:foo}" turns all "$1"'s into "foo")
	let i = 0
	if snippet !~ s:sigil . '{0'
		let snippet .= s:sigil . '{0}'
	endif
	while snippet =~ s:sigil.'{'.i
		let s = matchstr(snippet, s:sigil . '{' . i . ':\zs.\{-}\ze}')
		if s != ''
			let snippet = substitute(snippet, s:sigil . i, s.'&', 'g')
		endif
		let i += 1
	endw

	if &et " Expand tabs to spaces if 'expandtab' is set.
		return substitute(snippet, '\t', repeat(' ', snipmate#util#tabwidth()), 'g')
	endif
	return snippet
endfunction

" Builds a list of a list of each tab stop in the snippet containing:
" 1.) The tab stop's line number.
" 2.) The tab stop's column number
"     (by getting the length of the string between the last "\n" and the
"     tab stop).
" 3.) The length of the text after the colon for the current tab stop
"     (e.g. "${1:foo}" would return 3).
" 4.) If the "${#:}" construct is given, another list containing all
"     the matches of "$#", to be replaced with the placeholder. This list is
"     composed the same way as the parent; the first item is the line number,
"     and the second is the column.
function! snipmate#legacy#build_stops(snip, lnum, col, indent) abort
	let stops = {}
	let i = 0
	let withoutVars = substitute(a:snip, s:sigil . '\d\+', '', 'g')
	while a:snip =~ s:sigil . '{' . i
		let beforeTabStop = matchstr(withoutVars, '^.*\ze'.s:sigil .'{'.i.'\D')
		let withoutOthers = substitute(withoutVars, ''.s:sigil .'{\('.i.'\D\)\@!\d\+.\{-}}', '', 'g')

		let stops[i] = {}
		let stops[i].line = a:lnum + s:count(beforeTabStop, "\n")
		let stops[i].col = a:indent + len(matchstr(withoutOthers, '[^\n]\{-}\ze'.s:sigil .'{'.i.'\D'))
		let stops[i].placeholder = 0
		let stops[i].mirrors = []
		if stops[i].line == a:lnum
			let stops[i].col += a:col
		endif

		" Get all $# matches in another list, if ${#:name} is given
		if withoutVars =~ printf('%s{%d:', s:sigil, i)
			let stops[i].placeholder = len(matchstr(withoutVars, ''.s:sigil .'{'.i.':\zs.\{-}\ze}'))
			let withoutOthers = substitute(a:snip, ''.s:sigil .'{\d\+.\{-}}\|'.s:sigil .''.i.'\@!\d\+', '', 'g')

			while match(withoutOthers, ''.s:sigil .''.i.'\(\D\|$\)') != -1
				let stops[i].mirrors = get(stops[i], 'mirrors', [])
				let beforeMark = matchstr(withoutOthers,
							\ printf('^.\{-}\ze%s%s%d\(\D\|$\)',
							\ repeat('.', stops[i].placeholder), s:sigil, i))
				let line = a:lnum + s:count(beforeMark, "\n")
				let col = a:indent + (line > a:lnum
				                           \ ? len(matchstr(beforeMark, '.*\n\zs.*'))
				                           \ : a:col + len(beforeMark))
				call add(stops[i].mirrors, { 'line' : line, 'col' : col })
				let withoutOthers = substitute(withoutOthers, ''.s:sigil .''.i.'\ze\(\D\|$\)', '', '')
			endw
		endif
		let i += 1
	endw
	let stops[i] = stops[0]
	return [stops, i + 1]
endfunction

function! s:substitute_visual(snippet, visual) abort
    let lines = []
    for line in split(a:snippet, "\n")
        let indent = matchstr(line, '^\t\+')
        call add(lines, substitute(line, '{VISUAL}',
                    \ substitute(escape(a:visual, '%\'), "\n", "\n" . indent, 'g'), 'g'))
    endfor
    return join(lines, "\n")
endfunction

" Counts occurences of haystack in needle
function! s:count(haystack, needle) abort
	let counter = 0
	let index = stridx(a:haystack, a:needle)
	while index != -1
		let index = stridx(a:haystack, a:needle, index+1)
		let counter += 1
	endw
	return counter
endfunction