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