You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

564 lines
16 KiB

  1. "=============================================================================
  2. " FILE: view.vim
  3. " AUTHOR: Shougo Matsushita <Shougo.Matsu@gmail.com>
  4. " License: MIT license
  5. "=============================================================================
  6. let s:save_cpo = &cpo
  7. set cpo&vim
  8. function! neosnippet#view#_expand(cur_text, col, trigger_name) abort "{{{
  9. let snippets = neosnippet#helpers#get_snippets()
  10. if a:trigger_name == '' || !has_key(snippets, a:trigger_name)
  11. let pos = getpos('.')
  12. let pos[2] = len(a:cur_text)+1
  13. call setpos('.', pos)
  14. if pos[2] < col('$')
  15. startinsert
  16. else
  17. startinsert!
  18. endif
  19. return
  20. endif
  21. let snippet = snippets[a:trigger_name]
  22. let cur_text = a:cur_text[: -1-len(a:trigger_name)]
  23. call neosnippet#view#_insert(snippet.snip, snippet.options, cur_text, a:col)
  24. endfunction"}}}
  25. function! neosnippet#view#_insert(snippet, options, cur_text, col) abort "{{{
  26. let options = extend(
  27. \ neosnippet#parser#_initialize_snippet_options(),
  28. \ a:options)
  29. let snip_word = a:snippet
  30. if snip_word =~ '\\\@<!`.*\\\@<!`'
  31. let snip_word = s:eval_snippet(snip_word)
  32. endif
  33. " Substitute markers.
  34. let snip_word = substitute(snip_word,
  35. \ neosnippet#get_placeholder_marker_substitute_pattern(),
  36. \ '<`\1`>', 'g')
  37. let snip_word = substitute(snip_word,
  38. \ neosnippet#get_mirror_placeholder_marker_substitute_pattern(),
  39. \ '<|\1|>', 'g')
  40. " Substitute escaped characters.
  41. let snip_word = substitute(snip_word, '\\\(\\\|`\|\$\)', '\1', 'g')
  42. " Insert snippets.
  43. let next_line = getline('.')[a:col-1 :]
  44. let snippet_lines = split(snip_word, '\n', 1)
  45. if empty(snippet_lines)
  46. return
  47. endif
  48. let begin_line = line('.')
  49. let end_line = line('.') + len(snippet_lines) - 1
  50. let snippet_lines[0] = a:cur_text . snippet_lines[0]
  51. let snippet_lines[-1] = snippet_lines[-1] . next_line
  52. let foldmethod_save = ''
  53. if has('folding')
  54. " Note: Change foldmethod to "manual". Because, if you use foldmethod is
  55. " expr, whole snippet is visually selected.
  56. let foldmethod_save = &l:foldmethod
  57. let &l:foldmethod = 'manual'
  58. endif
  59. let expand_stack = neosnippet#variables#expand_stack()
  60. try
  61. if len(snippet_lines) > 1
  62. call append('.', snippet_lines[1:])
  63. endif
  64. call setline('.', snippet_lines[0])
  65. if begin_line != end_line || options.indent
  66. call s:indent_snippet(begin_line, end_line)
  67. endif
  68. let begin_patterns = (begin_line > 1) ?
  69. \ [getline(begin_line - 1)] : []
  70. let end_patterns = (end_line < line('$')) ?
  71. \ [getline(end_line + 1)] : []
  72. call add(expand_stack, {
  73. \ 'snippet' : a:snippet,
  74. \ 'begin_line' : begin_line,
  75. \ 'begin_patterns' : begin_patterns,
  76. \ 'end_line' : end_line,
  77. \ 'end_patterns' : end_patterns,
  78. \ 'holder_cnt' : 1,
  79. \ })
  80. if snip_word =~ neosnippet#get_placeholder_marker_pattern()
  81. call neosnippet#view#_jump('', a:col)
  82. endif
  83. finally
  84. if has('folding')
  85. if foldmethod_save !=# &l:foldmethod
  86. let &l:foldmethod = foldmethod_save
  87. endif
  88. silent! execute begin_line . ',' . end_line . 'foldopen!'
  89. endif
  90. endtry
  91. endfunction"}}}
  92. function! neosnippet#view#_jump(_, col) abort "{{{
  93. try
  94. let expand_stack = neosnippet#variables#expand_stack()
  95. " Get patterns and count.
  96. if empty(expand_stack)
  97. return neosnippet#view#_search_outof_range(a:col)
  98. endif
  99. let expand_info = expand_stack[-1]
  100. " Search patterns.
  101. let [begin, end] = neosnippet#view#_get_snippet_range(
  102. \ expand_info.begin_line,
  103. \ expand_info.begin_patterns,
  104. \ expand_info.end_line,
  105. \ expand_info.end_patterns)
  106. let begin_cnt = expand_info.holder_cnt
  107. if expand_info.snippet =~
  108. \ neosnippet#get_placeholder_marker_substitute_nonzero_pattern()
  109. while (expand_info.holder_cnt - begin_cnt) < 5
  110. " Next count.
  111. let expand_info.holder_cnt += 1
  112. if neosnippet#view#_search_snippet_range(
  113. \ begin, end, expand_info.holder_cnt - 1)
  114. return 1
  115. endif
  116. endwhile
  117. endif
  118. " Search placeholder 0.
  119. if neosnippet#view#_search_snippet_range(begin, end, 0)
  120. return 1
  121. endif
  122. " Not found.
  123. call neosnippet#variables#pop_expand_stack()
  124. return neosnippet#view#_jump(a:_, a:col)
  125. finally
  126. call s:skip_next_auto_completion()
  127. endtry
  128. endfunction"}}}
  129. function! s:indent_snippet(begin, end) abort "{{{
  130. if a:begin > a:end
  131. return
  132. endif
  133. let pos = getpos('.')
  134. let equalprg = &l:equalprg
  135. try
  136. setlocal equalprg=
  137. let neosnippet = neosnippet#variables#current_neosnippet()
  138. let base_indent = matchstr(getline(a:begin), '^\s\+')
  139. for line_nr in range((neosnippet.target != '' ?
  140. \ a:begin : a:begin + 1), a:end)
  141. call cursor(line_nr, 0)
  142. if getline('.') =~ '^\t\+'
  143. " Delete head tab character.
  144. let current_line = substitute(getline('.'), '^\t', '', '')
  145. if &l:expandtab && current_line =~ '^\t\+'
  146. " Expand tab.
  147. cal setline('.', substitute(current_line,
  148. \ '^\t\+', base_indent . repeat(' ', shiftwidth() *
  149. \ len(matchstr(current_line, '^\t\+'))), ''))
  150. elseif line_nr != a:begin
  151. call setline('.', base_indent . current_line)
  152. endif
  153. else
  154. silent normal! ==
  155. endif
  156. endfor
  157. finally
  158. let &l:equalprg = equalprg
  159. call setpos('.', pos)
  160. endtry
  161. endfunction"}}}
  162. function! neosnippet#view#_get_snippet_range(begin_line, begin_patterns, end_line, end_patterns) abort "{{{
  163. let pos = getpos('.')
  164. call cursor(a:begin_line, 0)
  165. if empty(a:begin_patterns)
  166. let begin = line('.') - 50
  167. else
  168. let begin = searchpos('^' . neosnippet#util#escape_pattern(
  169. \ a:begin_patterns[0]) . '$', 'bnW')[0]
  170. if begin > 0 && a:begin_line == a:end_line
  171. call setpos('.', pos)
  172. return [begin + 1, begin + 1]
  173. endif
  174. if begin <= 0
  175. let begin = line('.') - 50
  176. endif
  177. endif
  178. if begin <= 0
  179. let begin = 1
  180. endif
  181. call cursor(a:end_line, 0)
  182. if empty(a:end_patterns)
  183. let end = line('.') + 50
  184. else
  185. let end = searchpos('^' . neosnippet#util#escape_pattern(
  186. \ a:end_patterns[0]) . '$', 'nW')[0]
  187. if end <= 0
  188. let end = line('.') + 50
  189. endif
  190. endif
  191. if end > line('$')
  192. let end = line('$')
  193. endif
  194. call setpos('.', pos)
  195. return [begin, end]
  196. endfunction"}}}
  197. function! neosnippet#view#_search_snippet_range(start, end, cnt, ...) abort "{{{
  198. let is_select = get(a:000, 0, 1)
  199. call s:substitute_placeholder_marker(a:start, a:end, a:cnt)
  200. " Search marker pattern.
  201. let pattern = substitute(neosnippet#get_placeholder_marker_pattern(),
  202. \ '\\d\\+', a:cnt, '')
  203. for line in filter(range(a:start, a:end),
  204. \ 'getline(v:val) =~ pattern')
  205. call s:expand_placeholder(a:start, a:end, a:cnt, line, is_select)
  206. return 1
  207. endfor
  208. for linenum in range(a:start, a:end)
  209. let tmp_line = getline(linenum)
  210. let tmp_line = substitute(tmp_line, '\%uc(\([^)]\+\))', '\U\1\E', 'g')
  211. let tmp_line = substitute(tmp_line, '\%ucfirst(\([^)]+\))', '\u\1', 'g')
  212. call setline(linenum, tmp_line)
  213. endfor
  214. return 0
  215. endfunction"}}}
  216. function! neosnippet#view#_search_outof_range(col) abort "{{{
  217. call s:substitute_placeholder_marker(1, 0, 0)
  218. let pattern = neosnippet#get_placeholder_marker_pattern()
  219. if search(pattern, 'w') > 0
  220. call s:expand_placeholder(line('.'), 0, '\\d\\+', line('.'))
  221. return 1
  222. endif
  223. let pos = getpos('.')
  224. if a:col == 1
  225. let pos[2] = 1
  226. call setpos('.', pos)
  227. startinsert
  228. elseif a:col >= col('$')
  229. startinsert!
  230. else
  231. let pos[2] = a:col+1
  232. call setpos('.', pos)
  233. startinsert
  234. endif
  235. " Not found.
  236. return 0
  237. endfunction"}}}
  238. function! neosnippet#view#_clear_markers(expand_info) abort "{{{
  239. " Search patterns.
  240. let [begin, end] = neosnippet#view#_get_snippet_range(
  241. \ a:expand_info.begin_line,
  242. \ a:expand_info.begin_patterns,
  243. \ a:expand_info.end_line,
  244. \ a:expand_info.end_patterns)
  245. let mode = mode()
  246. let pos = getpos('.')
  247. " Found snippet.
  248. let found = 0
  249. try
  250. while neosnippet#view#_search_snippet_range(
  251. \ begin, end, a:expand_info.holder_cnt, 0)
  252. " Next count.
  253. let a:expand_info.holder_cnt += 1
  254. let found = 1
  255. endwhile
  256. " Search placeholder 0.
  257. if neosnippet#view#_search_snippet_range(begin, end, 0)
  258. let found = 1
  259. endif
  260. finally
  261. if found && mode !=# 'i'
  262. stopinsert
  263. endif
  264. call setpos('.', pos)
  265. call neosnippet#variables#pop_expand_stack()
  266. endtry
  267. endfunction"}}}
  268. function! s:expand_placeholder(start, end, holder_cnt, line, ...) abort "{{{
  269. let is_select = get(a:000, 0, 1)
  270. let pattern = substitute(neosnippet#get_placeholder_marker_pattern(),
  271. \ '\\d\\+', a:holder_cnt, '')
  272. let current_line = getline(a:line)
  273. let match = match(current_line, pattern)
  274. let neosnippet = neosnippet#variables#current_neosnippet()
  275. let default_pattern = substitute(
  276. \ neosnippet#get_placeholder_marker_default_pattern(),
  277. \ '\\d\\+', a:holder_cnt, '')
  278. let default = substitute(
  279. \ matchstr(current_line, default_pattern),
  280. \ '\\\ze[^\\]', '', 'g')
  281. let neosnippet.optional_tabstop = (default =~ '^#:')
  282. if !is_select && default =~ '^#:'
  283. " Delete comments.
  284. let default = ''
  285. endif
  286. " Remove optional marker
  287. let default = substitute(default, '^#:', '', '')
  288. let is_target = (default =~ '^TARGET\>' && neosnippet.target != '')
  289. let default = substitute(default, '^TARGET:\?', neosnippet.target, '')
  290. let neosnippet.selected_text = default
  291. " Substitute marker.
  292. let default = substitute(default,
  293. \ neosnippet#get_placeholder_marker_substitute_pattern(),
  294. \ '<`\1`>', 'g')
  295. let default = substitute(default,
  296. \ neosnippet#get_mirror_placeholder_marker_substitute_pattern(),
  297. \ '<|\1|>', 'g')
  298. " len() cannot use for multibyte.
  299. let default_len = len(substitute(default, '.', 'x', 'g'))
  300. let pos = getpos('.')
  301. let pos[1] = a:line
  302. let pos[2] = match+1
  303. let cnt = s:search_sync_placeholder(a:start, a:end, a:holder_cnt)
  304. if cnt >= 0
  305. let pattern = substitute(neosnippet#get_placeholder_marker_pattern(),
  306. \ '\\d\\+', cnt, '')
  307. call setline(a:line, substitute(current_line, pattern,
  308. \ '<{'.cnt.':'.escape(default, '\').'}>', ''))
  309. let pos[2] += len('<{'.cnt.':')
  310. else
  311. if is_target
  312. let default = ''
  313. endif
  314. " Substitute holder.
  315. call setline(a:line,
  316. \ substitute(current_line, pattern, escape(default, '&\'), ''))
  317. endif
  318. call setpos('.', pos)
  319. if is_target && cnt < 0
  320. " Expand target
  321. return s:expand_target_placeholder(a:line, match+1)
  322. endif
  323. if default_len > 0 && is_select
  324. " Select default value.
  325. let neosnippet.unnamed_register = @"
  326. let len = default_len-1
  327. if &l:selection == 'exclusive'
  328. let len += 1
  329. endif
  330. let mode = mode()
  331. stopinsert
  332. normal! v
  333. call cursor(0, col('.') + (mode ==# 'i' ? len+1 : len))
  334. execute 'normal! ' "\<C-g>"
  335. elseif pos[2] < col('$')
  336. startinsert
  337. else
  338. startinsert!
  339. endif
  340. endfunction"}}}
  341. function! s:expand_target_placeholder(line, col) abort "{{{
  342. " Expand target
  343. let neosnippet = neosnippet#variables#current_neosnippet()
  344. let next_line = getline(a:line)[a:col-1 :]
  345. let target_lines = split(neosnippet.target, '\n', 1)
  346. let cur_text = getline(a:line)[: a:col-2]
  347. if match(cur_text, '^\s\+$') < 0
  348. let target_lines[0] = cur_text . target_lines[0]
  349. endif
  350. let target_lines[-1] = target_lines[-1] . next_line
  351. let begin_line = a:line
  352. let end_line = a:line + len(target_lines) - 1
  353. let col = col('.')
  354. try
  355. let base_indent = matchstr(cur_text, '^\s\+')
  356. let target_base_indent = -1
  357. for target_line in target_lines
  358. if match(target_line, '^\s\+$') < 0
  359. let target_current_indent = max([matchend(target_line, '^ *'), matchend(target_line, '^\t*') * &tabstop])
  360. if target_base_indent < 0 || target_current_indent < target_base_indent
  361. let target_base_indent = target_current_indent
  362. endif
  363. endif
  364. endfor
  365. if target_base_indent < 0
  366. let target_base_indent = 0
  367. end
  368. let target_strip_indent_regex = '^\s\+$\|^' .
  369. \ repeat(' ', target_base_indent) . '\|^' .
  370. \ repeat('\t', target_base_indent / &tabstop)
  371. call map(target_lines, 'substitute(v:val, target_strip_indent_regex, "", "")')
  372. call map(target_lines, 'v:val == "" ? "" : base_indent . v:val')
  373. call setline(a:line, target_lines[0])
  374. if len(target_lines) > 1
  375. call append(a:line, target_lines[1:])
  376. endif
  377. call cursor(end_line, 0)
  378. if next_line != ''
  379. startinsert
  380. let col = col('.')
  381. else
  382. startinsert!
  383. let col = col('$')
  384. endif
  385. finally
  386. if has('folding')
  387. silent! execute begin_line . ',' . end_line . 'foldopen!'
  388. endif
  389. endtry
  390. let neosnippet.target = ''
  391. call neosnippet#view#_jump('', col)
  392. endfunction"}}}
  393. function! s:search_sync_placeholder(start, end, number) abort "{{{
  394. if a:end == 0
  395. " Search in current buffer.
  396. let cnt = matchstr(getline('.'),
  397. \ substitute(neosnippet#get_placeholder_marker_pattern(),
  398. \ '\\d\\+', '\\zs\\d\\+\\ze', ''))
  399. return search(substitute(
  400. \ neosnippet#get_mirror_placeholder_marker_pattern(),
  401. \ '\\d\\+', cnt, ''), 'nw') > 0 ? cnt : -1
  402. endif
  403. let pattern = substitute(
  404. \ neosnippet#get_mirror_placeholder_marker_pattern(),
  405. \ '\\d\\+', a:number, '')
  406. if !empty(filter(range(a:start, a:end),
  407. \ 'getline(v:val) =~ pattern'))
  408. return a:number
  409. endif
  410. return -1
  411. endfunction"}}}
  412. function! s:substitute_placeholder_marker(start, end, snippet_holder_cnt) abort "{{{
  413. if a:snippet_holder_cnt > 0
  414. let cnt = a:snippet_holder_cnt-1
  415. let sync_marker = substitute(neosnippet#get_sync_placeholder_marker_pattern(),
  416. \ '\\d\\+', cnt, '')
  417. let mirror_marker = substitute(
  418. \ neosnippet#get_mirror_placeholder_marker_pattern(),
  419. \ '\\d\\+', cnt, '')
  420. let line = a:start
  421. for line in range(a:start, a:end)
  422. if getline(line) =~ sync_marker
  423. let sub = escape(matchstr(getline(line),
  424. \ substitute(neosnippet#get_sync_placeholder_marker_default_pattern(),
  425. \ '\\d\\+', cnt, '')), '/\&')
  426. silent execute printf('%d,%ds/\m' . mirror_marker . '/%s/'
  427. \ . (&gdefault ? '' : 'g'), a:start, a:end, sub)
  428. call setline(line, substitute(getline(line), sync_marker, sub, ''))
  429. endif
  430. endfor
  431. elseif search(neosnippet#get_sync_placeholder_marker_pattern(), 'wb') > 0
  432. let sub = escape(matchstr(getline('.'),
  433. \ neosnippet#get_sync_placeholder_marker_default_pattern()), '/\')
  434. let cnt = matchstr(getline('.'),
  435. \ substitute(neosnippet#get_sync_placeholder_marker_pattern(),
  436. \ '\\d\\+', '\\zs\\d\\+\\ze', ''))
  437. let mirror_marker = substitute(
  438. \ neosnippet#get_mirror_placeholder_marker_pattern(),
  439. \ '\\d\\+', cnt, '')
  440. silent execute printf('%%s/\m' . mirror_marker . '/%s/'
  441. \ . (&gdefault ? 'g' : ''), sub)
  442. let sync_marker = substitute(neosnippet#get_sync_placeholder_marker_pattern(),
  443. \ '\\d\\+', cnt, '')
  444. call setline('.', substitute(getline('.'), sync_marker, sub, ''))
  445. endif
  446. endfunction"}}}
  447. function! s:eval_snippet(snippet_text) abort "{{{
  448. let snip_word = ''
  449. let prev_match = 0
  450. let match = match(a:snippet_text, '\\\@<!`.\{-}\\\@<!`')
  451. while match >= 0
  452. if match - prev_match > 0
  453. let snip_word .= a:snippet_text[prev_match : match - 1]
  454. endif
  455. let prev_match = matchend(a:snippet_text,
  456. \ '\\\@<!`.\{-}\\\@<!`', match)
  457. let snip_word .= eval(
  458. \ a:snippet_text[match+1 : prev_match - 2])
  459. let match = match(a:snippet_text, '\\\@<!`.\{-}\\\@<!`', prev_match)
  460. endwhile
  461. if prev_match >= 0
  462. let snip_word .= a:snippet_text[prev_match :]
  463. endif
  464. return snip_word
  465. endfunction"}}}
  466. function! s:skip_next_auto_completion() abort "{{{
  467. " Skip next auto completion.
  468. let neosnippet = neosnippet#variables#current_neosnippet()
  469. let neosnippet.trigger = 0
  470. if exists('#neocomplete#CompleteDone')
  471. doautocmd neocomplete CompleteDone
  472. endif
  473. if exists('#deoplete#CompleteDone')
  474. doautocmd deoplete CompleteDone
  475. endif
  476. endfunction"}}}
  477. let &cpo = s:save_cpo
  478. unlet s:save_cpo
  479. " vim: foldmethod=marker