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