neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

syntaxcomplete.vim (33651B)


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