PageRenderTime 63ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/autoload/acp.vim

https://github.com/Alan0521/vimfiles
Vim Script | 422 lines | 365 code | 24 blank | 33 comment | 26 complexity | c2f8ef69f62e881c04b9c1e2d0b0a20a MD5 | raw file
  1. "=============================================================================
  2. " Copyright (c) 2007-2009 Takeshi NISHIDA
  3. "
  4. "=============================================================================
  5. " LOAD GUARD {{{1
  6. if !l9#guardScriptLoading(expand('<sfile>:p'), 0, 0, [])
  7. finish
  8. endif
  9. " }}}1
  10. "=============================================================================
  11. " GLOBAL FUNCTIONS: {{{1
  12. "
  13. function acp#enable()
  14. call acp#disable()
  15. augroup AcpGlobalAutoCommand
  16. autocmd!
  17. autocmd InsertEnter * unlet! s:posLast s:lastUncompletable
  18. autocmd InsertLeave * call s:finishPopup(1)
  19. augroup END
  20. if g:acp_mappingDriven
  21. call s:mapForMappingDriven()
  22. else
  23. autocmd AcpGlobalAutoCommand CursorMovedI * call s:feedPopup()
  24. endif
  25. nnoremap <silent> i i<C-r>=<SID>feedPopup()<CR>
  26. nnoremap <silent> a a<C-r>=<SID>feedPopup()<CR>
  27. nnoremap <silent> R R<C-r>=<SID>feedPopup()<CR>
  28. endfunction
  29. "
  30. function acp#disable()
  31. call s:unmapForMappingDriven()
  32. augroup AcpGlobalAutoCommand
  33. autocmd!
  34. augroup END
  35. nnoremap i <Nop> | nunmap i
  36. nnoremap a <Nop> | nunmap a
  37. nnoremap R <Nop> | nunmap R
  38. endfunction
  39. "
  40. function acp#lock()
  41. let s:lockCount += 1
  42. endfunction
  43. "
  44. function acp#unlock()
  45. let s:lockCount -= 1
  46. if s:lockCount < 0
  47. let s:lockCount = 0
  48. throw "AutoComplPop: not locked"
  49. endif
  50. endfunction
  51. "
  52. function acp#meetsForSnipmate(context)
  53. if g:acp_behaviorSnipmateLength < 0
  54. return 0
  55. endif
  56. let matches = matchlist(a:context, '\(^\|\s\|\<\)\(\u\{' .
  57. \ g:acp_behaviorSnipmateLength . ',}\)$')
  58. return !empty(matches) && !empty(s:getMatchingSnipItems(matches[2]))
  59. endfunction
  60. "
  61. function acp#meetsForKeyword(context)
  62. if g:acp_behaviorKeywordLength < 0
  63. return 0
  64. endif
  65. let matches = matchlist(a:context, '\(\k\{' . g:acp_behaviorKeywordLength . ',}\)$')
  66. if empty(matches)
  67. return 0
  68. endif
  69. for ignore in g:acp_behaviorKeywordIgnores
  70. if stridx(ignore, matches[1]) == 0
  71. return 0
  72. endif
  73. endfor
  74. return 1
  75. endfunction
  76. "
  77. function acp#meetsForFile(context)
  78. if g:acp_behaviorFileLength < 0
  79. return 0
  80. endif
  81. if has('win32') || has('win64')
  82. let separator = '[/\\]'
  83. else
  84. let separator = '\/'
  85. endif
  86. if a:context !~ '\f' . separator . '\f\{' . g:acp_behaviorFileLength . ',}$'
  87. return 0
  88. endif
  89. return a:context !~ '[*/\\][/\\]\f*$\|[^[:print:]]\f*$'
  90. endfunction
  91. "
  92. function acp#meetsForRubyOmni(context)
  93. if !has('ruby')
  94. return 0
  95. endif
  96. if g:acp_behaviorRubyOmniMethodLength >= 0 &&
  97. \ a:context =~ '[^. \t]\(\.\|::\)\k\{' .
  98. \ g:acp_behaviorRubyOmniMethodLength . ',}$'
  99. return 1
  100. endif
  101. if g:acp_behaviorRubyOmniSymbolLength >= 0 &&
  102. \ a:context =~ '\(^\|[^:]\):\k\{' .
  103. \ g:acp_behaviorRubyOmniSymbolLength . ',}$'
  104. return 1
  105. endif
  106. return 0
  107. endfunction
  108. "
  109. function acp#meetsForPythonOmni(context)
  110. return has('python') && g:acp_behaviorPythonOmniLength >= 0 &&
  111. \ a:context =~ '\k\.\k\{' . g:acp_behaviorPythonOmniLength . ',}$'
  112. endfunction
  113. "
  114. function acp#meetsForPerlOmni(context)
  115. return g:acp_behaviorPerlOmniLength >= 0 &&
  116. \ a:context =~ '\w->\k\{' . g:acp_behaviorPerlOmniLength . ',}$'
  117. endfunction
  118. "
  119. function acp#meetsForXmlOmni(context)
  120. return g:acp_behaviorXmlOmniLength >= 0 &&
  121. \ a:context =~ '\(<\|<\/\|<[^>]\+ \|<[^>]\+=\"\)\k\{' .
  122. \ g:acp_behaviorXmlOmniLength . ',}$'
  123. endfunction
  124. "
  125. function acp#meetsForHtmlOmni(context)
  126. return g:acp_behaviorHtmlOmniLength >= 0 &&
  127. \ a:context =~ '\(<\|<\/\|<[^>]\+ \|<[^>]\+=\"\)\k\{' .
  128. \ g:acp_behaviorHtmlOmniLength . ',}$'
  129. endfunction
  130. "
  131. function acp#meetsForCssOmni(context)
  132. if g:acp_behaviorCssOmniPropertyLength >= 0 &&
  133. \ a:context =~ '\(^\s\|[;{]\)\s*\k\{' .
  134. \ g:acp_behaviorCssOmniPropertyLength . ',}$'
  135. return 1
  136. endif
  137. if g:acp_behaviorCssOmniValueLength >= 0 &&
  138. \ a:context =~ '[:@!]\s*\k\{' .
  139. \ g:acp_behaviorCssOmniValueLength . ',}$'
  140. return 1
  141. endif
  142. return 0
  143. endfunction
  144. "
  145. function acp#completeSnipmate(findstart, base)
  146. if a:findstart
  147. let s:posSnipmateCompletion = len(matchstr(s:getCurrentText(), '.*\U'))
  148. return s:posSnipmateCompletion
  149. endif
  150. let lenBase = len(a:base)
  151. let items = filter(GetSnipsInCurrentScope(),
  152. \ 'strpart(v:key, 0, lenBase) ==? a:base')
  153. return map(sort(items(items)), 's:makeSnipmateItem(v:val[0], v:val[1])')
  154. endfunction
  155. "
  156. function acp#onPopupCloseSnipmate()
  157. let word = s:getCurrentText()[s:posSnipmateCompletion :]
  158. for trigger in keys(GetSnipsInCurrentScope())
  159. if word ==# trigger
  160. call feedkeys("\<C-r>=TriggerSnippet()\<CR>", "n")
  161. return 0
  162. endif
  163. endfor
  164. return 1
  165. endfunction
  166. "
  167. function acp#onPopupPost()
  168. " to clear <C-r>= expression on command-line
  169. echo ''
  170. if pumvisible()
  171. inoremap <silent> <expr> <C-h> acp#onBs()
  172. inoremap <silent> <expr> <BS> acp#onBs()
  173. " a command to restore to original text and select the first match
  174. return (s:behavsCurrent[s:iBehavs].command =~# "\<C-p>" ? "\<C-n>\<Up>"
  175. \ : "\<C-p>\<Down>")
  176. endif
  177. let s:iBehavs += 1
  178. if len(s:behavsCurrent) > s:iBehavs
  179. call s:setCompletefunc()
  180. return printf("\<C-e>%s\<C-r>=acp#onPopupPost()\<CR>",
  181. \ s:behavsCurrent[s:iBehavs].command)
  182. else
  183. let s:lastUncompletable = {
  184. \ 'word': s:getCurrentWord(),
  185. \ 'commands': map(copy(s:behavsCurrent), 'v:val.command')[1:],
  186. \ }
  187. call s:finishPopup(0)
  188. return "\<C-e>"
  189. endif
  190. endfunction
  191. "
  192. function acp#onBs()
  193. " using "matchstr" and not "strpart" in order to handle multi-byte
  194. " characters
  195. if call(s:behavsCurrent[s:iBehavs].meets,
  196. \ [matchstr(s:getCurrentText(), '.*\ze.')])
  197. return "\<BS>"
  198. endif
  199. return "\<C-e>\<BS>"
  200. endfunction
  201. " }}}1
  202. "=============================================================================
  203. " LOCAL FUNCTIONS: {{{1
  204. "
  205. function s:mapForMappingDriven()
  206. call s:unmapForMappingDriven()
  207. let s:keysMappingDriven = [
  208. \ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  209. \ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  210. \ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  211. \ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  212. \ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  213. \ '-', '_', '~', '^', '.', ',', ':', '!', '#', '=', '%', '$', '@', '<', '>', '/', '\',
  214. \ '<Space>', '<C-h>', '<BS>', ]
  215. for key in s:keysMappingDriven
  216. execute printf('inoremap <silent> %s %s<C-r>=<SID>feedPopup()<CR>',
  217. \ key, key)
  218. endfor
  219. endfunction
  220. "
  221. function s:unmapForMappingDriven()
  222. if !exists('s:keysMappingDriven')
  223. return
  224. endif
  225. for key in s:keysMappingDriven
  226. execute 'iunmap ' . key
  227. endfor
  228. let s:keysMappingDriven = []
  229. endfunction
  230. "
  231. function s:getCurrentWord()
  232. return matchstr(s:getCurrentText(), '\k*$')
  233. endfunction
  234. "
  235. function s:getCurrentText()
  236. return strpart(getline('.'), 0, col('.') - 1)
  237. endfunction
  238. "
  239. function s:getPostText()
  240. return strpart(getline('.'), col('.') - 1)
  241. endfunction
  242. "
  243. function s:isModifiedSinceLastCall()
  244. if exists('s:posLast')
  245. let posPrev = s:posLast
  246. let nLinesPrev = s:nLinesLast
  247. let textPrev = s:textLast
  248. endif
  249. let s:posLast = getpos('.')
  250. let s:nLinesLast = line('$')
  251. let s:textLast = getline('.')
  252. if !exists('posPrev')
  253. return 1
  254. elseif posPrev[1] != s:posLast[1] || nLinesPrev != s:nLinesLast
  255. return (posPrev[1] - s:posLast[1] == nLinesPrev - s:nLinesLast)
  256. elseif textPrev ==# s:textLast
  257. return 0
  258. elseif posPrev[2] > s:posLast[2]
  259. return 1
  260. elseif has('gui_running') && has('multi_byte')
  261. " NOTE: auto-popup causes a strange behavior when IME/XIM is working
  262. return posPrev[2] + 1 == s:posLast[2]
  263. endif
  264. return posPrev[2] != s:posLast[2]
  265. endfunction
  266. "
  267. function s:makeCurrentBehaviorSet()
  268. let modified = s:isModifiedSinceLastCall()
  269. if exists('s:behavsCurrent[s:iBehavs].repeat') && s:behavsCurrent[s:iBehavs].repeat
  270. let behavs = [ s:behavsCurrent[s:iBehavs] ]
  271. elseif exists('s:behavsCurrent[s:iBehavs]')
  272. return []
  273. elseif modified
  274. let behavs = copy(exists('g:acp_behavior[&filetype]')
  275. \ ? g:acp_behavior[&filetype]
  276. \ : g:acp_behavior['*'])
  277. else
  278. return []
  279. endif
  280. let text = s:getCurrentText()
  281. call filter(behavs, 'call(v:val.meets, [text])')
  282. let s:iBehavs = 0
  283. if exists('s:lastUncompletable') &&
  284. \ stridx(s:getCurrentWord(), s:lastUncompletable.word) == 0 &&
  285. \ map(copy(behavs), 'v:val.command') ==# s:lastUncompletable.commands
  286. let behavs = []
  287. else
  288. unlet! s:lastUncompletable
  289. endif
  290. return behavs
  291. endfunction
  292. "
  293. function s:feedPopup()
  294. " NOTE: CursorMovedI is not triggered while the popup menu is visible. And
  295. " it will be triggered when popup menu is disappeared.
  296. if s:lockCount > 0 || pumvisible() || &paste
  297. return ''
  298. endif
  299. if exists('s:behavsCurrent[s:iBehavs].onPopupClose')
  300. if !call(s:behavsCurrent[s:iBehavs].onPopupClose, [])
  301. call s:finishPopup(1)
  302. return ''
  303. endif
  304. endif
  305. let s:behavsCurrent = s:makeCurrentBehaviorSet()
  306. if empty(s:behavsCurrent)
  307. call s:finishPopup(1)
  308. return ''
  309. endif
  310. " In case of dividing words by symbols (e.g. "for(int", "ab==cd") while a
  311. " popup menu is visible, another popup is not available unless input <C-e>
  312. " or try popup once. So first completion is duplicated.
  313. call insert(s:behavsCurrent, s:behavsCurrent[s:iBehavs])
  314. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  315. \ '&spell', 0)
  316. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  317. \ '&completeopt', 'menuone' . (g:acp_completeoptPreview ? ',preview' : ''))
  318. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  319. \ '&complete', g:acp_completeOption)
  320. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  321. \ '&ignorecase', g:acp_ignorecaseOption)
  322. " NOTE: With CursorMovedI driven, Set 'lazyredraw' to avoid flickering.
  323. " With Mapping driven, set 'nolazyredraw' to make a popup menu visible.
  324. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  325. \ '&lazyredraw', !g:acp_mappingDriven)
  326. " NOTE: 'textwidth' must be restored after <C-e>.
  327. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP1,
  328. \ '&textwidth', 0)
  329. call s:setCompletefunc()
  330. call feedkeys(s:behavsCurrent[s:iBehavs].command . "\<C-r>=acp#onPopupPost()\<CR>", 'n')
  331. return '' " this function is called by <C-r>=
  332. endfunction
  333. "
  334. function s:finishPopup(fGroup1)
  335. inoremap <C-h> <Nop> | iunmap <C-h>
  336. inoremap <BS> <Nop> | iunmap <BS>
  337. let s:behavsCurrent = []
  338. call l9#tempvariables#end(s:TEMP_VARIABLES_GROUP0)
  339. if a:fGroup1
  340. call l9#tempvariables#end(s:TEMP_VARIABLES_GROUP1)
  341. endif
  342. endfunction
  343. "
  344. function s:setCompletefunc()
  345. if exists('s:behavsCurrent[s:iBehavs].completefunc')
  346. call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP0,
  347. \ '&completefunc', s:behavsCurrent[s:iBehavs].completefunc)
  348. endif
  349. endfunction
  350. "
  351. function s:makeSnipmateItem(key, snip)
  352. if type(a:snip) == type([])
  353. let descriptions = map(copy(a:snip), 'v:val[0]')
  354. let snipFormatted = '[MULTI] ' . join(descriptions, ', ')
  355. else
  356. let snipFormatted = substitute(a:snip, '\(\n\|\s\)\+', ' ', 'g')
  357. endif
  358. return {
  359. \ 'word': a:key,
  360. \ 'menu': strpart(snipFormatted, 0, 80),
  361. \ }
  362. endfunction
  363. "
  364. function s:getMatchingSnipItems(base)
  365. let key = a:base . "\n"
  366. if !exists('s:snipItems[key]')
  367. let s:snipItems[key] = items(GetSnipsInCurrentScope())
  368. call filter(s:snipItems[key], 'strpart(v:val[0], 0, len(a:base)) ==? a:base')
  369. call map(s:snipItems[key], 's:makeSnipmateItem(v:val[0], v:val[1])')
  370. endif
  371. return s:snipItems[key]
  372. endfunction
  373. " }}}1
  374. "=============================================================================
  375. " INITIALIZATION {{{1
  376. let s:TEMP_VARIABLES_GROUP0 = "AutoComplPop0"
  377. let s:TEMP_VARIABLES_GROUP1 = "AutoComplPop1"
  378. let s:lockCount = 0
  379. let s:behavsCurrent = []
  380. let s:iBehavs = 0
  381. let s:snipItems = {}
  382. " }}}1
  383. "=============================================================================
  384. " vim: set fdm=marker: