1" Vim completion script 2" Language: All languages, uses existing syntax highlighting rules 3" Maintainer: David Fishburn <[email protected]> 4" Version: 2.0 5" Last Change: Fri May 05 2006 10:34:57 PM 6" Usage: For detailed help, ":help ft-syntax-omni" 7 8" Set completion with CTRL-X CTRL-O to autoloaded function. 9" This check is in place in case this script is 10" sourced directly instead of using the autoload feature. 11if exists('+omnifunc') 12 " Do not set the option if already set since this 13 " results in an E117 warning. 14 if &omnifunc == "" 15 setlocal omnifunc=syntaxcomplete#Complete 16 endif 17endif 18 19if exists('g:loaded_syntax_completion') 20 finish 21endif 22let g:loaded_syntax_completion = 20 23 24" Set ignorecase to the ftplugin standard 25if !exists('g:omni_syntax_ignorecase') 26 let g:omni_syntax_ignorecase = &ignorecase 27endif 28 29" This script will build a completion list based on the syntax 30" elements defined by the files in $VIMRUNTIME/syntax. 31let s:syn_remove_words = 'match,matchgroup=,contains,'. 32 \ 'links to,start=,end=,nextgroup=' 33 34let s:cache_name = [] 35let s:cache_list = [] 36let s:prepended = '' 37 38" This function is used for the 'omnifunc' option. 39function! syntaxcomplete#Complete(findstart, base) 40 41 if a:findstart 42 " Locate the start of the item, including "." 43 let line = getline('.') 44 let start = col('.') - 1 45 let lastword = -1 46 while start > 0 47 if line[start - 1] =~ '\w' 48 let start -= 1 49 elseif line[start - 1] =~ '\.' 50 " The user must be specifying a column name 51 if lastword == -1 52 let lastword = start 53 endif 54 let start -= 1 55 let b:sql_compl_type = 'column' 56 else 57 break 58 endif 59 endwhile 60 61 " Return the column of the last word, which is going to be changed. 62 " Remember the text that comes before it in s:prepended. 63 if lastword == -1 64 let s:prepended = '' 65 return start 66 endif 67 let s:prepended = strpart(line, start, lastword - start) 68 return lastword 69 endif 70 71 let base = s:prepended . a:base 72 73 let filetype = substitute(&filetype, '\.', '_', 'g') 74 let list_idx = index(s:cache_name, filetype, 0, &ignorecase) 75 if list_idx > -1 76 let compl_list = s:cache_list[list_idx] 77 else 78 let compl_list = OmniSyntaxList() 79 let s:cache_name = add( s:cache_name, filetype ) 80 let s:cache_list = add( s:cache_list, compl_list ) 81 endif 82 83 " Return list of matches. 84 85 if base =~ '\w' 86 let compstr = join(compl_list, ' ') 87 let expr = (g:omni_syntax_ignorecase==0?'\C':'').'\<\%('.base.'\)\@!\w\+\s*' 88 let compstr = substitute(compstr, expr, '', 'g') 89 let compl_list = split(compstr, '\s\+') 90 endif 91 92 return compl_list 93endfunc 94 95function! OmniSyntaxList() 96 " Default to returning a dictionary, if use_dictionary is set to 0 97 " a list will be returned. 98 " let use_dictionary = 1 99 " if a:0 > 0 && a:1 != '' 100 " let use_dictionary = a:1 101 " endif 102 103 let saveL = @l 104 105 " Loop through all the syntax groupnames, and build a 106 " syntax file which contains these names. This can 107 " work generically for any filetype that does not already 108 " have a plugin defined. 109 " This ASSUMES the syntax groupname BEGINS with the name 110 " of the filetype. From my casual viewing of the vim7\syntax 111 " directory. 112 redir @l 113 silent! exec 'syntax list ' 114 redir END 115 116 let syntax_full = "\n".@l 117 let @l = saveL 118 119 if syntax_full =~ 'E28' 120 \ || syntax_full =~ 'E411' 121 \ || syntax_full =~ 'E415' 122 \ || syntax_full =~ 'No Syntax items' 123 return [] 124 endif 125 126 let filetype = substitute(&filetype, '\.', '_', 'g') 127 128 " Default the include group to include the requested syntax group 129 let syntax_group_include_{filetype} = '' 130 " Check if there are any overrides specified for this filetype 131 if exists('g:omni_syntax_group_include_'.filetype) 132 let syntax_group_include_{filetype} = 133 \ substitute( g:omni_syntax_group_include_{filetype},'\s\+','','g') 134 if syntax_group_include_{filetype} =~ '\w' 135 let syntax_group_include_{filetype} = 136 \ substitute( syntax_group_include_{filetype}, 137 \ '\s*,\s*', '\\|', 'g' 138 \ ) 139 endif 140 endif 141 142 " Default the exclude group to nothing 143 let syntax_group_exclude_{filetype} = '' 144 " Check if there are any overrides specified for this filetype 145 if exists('g:omni_syntax_group_exclude_'.filetype) 146 let syntax_group_exclude_{filetype} = 147 \ substitute( g:omni_syntax_group_exclude_{filetype},'\s\+','','g') 148 if syntax_group_exclude_{filetype} =~ '\w' 149 let syntax_group_exclude_{filetype} = 150 \ substitute( syntax_group_exclude_{filetype}, 151 \ '\s*,\s*', '\\|', 'g' 152 \ ) 153 endif 154 endif 155 156 " Sometimes filetypes can be composite names, like c.doxygen 157 " Loop through each individual part looking for the syntax 158 " items specific to each individual filetype. 159 let syn_list = '' 160 let ftindex = 0 161 let ftindex = match(&filetype, '\w\+', ftindex) 162 163 while ftindex > -1 164 let ft_part_name = matchstr( &filetype, '\w\+', ftindex ) 165 166 " Syntax rules can contain items for more than just the current 167 " filetype. They can contain additional items added by the user 168 " via autocmds or their vimrc. 169 " Some syntax files can be combined (html, php, jsp). 170 " We want only items that begin with the filetype we are interested in. 171 let next_group_regex = '\n' . 172 \ '\zs'.ft_part_name.'\w\+\ze'. 173 \ '\s\+xxx\s\+' 174 let index = 0 175 let index = match(syntax_full, next_group_regex, index) 176 177 while index > -1 178 let group_name = matchstr( syntax_full, '\w\+', index ) 179 180 let get_syn_list = 1 181 " if syntax_group_include_{&filetype} == '' 182 " if syntax_group_exclude_{&filetype} != '' 183 " if '\<'.syntax_group_exclude_{&filetype}.'\>' =~ '\<'.group_name.'\>' 184 " let get_syn_list = 0 185 " endif 186 " endif 187 " else 188 " if '\<'.syntax_group_include_{&filetype}.'\>' !~ '\<'.group_name.'\>' 189 " let get_syn_list = 0 190 " endif 191 " endif 192 if syntax_group_exclude_{filetype} != '' 193 if '\<'.syntax_group_exclude_{filetype}.'\>' =~ '\<'.group_name.'\>' 194 let get_syn_list = 0 195 endif 196 endif 197 198 if get_syn_list == 1 199 if syntax_group_include_{filetype} != '' 200 if '\<'.syntax_group_include_{filetype}.'\>' !~ '\<'.group_name.'\>' 201 let get_syn_list = 0 202 endif 203 endif 204 endif 205 206 if get_syn_list == 1 207 " Pass in the full syntax listing, plus the group name we 208 " are interested in. 209 let extra_syn_list = s:SyntaxCSyntaxGroupItems(group_name, syntax_full) 210 211 " if !empty(extra_syn_list) 212 " for elem in extra_syn_list 213 " let item = {'word':elem, 'kind':'t', 'info':group_name} 214 " let compl_list += [item] 215 " endfor 216 " endif 217 218 let syn_list = syn_list . extra_syn_list . "\n" 219 endif 220 221 let index = index + strlen(group_name) 222 let index = match(syntax_full, next_group_regex, index) 223 endwhile 224 225 let ftindex = ftindex + len(ft_part_name) 226 let ftindex = match( &filetype, '\w\+', ftindex ) 227 endwhile 228 229 " Convert the string to a List and sort it. 230 let compl_list = sort(split(syn_list)) 231 232 if &filetype == 'vim' 233 let short_compl_list = [] 234 for i in range(len(compl_list)) 235 if i == len(compl_list)-1 236 let next = i 237 else 238 let next = i + 1 239 endif 240 if compl_list[next] !~ '^'.compl_list[i].'.$' 241 let short_compl_list += [compl_list[i]] 242 endif 243 endfor 244 245 return short_compl_list 246 else 247 return compl_list 248 endif 249endfunction 250 251function! s:SyntaxCSyntaxGroupItems( group_name, syntax_full ) 252 253 let syn_list = "" 254 255 " From the full syntax listing, strip out the portion for the 256 " request group. 257 " Query: 258 " \n - must begin with a newline 259 " a:group_name - the group name we are interested in 260 " \s\+xxx\s\+ - group names are always followed by xxx 261 " \zs - start the match 262 " .\{-} - everything ... 263 " \ze - end the match 264 " \n\w - at the first newline starting with a character 265 let syntax_group = matchstr(a:syntax_full, 266 \ "\n".a:group_name.'\s\+xxx\s\+\zs.\{-}\ze'."\n".'\w' 267 \ ) 268 269 if syntax_group != "" 270 " let syn_list = substitute( @l, '^.*xxx\s*\%(contained\s*\)\?', "", '' ) 271 " let syn_list = substitute( @l, '^.*xxx\s*', "", '' ) 272 273 " We only want the words for the lines begining with 274 " containedin, but there could be other items. 275 276 " Tried to remove all lines that do not begin with contained 277 " but this does not work in all cases since you can have 278 " contained nextgroup=... 279 " So this will strip off the ending of lines with known 280 " keywords. 281 let syn_list = substitute( 282 \ syntax_group, '\<\('. 283 \ substitute( 284 \ escape(s:syn_remove_words, '\\/.*$^~[]') 285 \ , ',', '\\|', 'g' 286 \ ). 287 \ '\).\{-}\%($\|'."\n".'\)' 288 \ , "\n", 'g' 289 \ ) 290 291 " Now strip off the newline + blank space + contained 292 let syn_list = substitute( 293 \ syn_list, '\%(^\|\n\)\@<=\s*\<\(contained\)' 294 \ , "", 'g' 295 \ ) 296 297 " There are a number of items which have non-word characters in 298 " them, *'T_F1'*. vim.vim is one such file. 299 " This will replace non-word characters with spaces. 300 let syn_list = substitute( syn_list, '[^0-9A-Za-z_ ]', ' ', 'g' ) 301 else 302 let syn_list = '' 303 endif 304 305 return syn_list 306endfunction 307 308