highlight.vim 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. scriptencoding utf-8
  2. let s:is_vim = !has('nvim')
  3. let s:clear_match_by_window = has('nvim-0.6.0') || has('patch-8.1.1084')
  4. let s:set_extmark = has('nvim') && exists('*nvim_buf_set_extmark')
  5. let s:prop_offset = get(g:, 'coc_text_prop_offset', 1000)
  6. let s:namespace_map = {}
  7. let s:ns_id = 1
  8. let s:diagnostic_hlgroups = ['CocErrorHighlight', 'CocWarningHighlight', 'CocInfoHighlight', 'CocHintHighlight', 'CocDeprecatedHighlight', 'CocUnusedHighlight']
  9. " Maximum count to highlight each time.
  10. let g:coc_highlight_maximum_count = get(g:, 'coc_highlight_maximum_count', 100)
  11. if has('nvim-0.5.0') && s:clear_match_by_window == 0
  12. try
  13. call getmatches(0)
  14. let s:clear_match_by_window = 1
  15. catch /^Vim\%((\a\+)\)\=:E118/
  16. " ignored
  17. endtry
  18. endif
  19. " Update buffer region by region.
  20. function! coc#highlight#buffer_update(bufnr, key, highlights, ...) abort
  21. if !bufloaded(a:bufnr)
  22. return
  23. endif
  24. if empty(a:highlights)
  25. call coc#highlight#clear_highlight(a:bufnr, a:key, 0, -1)
  26. return
  27. endif
  28. let priority = get(a:, 1, v:null)
  29. let changedtick = getbufvar(a:bufnr, 'changedtick', 0)
  30. if type(get(a:, 2, v:null)) == 0 && changedtick > a:2
  31. return
  32. endif
  33. let hls = map(copy(a:highlights), "{'hlGroup':v:val[0],'lnum':v:val[1],'colStart':v:val[2],'colEnd':v:val[3],'combine':get(v:val,4,1),'start_incl':get(v:val,5,0),'end_incl':get(v:val,6,0)}")
  34. if len(hls) <= g:coc_highlight_maximum_count || get(g:, 'coc_node_env', '') ==# 'test'
  35. call coc#highlight#update_highlights(a:bufnr, a:key, hls, 0, -1, priority)
  36. return
  37. endif
  38. let linecount = coc#compat#buf_line_count(a:bufnr)
  39. let groups = s:group_hls(hls, linecount)
  40. call s:update_highlights_timer(a:bufnr, changedtick, a:key, priority, groups, 0)
  41. endfunction
  42. " Update highlights by check exists highlights.
  43. " 0 based, end exclusive start and end
  44. function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
  45. let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
  46. if !bufloaded(bufnr)
  47. return
  48. endif
  49. let start = get(a:, 1, 0)
  50. let end = get(a:, 2, -1)
  51. if end == 0
  52. return
  53. endif
  54. let linecount = coc#compat#buf_line_count(a:bufnr)
  55. if end >= linecount
  56. let end = -1
  57. endif
  58. if empty(a:highlights)
  59. call coc#highlight#clear_highlight(bufnr, a:key, start, end)
  60. return
  61. endif
  62. let priority = get(a:, 3, v:null)
  63. let total = len(a:highlights)
  64. " index list that exists with current highlights
  65. let exists = []
  66. let ns = coc#highlight#create_namespace(a:key)
  67. if has('nvim-0.5.0') || exists('*prop_list')
  68. let endLnum = end < 0 ? linecount - 1 : end - 1
  69. let firstLnum = a:highlights[0]['lnum']
  70. if firstLnum > start
  71. call coc#highlight#clear_highlight(bufnr, a:key, start, firstLnum)
  72. let start = firstLnum
  73. endif
  74. let lastLnum = a:highlights[total - 1]['lnum']
  75. if lastLnum < endLnum
  76. call coc#highlight#clear_highlight(bufnr, a:key, lastLnum + 1, endLnum + 1)
  77. let endLnum = lastLnum
  78. endif
  79. let current = coc#highlight#get_highlights(bufnr, a:key, start, endLnum)
  80. let currIndex = 0
  81. if !empty(current)
  82. for [lnum, items] in s:to_group(current)
  83. let indexes = []
  84. let currIndexes = range(0, len(items) - 1)
  85. let removeIndexes = []
  86. while currIndex != total
  87. let hi = a:highlights[currIndex]
  88. if hi['lnum'] == lnum
  89. let findIndex = -1
  90. for idx in currIndexes
  91. let item = items[idx]
  92. if hi['hlGroup'] ==# item[0] && hi['colStart'] == item[2] && hi['colEnd'] == item[3]
  93. call add(indexes, currIndex)
  94. let findIndex = idx
  95. break
  96. elseif item[2] > hi['colStart']
  97. break
  98. endif
  99. endfor
  100. if findIndex != -1
  101. call filter(currIndexes, 'v:val != '.findIndex)
  102. endif
  103. elseif hi['lnum'] > lnum
  104. break
  105. endif
  106. let currIndex = currIndex + 1
  107. endwhile
  108. for idx in currIndexes
  109. if s:is_vim
  110. call prop_remove({'bufnr': bufnr, 'id': items[idx][4]})
  111. else
  112. call nvim_buf_del_extmark(bufnr, ns, items[idx][4])
  113. endif
  114. endfor
  115. call extend(exists, indexes)
  116. endfor
  117. endif
  118. else
  119. call coc#highlight#clear_highlight(bufnr, a:key, start, end)
  120. endif
  121. let indexes = range(0, total - 1)
  122. if !empty(exists)
  123. let indexes = filter(indexes, 'index(exists, v:val) == -1')
  124. endif
  125. for idx in indexes
  126. let hi = a:highlights[idx]
  127. let opts = {
  128. \ 'combine': get(hi, 'combine', 0),
  129. \ 'start_incl': get(hi, 'start_incl', 0),
  130. \ 'end_incl': get(hi, 'end_incl', 0),
  131. \ }
  132. if type(priority) == 0
  133. let opts['priority'] = s:get_priority(a:key, hi['hlGroup'], priority)
  134. endif
  135. call coc#highlight#add_highlight(bufnr, ns, hi['hlGroup'], hi['lnum'], hi['colStart'], hi['colEnd'], opts)
  136. endfor
  137. endfunction
  138. " Get list of highlights by range or all buffer.
  139. " 0 based line, start_col and end_col
  140. " 0 based start & end line, end inclusive.
  141. function! coc#highlight#get_highlights(bufnr, key, ...) abort
  142. if !bufloaded(a:bufnr)
  143. return v:null
  144. endif
  145. if !has_key(s:namespace_map, a:key)
  146. return []
  147. endif
  148. let start = get(a:, 1, 0)
  149. let end = get(a:, 2, -1)
  150. let res = []
  151. let ns = s:namespace_map[a:key]
  152. if exists('*prop_list')
  153. let types = coc#api#get_types(ns)
  154. if empty(types)
  155. return res
  156. endif
  157. " Could filter by end_lnum and types
  158. if has('patch-8.2.3652')
  159. let endLnum = end == -1 ? -1 : end + 1
  160. for prop in prop_list(start + 1, {'bufnr': a:bufnr, 'types': types, 'end_lnum': endLnum})
  161. if prop['start'] == 0 || prop['end'] == 0
  162. " multi line textprop are not supported, simply ignore it
  163. continue
  164. endif
  165. let startCol = prop['col'] - 1
  166. let endCol = startCol + prop['length']
  167. call add(res, [s:prop_type_hlgroup(prop['type']), prop['lnum'] - 1, startCol, endCol, prop['id']])
  168. endfor
  169. else
  170. if end == -1
  171. let end = coc#compat#buf_line_count(a:bufnr)
  172. else
  173. let end = end + 1
  174. endif
  175. for line in range(start + 1, end)
  176. for prop in prop_list(line, {'bufnr': a:bufnr})
  177. if index(types, prop['type']) == -1 || prop['start'] == 0 || prop['end'] == 0
  178. " multi line textprop are not supported, simply ignore it
  179. continue
  180. endif
  181. let startCol = prop['col'] - 1
  182. let endCol = startCol + prop['length']
  183. call add(res, [s:prop_type_hlgroup(prop['type']), line - 1, startCol, endCol, prop['id']])
  184. endfor
  185. endfor
  186. endif
  187. elseif has('nvim-0.5.0')
  188. let start = [start, 0]
  189. let maximum = end == -1 ? nvim_buf_line_count(a:bufnr) : end + 1
  190. let end = end == -1 ? -1 : [end + 1, 0]
  191. let markers = nvim_buf_get_extmarks(a:bufnr, ns, start, -1, {'details': v:true})
  192. for [marker_id, line, start_col, details] in markers
  193. if line >= maximum
  194. " Could be markers exceed end of line
  195. continue
  196. endif
  197. let delta = details['end_row'] - line
  198. if delta > 1 || (delta == 1 && details['end_col'] != 0)
  199. " can't handle, single line only
  200. continue
  201. endif
  202. let endCol = details['end_col']
  203. if endCol == start_col
  204. call nvim_buf_del_extmark(a:bufnr, ns, marker_id)
  205. continue
  206. endif
  207. if delta == 1
  208. let text = get(nvim_buf_get_lines(a:bufnr, line, line + 1, 0), 0, '')
  209. let endCol = strlen(text)
  210. endif
  211. call add(res, [details['hl_group'], line, start_col, endCol, marker_id])
  212. endfor
  213. else
  214. throw 'Get highlights requires neovim 0.5.0 or vim support prop_list'
  215. endif
  216. return res
  217. endfunction
  218. " Add multiple highlights to buffer.
  219. " type HighlightItem = [hlGroup, lnum, colStart, colEnd, combine?, start_incl?, end_incl?]
  220. function! coc#highlight#set(bufnr, key, highlights, priority) abort
  221. if !bufloaded(a:bufnr)
  222. return
  223. endif
  224. let ns = coc#highlight#create_namespace(a:key)
  225. if len(a:highlights) > g:coc_highlight_maximum_count
  226. call s:add_highlights_timer(a:bufnr, ns, a:highlights, a:priority)
  227. else
  228. call s:add_highlights(a:bufnr, ns, a:highlights, a:priority)
  229. endif
  230. endfunction
  231. " Clear highlights by 0 based line numbers.
  232. function! coc#highlight#clear(bufnr, key, lnums) abort
  233. if !bufloaded(a:bufnr) || empty(a:lnums)
  234. return
  235. endif
  236. let ns = coc#highlight#create_namespace(a:key)
  237. for lnum in a:lnums
  238. if has('nvim')
  239. call nvim_buf_clear_namespace(a:bufnr, ns, lnum, lnum + 1)
  240. else
  241. call coc#api#exec('buf_clear_namespace', [a:bufnr, ns, lnum, lnum + 1])
  242. endif
  243. endfor
  244. " clear highlights in invalid line.
  245. if has('nvim')
  246. let linecount = nvim_buf_line_count(a:bufnr)
  247. call nvim_buf_clear_namespace(a:bufnr, ns, linecount, -1)
  248. endif
  249. endfunction
  250. function! coc#highlight#del_markers(bufnr, key, ids) abort
  251. if !bufloaded(a:bufnr)
  252. return
  253. endif
  254. let ns = coc#highlight#create_namespace(a:key)
  255. for id in a:ids
  256. if s:is_vim
  257. call prop_remove({'bufnr': a:bufnr, 'id': id})
  258. else
  259. call nvim_buf_del_extmark(a:bufnr, ns, id)
  260. endif
  261. endfor
  262. endfunction
  263. " highlight LSP range,
  264. function! coc#highlight#ranges(bufnr, key, hlGroup, ranges, ...) abort
  265. let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
  266. if !bufloaded(bufnr) || !exists('*getbufline')
  267. return
  268. endif
  269. let opts = get(a:, 1, {})
  270. let synmaxcol = getbufvar(a:bufnr, '&synmaxcol', 1000)
  271. if synmaxcol == 0
  272. let synmaxcol = 1000
  273. endif
  274. let synmaxcol = min([synmaxcol, 1000])
  275. let srcId = coc#highlight#create_namespace(a:key)
  276. for range in a:ranges
  277. let start = range['start']
  278. let end = range['end']
  279. for lnum in range(start['line'] + 1, end['line'] + 1)
  280. let arr = getbufline(bufnr, lnum)
  281. let line = empty(arr) ? '' : arr[0]
  282. if empty(line)
  283. continue
  284. endif
  285. if start['character'] > synmaxcol || end['character'] > synmaxcol
  286. continue
  287. endif
  288. " TODO don't know how to count UTF16 code point, should work most cases.
  289. let colStart = lnum == start['line'] + 1 ? strlen(strcharpart(line, 0, start['character'])) : 0
  290. let colEnd = lnum == end['line'] + 1 ? strlen(strcharpart(line, 0, end['character'])) : strlen(line)
  291. if colStart == colEnd
  292. continue
  293. endif
  294. call coc#highlight#add_highlight(bufnr, srcId, a:hlGroup, lnum - 1, colStart, colEnd, opts)
  295. endfor
  296. endfor
  297. endfunction
  298. function! coc#highlight#add_highlight(bufnr, src_id, hl_group, line, col_start, col_end, ...) abort
  299. let opts = get(a:, 1, {})
  300. let priority = get(opts, 'priority', v:null)
  301. if !s:is_vim
  302. if s:set_extmark && a:src_id != -1
  303. " get(opts, 'start_incl', 0) ? v:true : v:false,
  304. try
  305. call nvim_buf_set_extmark(a:bufnr, a:src_id, a:line, a:col_start, {
  306. \ 'end_col': a:col_end,
  307. \ 'hl_group': a:hl_group,
  308. \ 'hl_mode': get(opts, 'combine', 1) ? 'combine' : 'replace',
  309. \ 'right_gravity': v:true,
  310. \ 'end_right_gravity': v:false,
  311. \ 'priority': type(priority) == 0 ? min([priority, 4096]) : 4096,
  312. \ })
  313. catch /^Vim\%((\a\+)\)\=:E5555/
  314. " the end_col could be invalid, ignore this error
  315. endtry
  316. else
  317. call nvim_buf_add_highlight(a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end)
  318. endif
  319. else
  320. call coc#api#exec('buf_add_highlight', [a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end, opts])
  321. endif
  322. endfunction
  323. function! coc#highlight#clear_highlight(bufnr, key, start_line, end_line) abort
  324. let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
  325. if !bufloaded(bufnr)
  326. return
  327. endif
  328. let src_id = coc#highlight#create_namespace(a:key)
  329. if has('nvim')
  330. call nvim_buf_clear_namespace(a:bufnr, src_id, a:start_line, a:end_line)
  331. else
  332. call coc#api#exec('buf_clear_namespace', [a:bufnr, src_id, a:start_line, a:end_line])
  333. endif
  334. endfunction
  335. " highlight buffer in winid with CodeBlock &HighlightItems
  336. " export interface HighlightItem {
  337. " lnum: number // 0 based
  338. " hlGroup: string
  339. " colStart: number // 0 based
  340. " colEnd: number
  341. " }
  342. " export interface CodeBlock {
  343. " filetype?: string
  344. " hlGroup?: string
  345. " startLine: number // 0 based
  346. " endLine: number
  347. " }
  348. function! coc#highlight#add_highlights(winid, codes, highlights) abort
  349. if get(g:, 'coc_node_env', '') ==# 'test'
  350. call setwinvar(a:winid, 'highlights', a:highlights)
  351. endif
  352. " clear highlights
  353. call coc#compat#execute(a:winid, 'syntax clear')
  354. let bufnr = winbufnr(a:winid)
  355. call coc#highlight#clear_highlight(bufnr, -1, 0, -1)
  356. if !empty(a:codes)
  357. call coc#highlight#highlight_lines(a:winid, a:codes)
  358. endif
  359. if !empty(a:highlights)
  360. for item in a:highlights
  361. let hlGroup = item['hlGroup']
  362. let opts = hlGroup =~# 'Search$' ? {'priority': 999, 'combine': 1} : {}
  363. call coc#highlight#add_highlight(bufnr, -1, hlGroup, item['lnum'], item['colStart'], item['colEnd'])
  364. endfor
  365. endif
  366. endfunction
  367. " Add highlights to line groups of winid, support hlGroup and filetype
  368. " config should have startLine, endLine (0 based, end excluded) and filetype or hlGroup
  369. " endLine should > startLine and endLine is excluded
  370. "
  371. " export interface CodeBlock {
  372. " filetype?: string
  373. " hlGroup?: string
  374. " startLine: number // 0 based
  375. " endLine: number
  376. " }
  377. function! coc#highlight#highlight_lines(winid, blocks) abort
  378. let region_id = 1
  379. let defined = []
  380. let cmds = []
  381. for config in a:blocks
  382. let start = config['startLine'] + 1
  383. let end = config['endLine'] == -1 ? len(getbufline(winbufnr(a:winid), 1, '$')) + 1 : config['endLine'] + 1
  384. let filetype = get(config, 'filetype', '')
  385. let hlGroup = get(config, 'hlGroup', '')
  386. if !empty(hlGroup)
  387. call add(cmds, 'syntax region '.hlGroup.' start=/\%'.start.'l/ end=/\%'.end.'l/')
  388. else
  389. let filetype = matchstr(filetype, '\v^\w+')
  390. if empty(filetype) || filetype == 'txt' || index(get(g:, 'coc_markdown_disabled_languages', []), filetype) != -1
  391. continue
  392. endif
  393. if index(defined, filetype) == -1
  394. call add(cmds, 'syntax include @'.toupper(filetype).' syntax/'.filetype.'.vim')
  395. call add(cmds, 'unlet! b:current_syntax')
  396. call add(defined, filetype)
  397. endif
  398. call add(cmds, 'syntax region CodeBlock'.region_id.' start=/\%'.start.'l/ end=/\%'.end.'l/ contains=@'.toupper(filetype).' keepend')
  399. let region_id = region_id + 1
  400. endif
  401. endfor
  402. if !empty(cmds)
  403. call coc#compat#execute(a:winid, cmds, 'silent!')
  404. endif
  405. endfunction
  406. " Compose hlGroups with foreground and background colors.
  407. function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort
  408. let hlGroup = 'Fg'.a:fgGroup.'Bg'.a:bgGroup
  409. if a:fgGroup ==# a:bgGroup
  410. return a:fgGroup
  411. endif
  412. if hlexists(hlGroup) && match(execute('hi '.hlGroup, 'silent!'), 'cleared') == -1
  413. return hlGroup
  414. endif
  415. let fgId = synIDtrans(hlID(a:fgGroup))
  416. let bgId = synIDtrans(hlID(a:bgGroup))
  417. let isGuiReversed = synIDattr(fgId, 'reverse', 'gui') !=# '1' || synIDattr(bgId, 'reverse', 'gui') !=# '1'
  418. let guifg = isGuiReversed ? synIDattr(fgId, 'fg', 'gui') : synIDattr(fgId, 'bg', 'gui')
  419. let guibg = isGuiReversed ? synIDattr(bgId, 'bg', 'gui') : synIDattr(bgId, 'fg', 'gui')
  420. let isCtermReversed = synIDattr(fgId, 'reverse', 'cterm') !=# '1' || synIDattr(bgId, 'reverse', 'cterm') !=# '1'
  421. let ctermfg = isCtermReversed ? synIDattr(fgId, 'fg', 'cterm') : synIDattr(fgId, 'bg', 'cterm')
  422. let ctermbg = isCtermReversed ? synIDattr(bgId, 'bg', 'cterm') : synIDattr(bgId, 'fg', 'cterm')
  423. let bold = synIDattr(fgId, 'bold') ==# '1'
  424. let italic = synIDattr(fgId, 'italic') ==# '1'
  425. let underline = synIDattr(fgId, 'underline') ==# '1'
  426. let cmd = 'silent hi ' . hlGroup
  427. if !empty(guifg)
  428. let cmd .= ' guifg=' . guifg
  429. endif
  430. if !empty(ctermfg)
  431. let cmd .= ' ctermfg=' . ctermfg
  432. elseif guifg =~# '^#'
  433. let cmd .= ' ctermfg=' . coc#color#rgb2term(strpart(guifg, 1))
  434. endif
  435. if !empty(guibg)
  436. let cmd .= ' guibg=' . guibg
  437. endif
  438. if !empty(ctermbg)
  439. let cmd .= ' ctermbg=' . ctermbg
  440. elseif guibg =~# '^#'
  441. let cmd .= ' ctermbg=' . coc#color#rgb2term(strpart(guibg, 1))
  442. endif
  443. if bold
  444. let cmd .= ' cterm=bold gui=bold'
  445. elseif italic
  446. let cmd .= ' cterm=italic gui=italic'
  447. elseif underline
  448. let cmd .= ' cterm=underline gui=underline'
  449. endif
  450. if cmd ==# 'silent hi ' . hlGroup
  451. return 'Normal'
  452. endif
  453. execute cmd
  454. return hlGroup
  455. endfunction
  456. " add matches for winid, use 0 for current window.
  457. function! coc#highlight#match_ranges(winid, bufnr, ranges, hlGroup, priority) abort
  458. let winid = a:winid == 0 ? win_getid() : a:winid
  459. let bufnr = a:bufnr == 0 ? winbufnr(winid) : a:bufnr
  460. if empty(getwininfo(winid)) || (a:bufnr != 0 && winbufnr(a:winid) != a:bufnr)
  461. " not valid
  462. return []
  463. endif
  464. if !s:clear_match_by_window
  465. let curr = win_getid()
  466. if has('nvim')
  467. noa call nvim_set_current_win(winid)
  468. else
  469. noa call win_gotoid(winid)
  470. endif
  471. endif
  472. let ids = []
  473. for range in a:ranges
  474. let pos = []
  475. let start = range['start']
  476. let end = range['end']
  477. for lnum in range(start['line'] + 1, end['line'] + 1)
  478. let arr = getbufline(bufnr, lnum)
  479. let line = empty(arr) ? '' : arr[0]
  480. if empty(line)
  481. continue
  482. endif
  483. let colStart = lnum == start['line'] + 1 ? strlen(strcharpart(line, 0, start['character'])) + 1 : 1
  484. let colEnd = lnum == end['line'] + 1 ? strlen(strcharpart(line, 0, end['character'])) + 1 : strlen(line) + 1
  485. if colStart == colEnd
  486. continue
  487. endif
  488. call add(pos, [lnum, colStart, colEnd - colStart])
  489. endfor
  490. if !empty(pos)
  491. let opts = s:clear_match_by_window ? {'window': a:winid} : {}
  492. let i = 1
  493. let l = []
  494. for p in pos
  495. call add(l, p)
  496. if i % 8 == 0
  497. let id = matchaddpos(a:hlGroup, l, a:priority, -1, opts)
  498. call add(ids, id)
  499. let l = []
  500. endif
  501. let i += 1
  502. endfor
  503. if !empty(l)
  504. let id = matchaddpos(a:hlGroup, l, a:priority, -1, opts)
  505. call add(ids, id)
  506. endif
  507. endif
  508. endfor
  509. if !s:clear_match_by_window
  510. if has('nvim')
  511. noa call nvim_set_current_win(curr)
  512. else
  513. noa call win_gotoid(curr)
  514. endif
  515. endif
  516. return ids
  517. endfunction
  518. " Clear matches by hlGroup regexp.
  519. function! coc#highlight#clear_match_group(winid, match) abort
  520. let winid = a:winid == 0 ? win_getid() : a:winid
  521. if empty(getwininfo(winid))
  522. " not valid
  523. return
  524. endif
  525. if s:clear_match_by_window
  526. let arr = filter(getmatches(winid), 'v:val["group"] =~# "'.a:match.'"')
  527. for item in arr
  528. call matchdelete(item['id'], winid)
  529. endfor
  530. else
  531. let curr = win_getid()
  532. let switch = exists('*nvim_set_current_win') && curr != winid
  533. if switch
  534. noa call nvim_set_current_win(a:winid)
  535. endif
  536. if win_getid() == winid
  537. let arr = filter(getmatches(), 'v:val["group"] =~# "'.a:match.'"')
  538. for item in arr
  539. call matchdelete(item['id'])
  540. endfor
  541. endif
  542. if switch
  543. noa call nvim_set_current_win(curr)
  544. endif
  545. endif
  546. endfunction
  547. " Clear matches by match ids, use 0 for current win.
  548. function! coc#highlight#clear_matches(winid, ids)
  549. let winid = a:winid == 0 ? win_getid() : a:winid
  550. if empty(getwininfo(winid))
  551. " not valid
  552. return
  553. endif
  554. if s:clear_match_by_window
  555. for id in a:ids
  556. try
  557. call matchdelete(id, winid)
  558. catch /^Vim\%((\a\+)\)\=:E803/
  559. " ignore
  560. endtry
  561. endfor
  562. else
  563. let curr = win_getid()
  564. let switch = exists('*nvim_set_current_win') && curr != winid
  565. if switch
  566. noa call nvim_set_current_win(a:winid)
  567. endif
  568. if win_getid() == winid
  569. for id in a:ids
  570. try
  571. call matchdelete(id)
  572. catch /^Vim\%((\a\+)\)\=:E803/
  573. " ignore
  574. endtry
  575. endfor
  576. endif
  577. if switch
  578. noa call nvim_set_current_win(curr)
  579. endif
  580. endif
  581. endfunction
  582. function! coc#highlight#clear_all() abort
  583. for src_id in values(s:namespace_map)
  584. for bufnr in map(getbufinfo({'bufloaded': 1}), 'v:val["bufnr"]')
  585. if has('nvim')
  586. call nvim_buf_clear_namespace(bufnr, src_id, 0, -1)
  587. else
  588. call coc#api#exec('buf_clear_namespace', [bufnr, src_id, 0, -1])
  589. endif
  590. endfor
  591. endfor
  592. endfunction
  593. function! coc#highlight#create_namespace(key) abort
  594. if type(a:key) == 0
  595. return a:key
  596. endif
  597. if has_key(s:namespace_map, a:key)
  598. return s:namespace_map[a:key]
  599. endif
  600. if has('nvim')
  601. let s:namespace_map[a:key] = nvim_create_namespace('coc-'.a:key)
  602. else
  603. let s:namespace_map[a:key] = s:ns_id
  604. let s:ns_id = s:ns_id + 1
  605. endif
  606. return s:namespace_map[a:key]
  607. endfunction
  608. function! coc#highlight#get_syntax_name(lnum, col)
  609. return synIDattr(synIDtrans(synID(a:lnum,a:col,1)),"name")
  610. endfunction
  611. function! s:prop_type_hlgroup(type) abort
  612. return substitute(a:type, '_\d\+$', '', '')
  613. endfunction
  614. function! s:update_highlights_timer(bufnr, changedtick, key, priority, groups, idx) abort
  615. if getbufvar(a:bufnr, 'changedtick', 0) != a:changedtick
  616. return
  617. endif
  618. let group = get(a:groups, a:idx, v:null)
  619. if empty(group)
  620. return
  621. endif
  622. if empty(group['highlights'])
  623. call coc#highlight#clear_highlight(a:bufnr, a:key, group['start'], group['end'])
  624. else
  625. call coc#highlight#update_highlights(a:bufnr, a:key, group['highlights'], group['start'], group['end'], a:priority)
  626. endif
  627. if a:idx < len(a:groups) - 1
  628. call timer_start(50, { -> s:update_highlights_timer(a:bufnr, a:changedtick, a:key, a:priority, a:groups, a:idx + 1)})
  629. endif
  630. endfunction
  631. function! s:add_highlights_timer(bufnr, ns, highlights, priority) abort
  632. let hls = []
  633. let next = []
  634. for i in range(0, len(a:highlights) - 1)
  635. if i < g:coc_highlight_maximum_count
  636. call add(hls, a:highlights[i])
  637. else
  638. call add(next, a:highlights[i])
  639. endif
  640. endfor
  641. call s:add_highlights(a:bufnr, a:ns, hls, a:priority)
  642. if len(next)
  643. call timer_start(30, {->s:add_highlights_timer(a:bufnr, a:ns, next, a:priority)})
  644. endif
  645. endfunction
  646. function! s:add_highlights(bufnr, ns, highlights, priority) abort
  647. for item in a:highlights
  648. let opts = {
  649. \ 'priority': a:priority,
  650. \ 'combine': get(item, 4, 1) ? 1 : 0,
  651. \ 'start_incl': get(item, 5, 0) ? 1 : 0,
  652. \ 'end_incl': get(item, 6, 0) ? 1 : 0,
  653. \ }
  654. call coc#highlight#add_highlight(a:bufnr, a:ns, item[0], item[1], item[2], item[3], opts)
  655. endfor
  656. endfunction
  657. function! s:to_group(items) abort
  658. let res = []
  659. let before = v:null
  660. for item in a:items
  661. if empty(before) || before[0] != item[1]
  662. let before = [item[1], [item]]
  663. call add(res, before)
  664. else
  665. call add(before[1], item)
  666. endif
  667. endfor
  668. return res
  669. endfunction
  670. function! s:get_priority(key, hlGroup, priority) abort
  671. if a:hlGroup ==# 'CocSearch'
  672. return 999
  673. endif
  674. if strpart(a:key, 0, 10) !=# 'diagnostic'
  675. return a:priority
  676. endif
  677. return a:priority - index(s:diagnostic_hlgroups, a:hlGroup)
  678. endfunction
  679. function! s:group_hls(hls, linecount) abort
  680. " start, end, highlights
  681. let groups = []
  682. if empty(a:hls)
  683. call add(groups, {'start': 0, 'end': a:linecount, 'highlights': []})
  684. return groups
  685. endif
  686. let start = 0
  687. let highlights = []
  688. let lastLnum = -1
  689. for item in a:hls
  690. let lnum = item['lnum']
  691. if lnum >= a:linecount
  692. break
  693. endif
  694. if len(highlights) < g:coc_highlight_maximum_count || lnum == lastLnum
  695. call add(highlights, item)
  696. let lastLnum = lnum
  697. else
  698. call add(groups, {'start': start, 'end': lastLnum + 1, 'highlights': highlights})
  699. let highlights = []
  700. let start = lastLnum + 1
  701. call add(highlights, item)
  702. let lastLnum = lnum
  703. endif
  704. endfor
  705. call add(groups, {'start': start, 'end': a:linecount, 'highlights': highlights})
  706. return groups
  707. endfunction