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.
 
 

419 lines
12 KiB

  1. "=============================================================================
  2. " FILE: parser.vim
  3. " AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
  4. " License: MIT license
  5. "=============================================================================
  6. let s:Cache = neosnippet#util#get_vital().import('System.Cache.Deprecated')
  7. function! neosnippet#parser#_parse_snippets(filename) abort
  8. if !filereadable(a:filename)
  9. call neosnippet#util#print_error(
  10. \ printf('snippet file "%s" is not found.', a:filename))
  11. return {}
  12. endif
  13. let cache_dir = neosnippet#variables#data_dir()
  14. let snippets = {}
  15. if !s:Cache.check_old_cache(cache_dir, a:filename)
  16. try
  17. let snippets = neosnippet#helpers#json2vim(
  18. \ s:Cache.readfile(cache_dir, a:filename)[0])
  19. catch
  20. endtry
  21. endif
  22. if empty(snippets) || s:Cache.check_old_cache(cache_dir, a:filename)
  23. let [snippets, sourced] = s:parse(a:filename)
  24. if len(snippets) > 5 && !neosnippet#util#is_sudo() && !sourced
  25. call s:Cache.writefile(
  26. \ cache_dir, a:filename,
  27. \ [neosnippet#helpers#vim2json(snippets)])
  28. endif
  29. endif
  30. return snippets
  31. endfunction
  32. function! neosnippet#parser#_parse_snippet(filename, trigger) abort
  33. if !filereadable(a:filename)
  34. call neosnippet#util#print_error(
  35. \ printf('snippet file "%s" is not found.', a:filename))
  36. return {}
  37. endif
  38. let snippet_dict = {
  39. \ 'word' : join(readfile(a:filename), "\n\t"),
  40. \ 'name' : a:trigger,
  41. \ 'options' : neosnippet#parser#_initialize_snippet_options()
  42. \ }
  43. return neosnippet#parser#_initialize_snippet(
  44. \ snippet_dict, a:filename, 1, '', a:trigger)
  45. endfunction
  46. function! s:parse(snippets_file) abort
  47. let dup_check = {}
  48. let snippet_dict = {}
  49. let linenr = 1
  50. let snippets = {}
  51. let sourced = 0
  52. for line in readfile(a:snippets_file)
  53. if line =~ '^\h\w*.*\s$'
  54. " Delete spaces.
  55. let line = substitute(line, '\s\+$', '', '')
  56. endif
  57. if line =~ '^#'
  58. " Ignore.
  59. elseif line =~ '^include'
  60. " Include snippets file.
  61. for file in split(globpath(join(
  62. \ neosnippet#helpers#get_snippets_directory(), ','),
  63. \ matchstr(line, '^include\s\+\zs.*$')), '\n')
  64. let snippets = extend(snippets,
  65. \ neosnippet#parser#_parse_snippets(file))
  66. endfor
  67. elseif line =~ '^source'
  68. " Source Vim script file.
  69. for file in split(globpath(join(
  70. \ neosnippet#helpers#get_snippets_directory(), ','),
  71. \ matchstr(line, '^source\s\+\zs.*$')), '\n')
  72. execute 'source' fnameescape(file)
  73. let sourced = 1
  74. endfor
  75. elseif line =~ '^delete\s'
  76. let name = matchstr(line, '^delete\s\+\zs.*$')
  77. if name != '' && has_key(snippets, name)
  78. call filter(snippets, 'v:val.real_name !=# name')
  79. endif
  80. elseif line =~ '^snippet\s'
  81. if !empty(snippet_dict)
  82. " Set previous snippet.
  83. call s:set_snippet_dict(snippet_dict,
  84. \ snippets, dup_check, a:snippets_file)
  85. endif
  86. let snippet_dict = s:parse_snippet_name(
  87. \ a:snippets_file, line, linenr, dup_check)
  88. elseif !empty(snippet_dict)
  89. if line =~ '^\s' || line == ''
  90. if snippet_dict.word == ''
  91. " Substitute head tab character.
  92. let line = substitute(line, '^\t', '', '')
  93. endif
  94. let snippet_dict.word .=
  95. \ substitute(line, '^ *', '', '') . "\n"
  96. else
  97. call s:add_snippet_attribute(
  98. \ a:snippets_file, line, linenr, snippet_dict)
  99. endif
  100. endif
  101. let linenr += 1
  102. endfor
  103. if !empty(snippet_dict)
  104. " Set previous snippet.
  105. call s:set_snippet_dict(snippet_dict,
  106. \ snippets, dup_check, a:snippets_file)
  107. endif
  108. return [snippets, sourced]
  109. endfunction
  110. function! s:parse_snippet_name(snippets_file, line, linenr, dup_check) abort
  111. " Initialize snippet dict.
  112. let snippet_dict = {
  113. \ 'word' : '',
  114. \ 'linenr' : a:linenr,
  115. \ 'options' : neosnippet#parser#_initialize_snippet_options()
  116. \ }
  117. " Try using the name without the description (abbr).
  118. let snippet_dict.name = matchstr(a:line, '^snippet\s\+\zs\S\+')
  119. " Fall back to using the name and description (abbr) combined.
  120. " SnipMate snippets may have duplicate names, but different
  121. " descriptions (abbrs).
  122. let description = matchstr(a:line, '^snippet\s\+\S\+\s\+\zs.*$')
  123. if description != '' && description !=# snippet_dict.name
  124. " Convert description.
  125. let snippet_dict.name .= '_' .
  126. \ substitute(substitute(
  127. \ description, '\W\+', '_', 'g'), '_\+$', '', '')
  128. endif
  129. " Collect the description (abbr) of the snippet, if set on snippet line.
  130. " This is for compatibility with SnipMate-style snippets.
  131. let snippet_dict.abbr = matchstr(a:line,
  132. \ '^snippet\s\+\S\+\s\+\zs.*$')
  133. " Check for duplicated names.
  134. if has_key(a:dup_check, snippet_dict.name)
  135. let dup = a:dup_check[snippet_dict.name]
  136. call neosnippet#util#print_error(printf(
  137. \ '%s:%d is overriding `%s` from %s:%d',
  138. \ a:snippets_file, a:linenr, snippet_dict.name,
  139. \ dup.action__path, dup.action__line))
  140. call neosnippet#util#print_error(printf(
  141. \ 'Please rename the snippet name or use `delete %s`.',
  142. \ snippet_dict.name))
  143. endif
  144. return snippet_dict
  145. endfunction
  146. function! s:add_snippet_attribute(snippets_file, line, linenr, snippet_dict) abort
  147. " Allow overriding/setting of the description (abbr) of the snippet.
  148. " This will override what was set via the snippet line.
  149. if a:line =~ '^abbr\s'
  150. let a:snippet_dict.abbr = matchstr(a:line, '^abbr\s\+\zs.*$')
  151. elseif a:line =~ '^alias\s'
  152. let a:snippet_dict.alias = split(matchstr(a:line,
  153. \ '^alias\s\+\zs.*$'), '[,[:space:]]\+')
  154. elseif a:line =~ '^prev_word\s'
  155. let prev_word = matchstr(a:line,
  156. \ '^prev_word\s\+[''"]\zs.*\ze[''"]$')
  157. if prev_word == '^'
  158. " For backward compatibility.
  159. let a:snippet_dict.options.head = 1
  160. else
  161. call neosnippet#util#print_error(
  162. \ 'prev_word must be "^" character.')
  163. endif
  164. elseif a:line =~ '^regexp\s'
  165. let a:snippet_dict.regexp = matchstr(a:line,
  166. \ '^regexp\s\+[''"]\zs.*\ze[''"]$')
  167. elseif a:line =~ '^options\s\+'
  168. for option in split(matchstr(a:line,
  169. \ '^options\s\+\zs.*$'), '[,[:space:]]\+')
  170. if !has_key(a:snippet_dict.options, option)
  171. call neosnippet#util#print_error(
  172. \ printf('%s:%d', a:snippets_file, a:linenr))
  173. call neosnippet#util#print_error(
  174. \ printf('Invalid option name : "%s"', option))
  175. else
  176. let a:snippet_dict.options[option] = 1
  177. endif
  178. endfor
  179. else
  180. call neosnippet#util#print_error(
  181. \ printf('%s:%d', a:snippets_file, a:linenr))
  182. call neosnippet#util#print_error(
  183. \ printf('Invalid syntax : "%s"', a:line))
  184. endif
  185. endfunction
  186. function! s:set_snippet_dict(snippet_dict, snippets, dup_check, snippets_file) abort
  187. if empty(a:snippet_dict)
  188. return
  189. endif
  190. let action_pattern = '^snippet\s\+' . a:snippet_dict.name . '$'
  191. let snippet = neosnippet#parser#_initialize_snippet(
  192. \ a:snippet_dict, a:snippets_file,
  193. \ a:snippet_dict.linenr, action_pattern,
  194. \ a:snippet_dict.name)
  195. let a:snippets[a:snippet_dict.name] = snippet
  196. let a:dup_check[a:snippet_dict.name] = snippet
  197. for alias in get(a:snippet_dict, 'alias', [])
  198. let alias_snippet = copy(snippet)
  199. let alias_snippet.word = alias
  200. let a:snippets[alias] = alias_snippet
  201. let a:dup_check[alias] = alias_snippet
  202. endfor
  203. endfunction
  204. function! neosnippet#parser#_initialize_snippet(dict, path, line, pattern, name) abort
  205. let a:dict.word = substitute(a:dict.word, '\n\+$', '', '')
  206. if a:dict.word !~ '\n'
  207. \ && a:dict.word !~
  208. \ neosnippet#get_placeholder_marker_substitute_pattern().'$'
  209. \ && a:dict.word !~
  210. \ neosnippet#get_placeholder_marker_substitute_zero_pattern()
  211. " Add placeholder.
  212. let a:dict.word .= '${0}'
  213. endif
  214. if !has_key(a:dict, 'abbr') || a:dict.abbr == ''
  215. " Set default abbr.
  216. let abbr = substitute(a:dict.word,
  217. \ neosnippet#get_placeholder_marker_pattern(). '\|'.
  218. \ neosnippet#get_mirror_placeholder_marker_pattern().
  219. \ '\|\s\+\|\n\|TARGET', ' ', 'g')
  220. let abbr = substitute(abbr, '\\\(\\\|`\|\$\)', '\1', 'g')
  221. let a:dict.abbr = a:dict.name
  222. else
  223. let abbr = a:dict.abbr
  224. endif
  225. let snippet = {
  226. \ 'word' : a:dict.name, 'snip' : a:dict.word,
  227. \ 'description' : a:dict.word,
  228. \ 'menu_template' : abbr,
  229. \ 'menu_abbr' : abbr,
  230. \ 'options' : a:dict.options,
  231. \ 'action__path' : a:path, 'action__line' : a:line,
  232. \ 'action__pattern' : a:pattern, 'real_name' : a:name,
  233. \}
  234. if has_key(a:dict, 'regexp')
  235. let snippet.regexp = a:dict.regexp
  236. endif
  237. return snippet
  238. endfunction
  239. function! neosnippet#parser#_initialize_snippet_options() abort
  240. return {
  241. \ 'head' : 0,
  242. \ 'word' :
  243. \ g:neosnippet#expand_word_boundary,
  244. \ 'indent' : 0,
  245. \ 'oneshot' : 0,
  246. \ }
  247. endfunction
  248. function! neosnippet#parser#_get_completed_snippet(completed_item, cur_text, next_text) abort
  249. let item = a:completed_item
  250. if strridx(a:cur_text, item.word) != len(a:cur_text) - len(item.word)
  251. return ''
  252. endif
  253. if has_key(item, 'snippet')
  254. return item.snippet
  255. endif
  256. " Set abbr
  257. let abbr = (item.abbr != '') ? item.abbr : item.word
  258. if len(item.menu) > 5
  259. " Combine menu.
  260. let abbr .= ' ' . item.menu
  261. endif
  262. if item.info != ''
  263. let abbr .= split(item.info, '\n')[0]
  264. endif
  265. let abbr = escape(abbr, '\')
  266. let pairs = neosnippet#util#get_buffer_config(
  267. \ &filetype, '',
  268. \ 'g:neosnippet#completed_pairs', 'g:neosnippet#_completed_pairs', {})
  269. let word_pattern = neosnippet#util#escape_pattern(item.word)
  270. let angle_pattern = word_pattern . '<.\+>(.*)'
  271. let no_key = index(keys(pairs), item.word[-1:]) < 0
  272. if no_key && abbr !~# word_pattern . '\%(<.\+>\)\?(.*)'
  273. return ''
  274. endif
  275. let key = no_key ? '(' : item.word[-1:]
  276. if a:next_text[:0] ==# key
  277. " Disable auto pair
  278. return ''
  279. endif
  280. let pair = pairs[key]
  281. " Make snippet arguments
  282. let cnt = 1
  283. let snippet = ''
  284. if no_key && abbr !~# angle_pattern
  285. " Auto key
  286. let snippet .= key
  287. endif
  288. if empty(filter(values(pairs), 'stridx(abbr, v:val) > 0'))
  289. " Pairs not found pattern
  290. let snippet .= '${' . cnt . '}'
  291. let cnt += 1
  292. endif
  293. if abbr =~# angle_pattern
  294. " Add angle analysis
  295. let snippet .= '<'
  296. let args = ''
  297. for arg in split(substitute(
  298. \ neosnippet#parser#_get_in_paren('<', '>', abbr),
  299. \ '<\zs.\{-}\ze>', '', 'g'), '[^[]\zs\s*,\s*')
  300. let args .= neosnippet#parser#_conceal_argument(arg, cnt, args)
  301. let cnt += 1
  302. endfor
  303. let snippet .= args
  304. let snippet .= '>'
  305. if no_key
  306. let snippet .= key
  307. endif
  308. endif
  309. let args = ''
  310. for arg in split(substitute(
  311. \ neosnippet#parser#_get_in_paren(key, pair, abbr),
  312. \ key.'\zs.\{-}\ze'.pair . '\|<\zs.\{-}\ze>', '', 'g'),
  313. \ '[^[]\zs\s*,\s*')
  314. if key ==# '(' && (
  315. \ (&filetype ==# 'python' && arg ==# 'self') ||
  316. \ (&filetype ==# 'rust' && arg =~# '\m^&\?\(mut \)\?self$'))
  317. " Ignore self argument
  318. continue
  319. endif
  320. let args .= neosnippet#parser#_conceal_argument(arg, cnt, args)
  321. let cnt += 1
  322. endfor
  323. let snippet .= args
  324. if key != '(' && snippet == ''
  325. let snippet .= '${' . cnt . '}'
  326. let cnt += 1
  327. endif
  328. let snippet .= pair
  329. let snippet .= '${' . cnt . '}'
  330. return snippet
  331. endfunction
  332. function! neosnippet#parser#_get_in_paren(key, pair, str) abort
  333. let s = ''
  334. let level = 0
  335. for c in split(a:str, '\zs')
  336. if c ==# a:key
  337. let level += 1
  338. if level == 1
  339. continue
  340. endif
  341. elseif c ==# a:pair
  342. if level == 1 && (s != '' || a:str =~ '()\s*(.\{-})')
  343. return s
  344. else
  345. let level -= 1
  346. endif
  347. endif
  348. if level > 0
  349. let s .= c
  350. endif
  351. endfor
  352. return ''
  353. endfunction
  354. function! neosnippet#parser#_conceal_argument(arg, cnt, args) abort
  355. let outside = ''
  356. let inside = ''
  357. if (a:args != '')
  358. if g:neosnippet#enable_optional_arguments
  359. let inside = ', '
  360. else
  361. let outside = ', '
  362. endif
  363. endif
  364. return printf('%s${%d:#:%s%s}', outside, a:cnt, inside, escape(a:arg, '{}'))
  365. endfunction