1" Vim completion script 2" Language: All languages, uses existing syntax highlighting rules 3" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> 4" Version: 14.0 5" Last Change: 2020 Dec 30 6" Usage: For detailed help, ":help ft-syntax-omni" 7 8" History 9" 10" Version 14.0 11" - Fixed issue with single quotes and is_keyword 12" https://github.com/vim/vim/issues/7463 13" 14" Version 13.0 15" - Extended the option omni_syntax_group_include_{filetype} 16" to accept a comma separated list of regex's rather than 17" string. For example, for the javascript filetype you could 18" use: 19" let g:omni_syntax_group_include_javascript = 'javascript\w\+,jquery\w\+' 20" - Some syntax files (perl.vim) use the match // syntax as a mechanism 21" to identify keywords. This update attempts to parse the 22" match syntax and pull out syntax items which are at least 23" 3 words or more. 24" 25" Version 12.0 26" - It is possible to have '-' as part of iskeyword, when 27" checking for character ranges, tighten up the regex. 28" E688: More targets than List items. 29" 30" Version 11.0 31" - Corrected which characters required escaping during 32" substitution calls. 33" 34" Version 10.0 35" - Cycle through all the character ranges specified in the 36" iskeyword option and build a list of valid word separators. 37" Prior to this change, only actual characters were used, 38" where for example ASCII "45" == "-". If "45" were used 39" in iskeyword the hyphen would not be picked up. 40" This introduces a new option, since the character ranges 41" specified could be multibyte: 42" let g:omni_syntax_use_single_byte = 1 43" - This by default will only allow single byte ASCII 44" characters to be added and an additional check to ensure 45" the charater is printable (see documentation for isprint). 46" 47" Version 9.0 48" - Add the check for cpo. 49" 50" Version 8.0 51" - Updated SyntaxCSyntaxGroupItems() 52" - Some additional syntax items were also allowed 53" on nextgroup= lines which were ignored by default. 54" Now these lines are processed independently. 55" 56" Version 7.0 57" - Updated syntaxcomplete#OmniSyntaxList() 58" - Looking up the syntax groups defined from a syntax file 59" looked for only 1 format of {filetype}GroupName, but some 60" syntax writers use this format as well: 61" {b:current_syntax}GroupName 62" - OmniSyntaxList() will now check for both if the first 63" method does not find a match. 64" 65" Version 6.0 66" - Added syntaxcomplete#OmniSyntaxList() 67" - Allows other plugins to use this for their own 68" purposes. 69" - It will return a List of all syntax items for the 70" syntax group name passed in. 71" - XPTemplate for SQL will use this function via the 72" sqlcomplete plugin to populate a Choose box. 73" 74" Version 5.0 75" - Updated SyntaxCSyntaxGroupItems() 76" - When processing a list of syntax groups, the final group 77" was missed in function SyntaxCSyntaxGroupItems. 78" 79" Set completion with CTRL-X CTRL-O to autoloaded function. 80" This check is in place in case this script is 81" sourced directly instead of using the autoload feature. 82if exists('+omnifunc') 83 " Do not set the option if already set since this 84 " results in an E117 warning. 85 if &omnifunc == "" 86 setlocal omnifunc=syntaxcomplete#Complete 87 endif 88endif 89 90if exists('g:loaded_syntax_completion') 91 finish 92endif 93let g:loaded_syntax_completion = 130 94 95" Turn on support for line continuations when creating the script 96let s:cpo_save = &cpo 97set cpo&vim 98 99" Set ignorecase to the ftplugin standard 100" This is the default setting, but if you define a buffer local 101" variable you can override this on a per filetype. 102if !exists('g:omni_syntax_ignorecase') 103 let g:omni_syntax_ignorecase = &ignorecase 104endif 105 106" Indicates whether we should use the iskeyword option to determine 107" how to split words. 108" This is the default setting, but if you define a buffer local 109" variable you can override this on a per filetype. 110if !exists('g:omni_syntax_use_iskeyword') 111 let g:omni_syntax_use_iskeyword = 1 112endif 113 114" When using iskeyword, this setting controls whether the characters 115" should be limited to single byte characters. 116if !exists('g:omni_syntax_use_single_byte') 117 let g:omni_syntax_use_single_byte = 1 118endif 119 120" When using iskeyword, this setting controls whether the characters 121" should be limited to single byte characters. 122if !exists('g:omni_syntax_use_iskeyword_numeric') 123 let g:omni_syntax_use_iskeyword_numeric = 1 124endif 125 126" Only display items in the completion window that are at least 127" this many characters in length. 128" This is the default setting, but if you define a buffer local 129" variable you can override this on a per filetype. 130if !exists('g:omni_syntax_minimum_length') 131 let g:omni_syntax_minimum_length = 0 132endif 133 134" This script will build a completion list based on the syntax 135" elements defined by the files in $VIMRUNTIME/syntax. 136" let s:syn_remove_words = 'match,matchgroup=,contains,'. 137let s:syn_remove_words = 'matchgroup=,contains,'. 138 \ 'links to,start=,end=' 139 " \ 'links to,start=,end=,nextgroup=' 140 141let s:cache_name = [] 142let s:cache_list = [] 143let s:prepended = '' 144 145" This function is used for the 'omnifunc' option. 146function! syntaxcomplete#Complete(findstart, base) 147 148 " Only display items in the completion window that are at least 149 " this many characters in length 150 if !exists('b:omni_syntax_ignorecase') 151 if exists('g:omni_syntax_ignorecase') 152 let b:omni_syntax_ignorecase = g:omni_syntax_ignorecase 153 else 154 let b:omni_syntax_ignorecase = &ignorecase 155 endif 156 endif 157 158 if a:findstart 159 " Locate the start of the item, including "." 160 let line = getline('.') 161 let start = col('.') - 1 162 let lastword = -1 163 while start > 0 164 " if line[start - 1] =~ '\S' 165 " let start -= 1 166 " elseif line[start - 1] =~ '\.' 167 if line[start - 1] =~ '\k' 168 let start -= 1 169 let lastword = a:findstart 170 else 171 break 172 endif 173 endwhile 174 175 " Return the column of the last word, which is going to be changed. 176 " Remember the text that comes before it in s:prepended. 177 if lastword == -1 178 let s:prepended = '' 179 return start 180 endif 181 let s:prepended = strpart(line, start, (col('.') - 1) - start) 182 return start 183 endif 184 185 " let base = s:prepended . a:base 186 " let base = s:prepended 187 let base = substitute(s:prepended, "'", "''", 'g') 188 189 let filetype = substitute(&filetype, '\.', '_', 'g') 190 let list_idx = index(s:cache_name, filetype, 0, &ignorecase) 191 if list_idx > -1 192 let compl_list = s:cache_list[list_idx] 193 else 194 let compl_list = OmniSyntaxList() 195 let s:cache_name = add( s:cache_name, filetype ) 196 let s:cache_list = add( s:cache_list, compl_list ) 197 endif 198 199 " Return list of matches. 200 201 if base != '' 202 " let compstr = join(compl_list, ' ') 203 " let expr = (b:omni_syntax_ignorecase==0?'\C':'').'\<\%('.base.'\)\@!\w\+\s*' 204 " let compstr = substitute(compstr, expr, '', 'g') 205 " let compl_list = split(compstr, '\s\+') 206 207 " Filter the list based on the first few characters the user 208 " entered 209 let expr = 'v:val '.(g:omni_syntax_ignorecase==1?'=~?':'=~#')." '^".escape(base, '\\/.*$^~[]').".*'" 210 let compl_list = filter(deepcopy(compl_list), expr) 211 endif 212 213 return compl_list 214endfunc 215 216function! syntaxcomplete#OmniSyntaxList(...) 217 if a:0 > 0 218 let parms = [] 219 if 3 == type(a:1) 220 let parms = a:1 221 elseif 1 == type(a:1) 222 let parms = split(a:1, ',') 223 endif 224 return OmniSyntaxList( parms ) 225 else 226 return OmniSyntaxList() 227 endif 228endfunc 229 230function! OmniSyntaxList(...) 231 let list_parms = [] 232 if a:0 > 0 233 if 3 == type(a:1) 234 let list_parms = a:1 235 elseif 1 == type(a:1) 236 let list_parms = split(a:1, ',') 237 endif 238 endif 239 240 " Default to returning a dictionary, if use_dictionary is set to 0 241 " a list will be returned. 242 " let use_dictionary = 1 243 " if a:0 > 0 && a:1 != '' 244 " let use_dictionary = a:1 245 " endif 246 247 " Only display items in the completion window that are at least 248 " this many characters in length 249 if !exists('b:omni_syntax_use_iskeyword') 250 if exists('g:omni_syntax_use_iskeyword') 251 let b:omni_syntax_use_iskeyword = g:omni_syntax_use_iskeyword 252 else 253 let b:omni_syntax_use_iskeyword = 1 254 endif 255 endif 256 257 " Only display items in the completion window that are at least 258 " this many characters in length 259 if !exists('b:omni_syntax_minimum_length') 260 if exists('g:omni_syntax_minimum_length') 261 let b:omni_syntax_minimum_length = g:omni_syntax_minimum_length 262 else 263 let b:omni_syntax_minimum_length = 0 264 endif 265 endif 266 267 let saveL = @l 268 let filetype = substitute(&filetype, '\.', '_', 'g') 269 270 if empty(list_parms) 271 " Default the include group to include the requested syntax group 272 let syntax_group_include_{filetype} = '' 273 " Check if there are any overrides specified for this filetype 274 if exists('g:omni_syntax_group_include_'.filetype) 275 let syntax_group_include_{filetype} = 276 \ substitute( g:omni_syntax_group_include_{filetype},'\s\+','','g') 277 let list_parms = split(g:omni_syntax_group_include_{filetype}, ',') 278 if syntax_group_include_{filetype} =~ '\w' 279 let syntax_group_include_{filetype} = 280 \ substitute( syntax_group_include_{filetype}, 281 \ '\s*,\s*', '\\|', 'g' 282 \ ) 283 endif 284 endif 285 else 286 " A specific list was provided, use it 287 endif 288 289 " Loop through all the syntax groupnames, and build a 290 " syntax file which contains these names. This can 291 " work generically for any filetype that does not already 292 " have a plugin defined. 293 " This ASSUMES the syntax groupname BEGINS with the name 294 " of the filetype. From my casual viewing of the vim7\syntax 295 " directory this is true for almost all syntax definitions. 296 " As an example, the SQL syntax groups have this pattern: 297 " sqlType 298 " sqlOperators 299 " sqlKeyword ... 300 if !empty(list_parms) && empty(substitute(join(list_parms), '[a-zA-Z ]', '', 'g')) 301 " If list_parms only includes word characters, use it to limit 302 " the syntax elements. 303 " If using regex syntax list will fail to find those items, so 304 " simply grab the who syntax list. 305 redir @l 306 silent! exec 'syntax list '.join(list_parms) 307 redir END 308 else 309 redir @l 310 silent! exec 'syntax list' 311 redir END 312 endif 313 314 let syntax_full = "\n".@l 315 let @l = saveL 316 317 if syntax_full =~ 'E28' 318 \ || syntax_full =~ 'E411' 319 \ || syntax_full =~ 'E415' 320 \ || syntax_full =~ 'No Syntax items' 321 return [] 322 endif 323 324 let filetype = substitute(&filetype, '\.', '_', 'g') 325 326 let list_exclude_groups = [] 327 if a:0 > 0 328 " Do nothing since we have specific a specific list of groups 329 else 330 " Default the exclude group to nothing 331 let syntax_group_exclude_{filetype} = '' 332 " Check if there are any overrides specified for this filetype 333 if exists('g:omni_syntax_group_exclude_'.filetype) 334 let syntax_group_exclude_{filetype} = 335 \ substitute( g:omni_syntax_group_exclude_{filetype},'\s\+','','g') 336 let list_exclude_groups = split(g:omni_syntax_group_exclude_{filetype}, ',') 337 if syntax_group_exclude_{filetype} =~ '\w' 338 let syntax_group_exclude_{filetype} = 339 \ substitute( syntax_group_exclude_{filetype}, 340 \ '\s*,\s*', '\\|', 'g' 341 \ ) 342 endif 343 endif 344 endif 345 346 if empty(list_parms) 347 let list_parms = [&filetype.'\w\+'] 348 endif 349 350 let syn_list = '' 351 let index = 0 352 for group_regex in list_parms 353 " Sometimes filetypes can be composite names, like c.doxygen 354 " Loop through each individual part looking for the syntax 355 " items specific to each individual filetype. 356 " let ftindex = 0 357 " let ftindex = match(syntax_full, group_regex, ftindex) 358 359 " while ftindex > -1 360 " let ft_part_name = matchstr( syntax_full, '\w\+', ftindex ) 361 362 " Syntax rules can contain items for more than just the current 363 " filetype. They can contain additional items added by the user 364 " via autocmds or their vimrc. 365 " Some syntax files can be combined (html, php, jsp). 366 " We want only items that begin with the filetype we are interested in. 367 let next_group_regex = '\n' . 368 \ '\zs'.group_regex.'\ze'. 369 \ '\s\+xxx\s\+' 370 let index = match(syntax_full, next_group_regex, index) 371 372 " For the matched group name, strip off any of the regex special 373 " characters and see if we get a match with the current syntax 374 if index == -1 && exists('b:current_syntax') && substitute(group_regex, '[^a-zA-Z ]\+.*', '', 'g') !~ '^'.b:current_syntax 375 " There appears to be two standards when writing syntax files. 376 " Either items begin as: 377 " syn keyword {filetype}Keyword values ... 378 " let b:current_syntax = "sql" 379 " let b:current_syntax = "sqlanywhere" 380 " Or 381 " syn keyword {syntax_filename}Keyword values ... 382 " let b:current_syntax = "mysql" 383 " So, we will make the format of finding the syntax group names 384 " a bit more flexible and look for both if the first fails to 385 " find a match. 386 let next_group_regex = '\n' . 387 \ '\zs'.b:current_syntax.'\w\+\ze'. 388 \ '\s\+xxx\s\+' 389 let index = 0 390 let index = match(syntax_full, next_group_regex, index) 391 endif 392 393 while index > -1 394 let group_name = matchstr( syntax_full, '\w\+', index ) 395 396 let get_syn_list = 1 397 for exclude_group_name in list_exclude_groups 398 if '\<'.exclude_group_name.'\>' =~ '\<'.group_name.'\>' 399 let get_syn_list = 0 400 endif 401 endfor 402 403 " This code is no longer needed in version 6.0 since we have 404 " augmented the syntax list command to only retrieve the syntax 405 " groups we are interested in. 406 " 407 " if get_syn_list == 1 408 " if syntax_group_include_{filetype} != '' 409 " if '\<'.syntax_group_include_{filetype}.'\>' !~ '\<'.group_name.'\>' 410 " let get_syn_list = 0 411 " endif 412 " endif 413 " endif 414 415 if get_syn_list == 1 416 " Pass in the full syntax listing, plus the group name we 417 " are interested in. 418 let extra_syn_list = s:SyntaxCSyntaxGroupItems(group_name, syntax_full) 419 let syn_list = syn_list . extra_syn_list . "\n" 420 endif 421 422 let index = index + strlen(group_name) 423 let index = match(syntax_full, next_group_regex, index) 424 endwhile 425 426 " let ftindex = ftindex + len(ft_part_name) 427 " let ftindex = match( syntax_full, group_regex, ftindex ) 428 " endwhile 429 endfor 430 431" " Sometimes filetypes can be composite names, like c.doxygen 432" " Loop through each individual part looking for the syntax 433" " items specific to each individual filetype. 434" let syn_list = '' 435" let ftindex = 0 436" let ftindex = match(&filetype, '\w\+', ftindex) 437 438" while ftindex > -1 439" let ft_part_name = matchstr( &filetype, '\w\+', ftindex ) 440 441" " Syntax rules can contain items for more than just the current 442" " filetype. They can contain additional items added by the user 443" " via autocmds or their vimrc. 444" " Some syntax files can be combined (html, php, jsp). 445" " We want only items that begin with the filetype we are interested in. 446" let next_group_regex = '\n' . 447" \ '\zs'.ft_part_name.'\w\+\ze'. 448" \ '\s\+xxx\s\+' 449" let index = 0 450" let index = match(syntax_full, next_group_regex, index) 451 452" if index == -1 && exists('b:current_syntax') && ft_part_name != b:current_syntax 453" " There appears to be two standards when writing syntax files. 454" " Either items begin as: 455" " syn keyword {filetype}Keyword values ... 456" " let b:current_syntax = "sql" 457" " let b:current_syntax = "sqlanywhere" 458" " Or 459" " syn keyword {syntax_filename}Keyword values ... 460" " let b:current_syntax = "mysql" 461" " So, we will make the format of finding the syntax group names 462" " a bit more flexible and look for both if the first fails to 463" " find a match. 464" let next_group_regex = '\n' . 465" \ '\zs'.b:current_syntax.'\w\+\ze'. 466" \ '\s\+xxx\s\+' 467" let index = 0 468" let index = match(syntax_full, next_group_regex, index) 469" endif 470 471" while index > -1 472" let group_name = matchstr( syntax_full, '\w\+', index ) 473 474" let get_syn_list = 1 475" for exclude_group_name in list_exclude_groups 476" if '\<'.exclude_group_name.'\>' =~ '\<'.group_name.'\>' 477" let get_syn_list = 0 478" endif 479" endfor 480 481" " This code is no longer needed in version 6.0 since we have 482" " augmented the syntax list command to only retrieve the syntax 483" " groups we are interested in. 484" " 485" " if get_syn_list == 1 486" " if syntax_group_include_{filetype} != '' 487" " if '\<'.syntax_group_include_{filetype}.'\>' !~ '\<'.group_name.'\>' 488" " let get_syn_list = 0 489" " endif 490" " endif 491" " endif 492 493" if get_syn_list == 1 494" " Pass in the full syntax listing, plus the group name we 495" " are interested in. 496" let extra_syn_list = s:SyntaxCSyntaxGroupItems(group_name, syntax_full) 497" let syn_list = syn_list . extra_syn_list . "\n" 498" endif 499 500" let index = index + strlen(group_name) 501" let index = match(syntax_full, next_group_regex, index) 502" endwhile 503 504" let ftindex = ftindex + len(ft_part_name) 505" let ftindex = match( &filetype, '\w\+', ftindex ) 506" endwhile 507 508 " Convert the string to a List and sort it. 509 let compl_list = sort(split(syn_list)) 510 511 if &filetype == 'vim' 512 let short_compl_list = [] 513 for i in range(len(compl_list)) 514 if i == len(compl_list)-1 515 let next = i 516 else 517 let next = i + 1 518 endif 519 if compl_list[next] !~ '^'.compl_list[i].'.$' 520 let short_compl_list += [compl_list[i]] 521 endif 522 endfor 523 524 return short_compl_list 525 else 526 return compl_list 527 endif 528endfunction 529 530function! s:SyntaxCSyntaxGroupItems( group_name, syntax_full ) 531 532 let syn_list = "" 533 534 " From the full syntax listing, strip out the portion for the 535 " request group. 536 " Query: 537 " \n - must begin with a newline 538 " a:group_name - the group name we are interested in 539 " \s\+xxx\s\+ - group names are always followed by xxx 540 " \zs - start the match 541 " .\{-} - everything ... 542 " \ze - end the match 543 " \( - start a group or 2 potential matches 544 " \n\w - at the first newline starting with a character 545 " \| - 2nd potential match 546 " \%$ - matches end of the file or string 547 " \) - end a group 548 let syntax_group = matchstr(a:syntax_full, 549 \ "\n".a:group_name.'\s\+xxx\s\+\zs.\{-}\ze\(\n\w\|\%$\)' 550 \ ) 551 552 if syntax_group != "" 553 " let syn_list = substitute( @l, '^.*xxx\s*\%(contained\s*\)\?', "", '' ) 554 " let syn_list = substitute( @l, '^.*xxx\s*', "", '' ) 555 556 " We only want the words for the lines beginning with 557 " containedin, but there could be other items. 558 559 " Tried to remove all lines that do not begin with contained 560 " but this does not work in all cases since you can have 561 " contained nextgroup=... 562 " So this will strip off the ending of lines with known 563 " keywords. 564 let syn_list = substitute( 565 \ syntax_group, '\<\('. 566 \ substitute( 567 \ escape(s:syn_remove_words, '\\/.*$^~[]') 568 \ , ',', '\\|', 'g' 569 \ ). 570 \ '\).\{-}\%($\|'."\n".'\)' 571 \ , "\n", 'g' 572 \ ) 573 574 " Attempt to deal with lines using the match syntax 575 " javaScriptDocTags xxx match /@\(param\|argument\|requires\|file\)\>/ 576 " Though it can use any types of regex, so this plugin will attempt 577 " to restrict it 578 " 1. Only use \( or \%( constructs remove all else 579 " 2 Remove and []s 580 " 3. Account for match //constructs 581 " \%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\? 582 " 4. Hope for the best 583 " 584 " 585 let syn_list_old = syn_list 586 while syn_list =~ '\<match\>\s\+\/' 587 if syn_list =~ 'perlElseIfError' 588 let syn_list = syn_list 589 endif 590 " Check if the match has words at least 3 characters long 591 if syn_list =~ '\<match \/\zs.\{-}\<\w\{3,}\>.\{-}\ze\\\@<!\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+' 592 " Remove everything after / and before the first \( 593 let syn_list = substitute( syn_list, '\<match \/\zs.\{-}\ze\\%\?(.\{-}\\\@<!\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '', 'g' ) 594 " Remove everything after \) and up to the ending / 595 let syn_list = substitute( syn_list, '\<match \/.\{-}\\)\zs.\{-}\ze\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '', 'g' ) 596 597 " Remove any character classes 598 " let syn_list = substitute( syn_list, '\<match /\zs.\{-}\[[^]]*\].\{-}\ze\/ ', '', 'g' ) 599 let syn_list = substitute( syn_list, '\%(\<match \/[^/]\{-}\)\@<=\[[^]]*\]\ze.\{-}\\\@<!\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?', '', 'g' ) 600 " Remove any words < 3 characters 601 let syn_list = substitute( syn_list, '\%(\<match \/[^/]\{-}\)\@<=\<\w\{1,2}\>\ze.\{-}\\\@<!\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '', 'g' ) 602 " Remove all non-word characters 603 " let syn_list = substitute( syn_list, '\<match /\zs.\{-}\<\W\+\>.\{-}\ze\/ ', "", 'g' ) 604 " let syn_list = substitute( syn_list, '\%(\<match \/[^/]\{-}\)\@<=\W\+\ze.\{-}\/ ', ' ', 'g' ) 605 " Do this by using the outer substitute() call to gather all 606 " text between the match /.../ tags. 607 " The inner substitute() call operates on the text selected 608 " and replaces all non-word characters. 609 let syn_list = substitute( syn_list, '\<match \/\zs\(.\{-}\)\ze\\\@<!\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+' 610 \ , '\=substitute(submatch(1), "\\W\\+", " ", "g")' 611 \ , 'g' ) 612 " Remove the match / / syntax 613 let syn_list = substitute( syn_list, '\<match \/\(.\{-}\)\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '\1', 'g' ) 614 else 615 " No words long enough, remove the match 616 " Remove the match syntax 617 " let syn_list = substitute( syn_list, '\<match \/[^\/]*\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '', 'g' ) 618 let syn_list = substitute( syn_list, '\<match \/\%(.\{-}\)\?\/\%(\%(ms\|me\|hs\|he\|rs\|re\|lc\)\S\+\)\?\s\+', '', 'g' ) 619 endif 620 if syn_list =~ '\<match\>\s\+\/' 621 " Problem removing the match / / tags 622 let syn_list = '' 623 endif 624 endwhile 625 626 627 " Now strip off the newline + blank space + contained. 628 " Also include lines with nextgroup=@someName skip_key_words syntax_element 629 " \ syn_list, '\%(^\|\n\)\@<=\s*\<\(contained\|nextgroup=\)' 630 " \ syn_list, '\%(^\|\n\)\@<=\s*\<\(contained\|nextgroup=[@a-zA-Z,]*\)' 631 let syn_list = substitute( 632 \ syn_list, '\<\(contained\|nextgroup=[@a-zA-Z,]*\)' 633 \ , "", 'g' 634 \ ) 635 636 " This can leave lines like this 637 " =@vimMenuList skipwhite onoremenu 638 " Strip the special option keywords first 639 " :h :syn-skipwhite* 640 let syn_list = substitute( 641 \ syn_list, '\<\(skipwhite\|skipnl\|skipempty\)\>' 642 \ , "", 'g' 643 \ ) 644 645 " Now remove the remainder of the nextgroup=@someName lines 646 let syn_list = substitute( 647 \ syn_list, '\%(^\|\n\)\@<=\s*\(@\w\+\)' 648 \ , "", 'g' 649 \ ) 650 651 if b:omni_syntax_use_iskeyword == 0 652 " There are a number of items which have non-word characters in 653 " them, *'T_F1'*. vim.vim is one such file. 654 " This will replace non-word characters with spaces. 655 let syn_list = substitute( syn_list, '[^0-9A-Za-z_ ]', ' ', 'g' ) 656 else 657 if g:omni_syntax_use_iskeyword_numeric == 1 658 " iskeyword can contain value like this 659 " 38,42,43,45,47-58,60-62,64-90,97-122,_,+,-,*,/,%,<,=,>,:,$,?,!,@-@,94 660 " Numeric values convert to their ASCII equivalent using the 661 " nr2char() function. 662 " & 38 663 " * 42 664 " + 43 665 " - 45 666 " ^ 94 667 " Iterate through all numeric specifications and convert those 668 " to their ascii equivalent ensuring the character is printable. 669 " If so, add it to the list. 670 let accepted_chars = '' 671 for item in split(&iskeyword, ',') 672 if item =~ '\d-\d' 673 " This is a character range (ie 47-58), 674 " cycle through each character within the range 675 let [b:start, b:end] = split(item, '-') 676 for range_item in range( b:start, b:end ) 677 if range_item <= 127 || g:omni_syntax_use_single_byte == 0 678 if nr2char(range_item) =~ '\p' 679 let accepted_chars = accepted_chars . nr2char(range_item) 680 endif 681 endif 682 endfor 683 elseif item =~ '^\d\+$' 684 " Only numeric, translate to a character 685 if item < 127 || g:omni_syntax_use_single_byte == 0 686 if nr2char(item) =~ '\p' 687 let accepted_chars = accepted_chars . nr2char(item) 688 endif 689 endif 690 else 691 if char2nr(item) < 127 || g:omni_syntax_use_single_byte == 0 692 if item =~ '\p' 693 let accepted_chars = accepted_chars . item 694 endif 695 endif 696 endif 697 endfor 698 " Escape special regex characters 699 " Looks like the wrong chars are escaped. In a collection, 700 " :h /[] 701 " only `]', `\', `-' and `^' are special: 702 " let accepted_chars = escape(accepted_chars, '\\/.*$^~[]' ) 703 let accepted_chars = escape(accepted_chars, ']\-^' ) 704 " Remove all characters that are not acceptable 705 let syn_list = substitute( syn_list, '[^A-Za-z'.accepted_chars.']', ' ', 'g' ) 706 else 707 let accept_chars = ','.&iskeyword.',' 708 " Remove all character ranges 709 " let accept_chars = substitute(accept_chars, ',[^,]\+-[^,]\+,', ',', 'g') 710 let accept_chars = substitute(accept_chars, ',\@<=[^,]\+-[^,]\+,', '', 'g') 711 " Remove all numeric specifications 712 " let accept_chars = substitute(accept_chars, ',\d\{-},', ',', 'g') 713 let accept_chars = substitute(accept_chars, ',\@<=\d\{-},', '', 'g') 714 " Remove all commas 715 let accept_chars = substitute(accept_chars, ',', '', 'g') 716 " Escape special regex characters 717 " Looks like the wrong chars are escaped. In a collection, 718 " :h /[] 719 " only `]', `\', `-' and `^' are special: 720 " let accept_chars = escape(accept_chars, '\\/.*$^~[]' ) 721 let accept_chars = escape(accept_chars, ']\-^' ) 722 " Remove all characters that are not acceptable 723 let syn_list = substitute( syn_list, '[^0-9A-Za-z_'.accept_chars.']', ' ', 'g' ) 724 endif 725 endif 726 727 if b:omni_syntax_minimum_length > 0 728 " If the user specified a minimum length, enforce it 729 let syn_list = substitute(' '.syn_list.' ', ' \S\{,'.b:omni_syntax_minimum_length.'}\ze ', ' ', 'g') 730 endif 731 else 732 let syn_list = '' 733 endif 734 735 return syn_list 736endfunction 737 738function! OmniSyntaxShowChars(spec) 739 let result = [] 740 for item in split(a:spec, ',') 741 if len(item) > 1 742 if item == '@-@' 743 call add(result, char2nr(item)) 744 else 745 call extend(result, call('range', split(item, '-'))) 746 endif 747 else 748 if item == '@' " assume this is [A-Za-z] 749 for [c1, c2] in [['A', 'Z'], ['a', 'z']] 750 call extend(result, range(char2nr(c1), char2nr(c2))) 751 endfor 752 else 753 call add(result, char2nr(item)) 754 endif 755 endif 756 endfor 757 return join(map(result, 'nr2char(v:val)'), ', ') 758endfunction 759let &cpo = s:cpo_save 760unlet s:cpo_save 761