My dotfiles. Period.

htmlcomplete.vim 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. " Vim completion script
  2. " Language: HTML and XHTML
  3. " Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
  4. " Last Change: 2006 Oct 19
  5. " Modified: othree <othree@gmail.com>
  6. " Changes: Add HTML5, WAI-ARIA support
  7. " Last Change: 2010 Sep 25
  8. if !exists('g:aria_attributes_complete')
  9. let g:aria_attributes_complete = 1
  10. endif
  11. function! htmlcomplete#CompleteTags(findstart, base)
  12. if a:findstart
  13. " locate the start of the word
  14. let line = getline('.')
  15. let start = col('.') - 1
  16. let curline = line('.')
  17. let compl_begin = col('.') - 2
  18. while start >= 0 && line[start - 1] =~ '\(\k\|[!:.-]\)'
  19. let start -= 1
  20. endwhile
  21. " Handling of entities {{{
  22. if start >= 0 && line[start - 1] =~ '&'
  23. let b:entitiescompl = 1
  24. let b:compl_context = ''
  25. return start
  26. endif
  27. " }}}
  28. " Handling of <style> tag {{{
  29. let stylestart = searchpair('<style\>', '', '<\/style\>', "bnW")
  30. let styleend = searchpair('<style\>', '', '<\/style\>', "nW")
  31. if stylestart != 0 && styleend != 0
  32. if stylestart <= curline && styleend >= curline
  33. let start = col('.') - 1
  34. let b:csscompl = 1
  35. while start >= 0 && line[start - 1] =~ '\(\k\|-\)'
  36. let start -= 1
  37. endwhile
  38. endif
  39. endif
  40. " }}}
  41. " Handling of <script> tag {{{
  42. let scriptstart = searchpair('<script\>', '', '<\/script\>', "bnW")
  43. let scriptend = searchpair('<script\>', '', '<\/script\>', "nW")
  44. if scriptstart != 0 && scriptend != 0
  45. if scriptstart <= curline && scriptend >= curline
  46. let start = col('.') - 1
  47. let b:jscompl = 1
  48. let b:jsrange = [scriptstart, scriptend]
  49. while start >= 0 && line[start - 1] =~ '\k'
  50. let start -= 1
  51. endwhile
  52. " We are inside of <script> tag. But we should also get contents
  53. " of all linked external files and (secondary, less probably) other <script> tags
  54. " This logic could possible be done in separate function - may be
  55. " reused in events scripting (also with option could be reused for
  56. " CSS
  57. let b:js_extfiles = []
  58. let l = line('.')
  59. let c = col('.')
  60. call cursor(1,1)
  61. while search('<\@<=script\>', 'W') && line('.') <= l
  62. if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
  63. let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
  64. if filereadable(sname)
  65. let b:js_extfiles += readfile(sname)
  66. endif
  67. endif
  68. endwhile
  69. call cursor(1,1)
  70. let js_scripttags = []
  71. while search('<script\>', 'W') && line('.') < l
  72. if matchstr(getline('.'), '<script[^>]*src') == ''
  73. let js_scripttag = getline(line('.'), search('</script>', 'W'))
  74. let js_scripttags += js_scripttag
  75. endif
  76. endwhile
  77. let b:js_extfiles += js_scripttags
  78. call cursor(l,c)
  79. unlet! l c
  80. endif
  81. endif
  82. " }}}
  83. if !exists("b:csscompl") && !exists("b:jscompl")
  84. let b:compl_context = getline('.')[0:(compl_begin)]
  85. if b:compl_context !~ '<[^>]*$'
  86. " Look like we may have broken tag. Check previous lines.
  87. let i = 1
  88. while 1
  89. let context_line = getline(curline-i)
  90. if context_line =~ '<[^>]*$'
  91. " Yep, this is this line
  92. let context_lines = getline(curline-i, curline-1) + [b:compl_context]
  93. let b:compl_context = join(context_lines, ' ')
  94. break
  95. elseif context_line =~ '>[^<]*$' || i == curline
  96. " We are in normal tag line, no need for completion at all
  97. " OR reached first line without tag at all
  98. let b:compl_context = ''
  99. break
  100. endif
  101. let i += 1
  102. endwhile
  103. " Make sure we don't have counter
  104. unlet! i
  105. endif
  106. let b:compl_context = matchstr(b:compl_context, '.*\zs<.*')
  107. " Return proper start for on-events. Without that beginning of
  108. " completion will be badly reported
  109. if b:compl_context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
  110. let start = col('.') - 1
  111. while start >= 0 && line[start - 1] =~ '\k'
  112. let start -= 1
  113. endwhile
  114. endif
  115. " If b:compl_context begins with <? we are inside of PHP code. It
  116. " wasn't closed so PHP completion passed it to HTML
  117. if &filetype =~? 'php' && b:compl_context =~ '^<?'
  118. let b:phpcompl = 1
  119. let start = col('.') - 1
  120. while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]'
  121. let start -= 1
  122. endwhile
  123. endif
  124. else
  125. let b:compl_context = getline('.')[0:compl_begin]
  126. endif
  127. return start
  128. else
  129. " Initialize base return lists
  130. let res = []
  131. let res2 = []
  132. " a:base is very short - we need context
  133. let context = b:compl_context
  134. " Check if we should do CSS completion inside of <style> tag
  135. " or JS completion inside of <script> tag or PHP completion in case of <?
  136. " tag AND &ft==php
  137. if exists("b:csscompl")
  138. unlet! b:csscompl
  139. let context = b:compl_context
  140. unlet! b:compl_context
  141. return csscomplete#CompleteCSS(0, context)
  142. elseif exists("b:jscompl")
  143. unlet! b:jscompl
  144. return javascriptcomplete#CompleteJS(0, a:base)
  145. elseif exists("b:phpcompl")
  146. unlet! b:phpcompl
  147. let context = b:compl_context
  148. return phpcomplete#CompletePHP(0, a:base)
  149. else
  150. if len(b:compl_context) == 0 && !exists("b:entitiescompl")
  151. return []
  152. endif
  153. let context = matchstr(b:compl_context, '.\zs.*')
  154. endif
  155. unlet! b:compl_context
  156. " Entities completion {{{
  157. if exists("b:entitiescompl")
  158. unlet! b:entitiescompl
  159. if !exists("b:html_doctype")
  160. call htmlcomplete#CheckDoctype()
  161. endif
  162. if !exists("b:html_omni")
  163. "runtime! autoload/xml/xhtml10s.vim
  164. call htmlcomplete#LoadData()
  165. endif
  166. if g:aria_attributes_complete == 1 && !exists("b:aria_omni")
  167. call htmlcomplete#LoadAria()
  168. endif
  169. let entities = b:html_omni['vimxmlentities']
  170. if len(a:base) == 1
  171. for m in entities
  172. if m =~ '^'.a:base
  173. call add(res, m.';')
  174. endif
  175. endfor
  176. return res
  177. else
  178. for m in entities
  179. if m =~? '^'.a:base
  180. call add(res, m.';')
  181. elseif m =~? a:base
  182. call add(res2, m.';')
  183. endif
  184. endfor
  185. return res + res2
  186. endif
  187. endif
  188. " }}}
  189. if context =~ '>'
  190. " Generally if context contains > it means we are outside of tag and
  191. " should abandon action - with one exception: <style> span { bo
  192. if context =~ 'style[^>]\{-}>[^<]\{-}$'
  193. return csscomplete#CompleteCSS(0, context)
  194. elseif context =~ 'script[^>]\{-}>[^<]\{-}$'
  195. let b:jsrange = [line('.'), search('<\/script\>', 'nW')]
  196. return javascriptcomplete#CompleteJS(0, context)
  197. else
  198. return []
  199. endif
  200. endif
  201. " If context contains > it means we are already outside of tag and we
  202. " should abandon action
  203. " If context contains white space it is attribute.
  204. " It can be also value of attribute.
  205. " We have to get first word to offer proper completions
  206. if context == ''
  207. let tag = ''
  208. else
  209. let tag = split(context)[0]
  210. " Detect if tag is uppercase to return in proper case,
  211. " we need to make it lowercase for processing
  212. if tag =~ '^\u*$'
  213. let uppercase_tag = 1
  214. let tag = tolower(tag)
  215. else
  216. let uppercase_tag = 0
  217. endif
  218. endif
  219. " Get last word, it should be attr name
  220. let attr = matchstr(context, '\S\+="[^"]*$')
  221. if attr == ''
  222. let attr = matchstr(context, '.*\s\zs.*')
  223. endif
  224. " Possible situations where any prediction would be difficult:
  225. " 1. Events attributes
  226. if context =~ '\s'
  227. " Sort out style, class, and on* cases
  228. if context =~? "\\s\\(on[a-z]+\\|id\\|style\\|class\\)\\s*=\\s*[\"']"
  229. " Id, class completion {{{
  230. if context =~? "\\(id\\|class\\)\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
  231. if context =~? "class\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
  232. let search_for = "class"
  233. elseif context =~? "id\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
  234. let search_for = "id"
  235. endif
  236. " Handle class name completion
  237. " 1. Find lines of <link stylesheet>
  238. " 1a. Check file for @import
  239. " 2. Extract filename(s?) of stylesheet,
  240. call cursor(1,1)
  241. let head = getline(search('<head\>'), search('<\/head>'))
  242. let headjoined = join(copy(head), ' ')
  243. if headjoined =~ '<style'
  244. " Remove possibly confusing CSS operators
  245. let stylehead = substitute(headjoined, '+>\*[,', ' ', 'g')
  246. if search_for == 'class'
  247. let styleheadlines = split(stylehead)
  248. let headclasslines = filter(copy(styleheadlines), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
  249. else
  250. let stylesheet = split(headjoined, '[{}]')
  251. " Get all lines which fit id syntax
  252. let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
  253. " Filter out possible color definitions
  254. call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
  255. " Filter out complex border definitions
  256. call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
  257. let templines = join(classlines, ' ')
  258. let headclasslines = split(templines)
  259. call filter(headclasslines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
  260. endif
  261. let internal = 1
  262. else
  263. let internal = 0
  264. endif
  265. let styletable = []
  266. let secimportfiles = []
  267. let filestable = filter(copy(head), "v:val =~ '\\(@import\\|link.*stylesheet\\)'")
  268. for line in filestable
  269. if line =~ "@import"
  270. let styletable += [matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")]
  271. elseif line =~ "<link"
  272. let styletable += [matchstr(line, "href\\s*=\\s*[\"']\\zs\\f\\+\\ze")]
  273. endif
  274. endfor
  275. for file in styletable
  276. if filereadable(file)
  277. let stylesheet = readfile(file)
  278. let secimport = filter(copy(stylesheet), "v:val =~ '@import'")
  279. if len(secimport) > 0
  280. for line in secimport
  281. let secfile = matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")
  282. let secfile = fnamemodify(file, ":p:h").'/'.secfile
  283. let secimportfiles += [secfile]
  284. endfor
  285. endif
  286. endif
  287. endfor
  288. let cssfiles = styletable + secimportfiles
  289. let classes = []
  290. for file in cssfiles
  291. if filereadable(file)
  292. let stylesheet = readfile(file)
  293. let stylefile = join(stylesheet, ' ')
  294. let stylefile = substitute(stylefile, '+>\*[,', ' ', 'g')
  295. if search_for == 'class'
  296. let stylesheet = split(stylefile)
  297. let classlines = filter(copy(stylesheet), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
  298. else
  299. let stylesheet = split(stylefile, '[{}]')
  300. " Get all lines which fit id syntax
  301. let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
  302. " Filter out possible color definitions
  303. call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
  304. " Filter out complex border definitions
  305. call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
  306. let templines = join(classlines, ' ')
  307. let stylelines = split(templines)
  308. let classlines = filter(stylelines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
  309. endif
  310. " We gathered classes definitions from all external files
  311. let classes += classlines
  312. endif
  313. endfor
  314. if internal == 1
  315. let classes += headclasslines
  316. endif
  317. if search_for == 'class'
  318. let elements = {}
  319. for element in classes
  320. if element =~ '^\.'
  321. let class = matchstr(element, '^\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
  322. let class = substitute(class, ':.*', '', '')
  323. if has_key(elements, 'common')
  324. let elements['common'] .= ' '.class
  325. else
  326. let elements['common'] = class
  327. endif
  328. else
  329. let class = matchstr(element, '[a-zA-Z1-6]*\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
  330. let tagname = tolower(matchstr(element, '[a-zA-Z1-6]*\ze.'))
  331. if tagname != ''
  332. if has_key(elements, tagname)
  333. let elements[tagname] .= ' '.class
  334. else
  335. let elements[tagname] = class
  336. endif
  337. endif
  338. endif
  339. endfor
  340. if has_key(elements, tag) && has_key(elements, 'common')
  341. let values = split(elements[tag]." ".elements['common'])
  342. elseif has_key(elements, tag) && !has_key(elements, 'common')
  343. let values = split(elements[tag])
  344. elseif !has_key(elements, tag) && has_key(elements, 'common')
  345. let values = split(elements['common'])
  346. else
  347. return []
  348. endif
  349. elseif search_for == 'id'
  350. " Find used IDs
  351. " 1. Catch whole file
  352. let filelines = getline(1, line('$'))
  353. " 2. Find lines with possible id
  354. let used_id_lines = filter(filelines, 'v:val =~ "id\\s*=\\s*[\"''][a-zA-Z0-9_-]\\+"')
  355. " 3a. Join all filtered lines
  356. let id_string = join(used_id_lines, ' ')
  357. " 3b. And split them to be sure each id is in separate item
  358. let id_list = split(id_string, 'id\s*=\s*')
  359. " 4. Extract id values
  360. let used_id = map(id_list, 'matchstr(v:val, "[\"'']\\zs[a-zA-Z0-9_-]\\+\\ze")')
  361. let joined_used_id = ','.join(used_id, ',').','
  362. let allvalues = map(classes, 'matchstr(v:val, ".*#\\zs[a-zA-Z0-9_-]\\+")')
  363. let values = []
  364. for element in classes
  365. if joined_used_id !~ ','.element.','
  366. let values += [element]
  367. endif
  368. endfor
  369. endif
  370. " We need special version of sbase
  371. let classbase = matchstr(context, ".*[\"']")
  372. let classquote = matchstr(classbase, '.$')
  373. let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
  374. for m in sort(values)
  375. if m =~? '^'.entered_class
  376. call add(res, m . classquote)
  377. elseif m =~? entered_class
  378. call add(res2, m . classquote)
  379. endif
  380. endfor
  381. return res + res2
  382. elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
  383. return csscomplete#CompleteCSS(0, context)
  384. endif
  385. " }}}
  386. " Complete on-events {{{
  387. if context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
  388. " We have to:
  389. " 1. Find external files
  390. let b:js_extfiles = []
  391. let l = line('.')
  392. let c = col('.')
  393. call cursor(1,1)
  394. while search('<\@<=script\>', 'W') && line('.') <= l
  395. if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
  396. let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
  397. if filereadable(sname)
  398. let b:js_extfiles += readfile(sname)
  399. endif
  400. endif
  401. endwhile
  402. " 2. Find at least one <script> tag
  403. call cursor(1,1)
  404. let js_scripttags = []
  405. while search('<script\>', 'W') && line('.') < l
  406. if matchstr(getline('.'), '<script[^>]*src') == ''
  407. let js_scripttag = getline(line('.'), search('</script>', 'W'))
  408. let js_scripttags += js_scripttag
  409. endif
  410. endwhile
  411. let b:js_extfiles += js_scripttags
  412. " 3. Proper call for javascriptcomplete#CompleteJS
  413. call cursor(l,c)
  414. let js_context = matchstr(a:base, '\k\+$')
  415. let js_shortcontext = substitute(a:base, js_context.'$', '', '')
  416. let b:compl_context = context
  417. let b:jsrange = [l, l]
  418. unlet! l c
  419. return javascriptcomplete#CompleteJS(0, js_context)
  420. endif
  421. " }}}
  422. let stripbase = matchstr(context, ".*\\(on[a-zA-Z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
  423. " Now we have context stripped from all chars up to style/class.
  424. " It may fail with some strange style value combinations.
  425. if stripbase !~ "[\"']"
  426. return []
  427. endif
  428. endif
  429. " Value of attribute completion {{{
  430. " If attr contains =\s*[\"'] we catched value of attribute
  431. if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
  432. " Let do attribute specific completion
  433. let attrname = matchstr(attr, '.*\ze\s*=')
  434. let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
  435. let values = []
  436. " Load data {{{
  437. if !exists("b:html_doctype")
  438. call htmlcomplete#CheckDoctype()
  439. endif
  440. if !exists("b:html_omni")
  441. "runtime! autoload/xml/xhtml10s.vim
  442. call htmlcomplete#LoadData()
  443. endif
  444. if g:aria_attributes_complete == 1 && !exists("b:aria_omni")
  445. call htmlcomplete#LoadAria()
  446. endif
  447. " }}}
  448. if attrname == 'href'
  449. " Now we are looking for local anchors defined by name or id
  450. if entered_value =~ '^#'
  451. let file = join(getline(1, line('$')), ' ')
  452. " Split it be sure there will be one id/name element in
  453. " item, it will be also first word [a-zA-Z0-9_-] in element
  454. let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
  455. for i in oneelement
  456. let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
  457. endfor
  458. endif
  459. else
  460. if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
  461. let values = b:html_omni[tag][1][attrname]
  462. elseif attrname =~ '^aria-' && exists("b:aria_omni") && has_key(b:aria_omni['aria_attributes'], attrname)
  463. let values = b:aria_omni['aria_attributes'][attrname]
  464. else
  465. return []
  466. endif
  467. endif
  468. if len(values) == 0
  469. return []
  470. endif
  471. " We need special version of sbase
  472. let attrbase = matchstr(context, ".*[\"']")
  473. let attrquote = matchstr(attrbase, '.$')
  474. if attrquote !~ "['\"]"
  475. let attrquoteopen = '"'
  476. let attrquote = '"'
  477. else
  478. let attrquoteopen = ''
  479. endif
  480. " Multi value attributes don't need ending quote
  481. let info = ''
  482. if has_key(b:html_omni['vimxmlattrinfo'], attrname)
  483. let info = b:html_omni['vimxmlattrinfo'][attrname][0]
  484. elseif exists("b:aria_omni") && has_key(b:aria_omni['vimariaattrinfo'], attrname)
  485. let info = b:aria_omni['vimariaattrinfo'][attrname][0]
  486. endif
  487. if info =~ "^\\*"
  488. let attrquote = ''
  489. endif
  490. if len(entered_value) > 0
  491. if entered_value =~ "\\s$"
  492. let entered_value = ''
  493. else
  494. let entered_value = split(entered_value)[-1]
  495. endif
  496. endif
  497. for m in values
  498. " This if is needed to not offer all completions as-is
  499. " alphabetically but sort them. Those beginning with entered
  500. " part will be as first choices
  501. if m =~ '^'.entered_value
  502. call add(res, attrquoteopen . m . attrquote)
  503. elseif m =~ entered_value
  504. call add(res2, attrquoteopen . m . attrquote)
  505. endif
  506. endfor
  507. return res + res2
  508. endif
  509. " }}}
  510. " Attribute completion {{{
  511. " Shorten context to not include last word
  512. let sbase = matchstr(context, '.*\ze\s.*')
  513. " Load data {{{
  514. if !exists("b:html_doctype")
  515. call htmlcomplete#CheckDoctype()
  516. endif
  517. if !exists("b:html_omni")
  518. call htmlcomplete#LoadData()
  519. endif
  520. if g:aria_attributes_complete == 1 && !exists("b:aria_omni")
  521. call htmlcomplete#LoadAria()
  522. endif
  523. " }}}
  524. if has_key(b:html_omni, tag)
  525. let attrs = keys(b:html_omni[tag][1])
  526. else
  527. return []
  528. endif
  529. if exists("b:aria_omni")
  530. let roles = []
  531. if has_key(b:aria_omni['default_role'], tag)
  532. let roles = [b:aria_omni['default_role'][tag]]
  533. endif
  534. if context =~ 'role='
  535. let start = matchend(context, "role=['\"]")
  536. let end = matchend(context, "[a-z ]\\+['\"]", start)
  537. if start != -1 && end != -1
  538. let roles = split(strpart(context, start, end-start-1), " ")
  539. endif
  540. endif
  541. for i in range(len(roles))
  542. let role = roles[i]
  543. if has_key(b:aria_omni['role_attributes'], role)
  544. let attrs = extend(attrs, b:aria_omni['role_attributes'][role])
  545. endif
  546. endfor
  547. endif
  548. for m in sort(attrs)
  549. if m =~ '^'.attr
  550. call add(res, m)
  551. elseif m =~ attr
  552. call add(res2, m)
  553. endif
  554. endfor
  555. "let menu = res + res2
  556. let menu = res
  557. if has_key(b:html_omni, 'vimxmlattrinfo') || (exists("b:aria_omni") && has_key(b:aria_omni, 'vimariaattrinfo'))
  558. let final_menu = []
  559. for i in range(len(menu))
  560. let item = menu[i]
  561. if has_key(b:html_omni['vimxmlattrinfo'], item)
  562. let m_menu = b:html_omni['vimxmlattrinfo'][item][0]
  563. let m_info = b:html_omni['vimxmlattrinfo'][item][1]
  564. elseif exists("b:aria_omni") && has_key(b:aria_omni['vimariaattrinfo'], item)
  565. let m_menu = b:aria_omni['vimariaattrinfo'][item][0]
  566. let m_info = b:aria_omni['vimariaattrinfo'][item][1]
  567. else
  568. let m_menu = ''
  569. let m_info = ''
  570. endif
  571. if item =~ '^aria-' && exists("b:aria_omni")
  572. if len(b:aria_omni['aria_attributes'][item]) > 0 && b:aria_omni['aria_attributes'][item][0] =~ '^\(BOOL\|'.item.'\)$'
  573. let item = item
  574. let m_menu = 'Bool'
  575. else
  576. let item .= '="'
  577. endif
  578. else
  579. if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
  580. let item = item
  581. let m_menu = 'Bool'
  582. else
  583. let item .= '="'
  584. endif
  585. endif
  586. let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
  587. endfor
  588. else
  589. let final_menu = []
  590. for i in range(len(menu))
  591. let item = menu[i]
  592. if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
  593. let item = item
  594. else
  595. let item .= '="'
  596. endif
  597. let final_menu += [item]
  598. endfor
  599. return final_menu
  600. endif
  601. return final_menu
  602. endif
  603. " }}}
  604. " Close tag {{{
  605. let b:unaryTagsStack = "area base br col command embed hr img input keygen link meta param source track wbr"
  606. if context =~ '^\/'
  607. if context =~ '^\/.'
  608. return []
  609. else
  610. let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
  611. return [opentag.">"]
  612. endif
  613. endif
  614. " }}}
  615. " Load data {{{
  616. if !exists("b:html_doctype")
  617. call htmlcomplete#CheckDoctype()
  618. endif
  619. if !exists("b:html_omni")
  620. "runtime! autoload/xml/xhtml10s.vim
  621. call htmlcomplete#LoadData()
  622. endif
  623. if g:aria_attributes_complete == 1 && !exists("b:aria_omni")
  624. call htmlcomplete#LoadAria()
  625. endif
  626. " }}}
  627. " Tag completion {{{
  628. " Deal with tag completion.
  629. let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
  630. " MM: TODO: GLOT works always the same but with some weird situation it
  631. " behaves as intended in HTML but screws in PHP
  632. if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
  633. " Hack for sometimes failing GetLastOpenTag.
  634. " As far as I tested fail isn't GLOT fault but problem
  635. " of invalid document - not properly closed tags and other mish-mash.
  636. " Also when document is empty. Return list of *all* tags.
  637. let tags = keys(b:html_omni)
  638. call filter(tags, 'v:val !~ "^vimxml"')
  639. else
  640. if has_key(b:html_omni, opentag)
  641. let tags = b:html_omni[opentag][0]
  642. else
  643. return []
  644. endif
  645. endif
  646. " }}}
  647. if exists("uppercase_tag") && uppercase_tag == 1
  648. let context = tolower(context)
  649. endif
  650. " Handle XML keywords: DOCTYPE
  651. if opentag == ''
  652. let tags = [
  653. \ '!DOCTYPE html>',
  654. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">',
  655. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">',
  656. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
  657. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">',
  658. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
  659. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
  660. \ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
  661. \ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
  662. \ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
  663. \ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
  664. \ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml">'
  665. \ ] + sort(tags)
  666. endif
  667. "for m in sort(tags)
  668. for m in tags
  669. if m =~ '^'.context
  670. call add(res, m)
  671. elseif m =~ context
  672. call add(res2, m)
  673. endif
  674. endfor
  675. let menu = res + res2
  676. if has_key(b:html_omni, 'vimxmltaginfo')
  677. let final_menu = []
  678. for i in range(len(menu))
  679. let item = menu[i]
  680. if has_key(b:html_omni['vimxmltaginfo'], item)
  681. let m_menu = b:html_omni['vimxmltaginfo'][item][0]
  682. let m_info = b:html_omni['vimxmltaginfo'][item][1]
  683. else
  684. let m_menu = ''
  685. let m_info = ''
  686. endif
  687. if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
  688. let item = toupper(item)
  689. endif
  690. if item =~ 'DOCTYPE'
  691. if item =~ 'DTD'
  692. let abbr = 'DOCTYPE '.matchstr(item, 'DTD \zsX\?HTML .\{-}\ze\/\/')
  693. else
  694. let abbr = 'DOCTYPE HTML 5'
  695. endif
  696. else
  697. let abbr = item
  698. endif
  699. let final_menu += [{'abbr':abbr, 'word':item, 'menu':m_menu, 'info':m_info}]
  700. endfor
  701. else
  702. let final_menu = menu
  703. endif
  704. return final_menu
  705. " }}}
  706. endif
  707. endfunction
  708. function! htmlcomplete#LoadAria() " {{{
  709. runtime! autoload/xml/aria.vim
  710. if exists("g:xmldata_aria")
  711. \ && has_key(g:xmldata_aria, 'default_role')
  712. \ && has_key(g:xmldata_aria, 'role_attributes')
  713. \ && has_key(g:xmldata_aria, 'vimariaattrinfo')
  714. \ && has_key(g:xmldata_aria, 'aria_attributes')
  715. let b:aria_omni = g:xmldata_aria
  716. else
  717. let g:aria_attributes_complete = 0
  718. endif
  719. endfunction
  720. " }}}
  721. function! htmlcomplete#LoadData() " {{{
  722. if !exists("b:html_omni_flavor")
  723. if &filetype == 'html'
  724. let b:html_omni_flavor = 'html401t'
  725. else
  726. let b:html_omni_flavor = 'xhtml10s'
  727. endif
  728. endif
  729. " With that if we still have bloated memory but create new buffer
  730. " variables only by linking to existing g:variable, not sourcing whole
  731. " file.
  732. if exists('g:xmldata_'.b:html_omni_flavor)
  733. exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
  734. else
  735. exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
  736. exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
  737. endif
  738. endfunction
  739. " }}}
  740. function! htmlcomplete#CheckDoctype() " {{{
  741. if exists('b:html_omni_flavor')
  742. let old_flavor = b:html_omni_flavor
  743. else
  744. let old_flavor = ''
  745. endif
  746. let i = 1
  747. while i < 10 && i < line("$")
  748. let line = getline(i)
  749. if line =~ '<!DOCTYPE.*\<DTD HTML 3\.2'
  750. let b:html_omni_flavor = 'html32'
  751. let b:html_doctype = 1
  752. break
  753. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Transitional'
  754. let b:html_omni_flavor = 'html40t'
  755. let b:html_doctype = 1
  756. break
  757. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Frameset'
  758. let b:html_omni_flavor = 'html40f'
  759. let b:html_doctype = 1
  760. break
  761. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0'
  762. let b:html_omni_flavor = 'html40s'
  763. let b:html_doctype = 1
  764. break
  765. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Transitional'
  766. let b:html_omni_flavor = 'html401t'
  767. let b:html_doctype = 1
  768. break
  769. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Frameset'
  770. let b:html_omni_flavor = 'html401f'
  771. let b:html_doctype = 1
  772. break
  773. elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01'
  774. let b:html_omni_flavor = 'html401s'
  775. let b:html_doctype = 1
  776. break
  777. elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Transitional'
  778. let b:html_omni_flavor = 'xhtml10t'
  779. let b:html_doctype = 1
  780. break
  781. elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Frameset'
  782. let b:html_omni_flavor = 'xhtml10f'
  783. let b:html_doctype = 1
  784. break
  785. elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Strict'
  786. let b:html_omni_flavor = 'xhtml10s'
  787. let b:html_doctype = 1
  788. break
  789. elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.1'
  790. let b:html_omni_flavor = 'xhtml11'
  791. let b:html_doctype = 1
  792. break
  793. elseif line =~ '<!DOCTYPE html'
  794. let b:html_omni_flavor = 'html5'
  795. let b:html_doctype = 1
  796. break
  797. endif
  798. let i += 1
  799. endwhile
  800. if !exists("b:html_doctype")
  801. return
  802. else
  803. " Tie g:xmldata with b:html_omni this way we need to sourca data file only
  804. " once, not every time per buffer.
  805. if old_flavor == b:html_omni_flavor
  806. return
  807. else
  808. if exists('g:xmldata_'.b:html_omni_flavor)
  809. exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
  810. else
  811. exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
  812. exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
  813. endif
  814. return
  815. endif
  816. endif
  817. endfunction
  818. " }}}
  819. " vim:set foldmethod=marker: