xref: /vim-8.2.3635/runtime/ftplugin/sql.vim (revision f193fffd)
1" SQL filetype plugin file
2" Language:    SQL (Common for Oracle, Microsoft SQL Server, Sybase)
3" Version:     3.0
4" Maintainer:  David Fishburn <fishburn at ianywhere dot com>
5" Last Change: Wed Apr 26 2006 3:02:32 PM
6" Download:    http://vim.sourceforge.net/script.php?script_id=454
7
8" For more details please use:
9"        :h sql.txt
10"
11" This file should only contain values that are common to all SQL languages
12" Oracle, Microsoft SQL Server, Sybase ASA/ASE, MySQL, and so on
13" If additional features are required create:
14"        vimfiles/after/ftplugin/sql.vim (Windows)
15"        .vim/after/ftplugin/sql.vim     (Unix)
16" to override and add any of your own settings.
17
18
19" This file also creates a command, SQLSetType, which allows you to change
20" SQL dialects on the fly.  For example, if I open an Oracle SQL file, it
21" is color highlighted appropriately.  If I open an Informix SQL file, it
22" will still be highlighted according to Oracles settings.  By running:
23"     :SQLSetType sqlinformix
24"
25" All files called sqlinformix.vim will be loaded from the indent and syntax
26" directories.  This allows you to easily flip SQL dialects on a per file
27" basis.  NOTE: you can also use completion:
28"     :SQLSetType <tab>
29"
30" To change the default dialect, add the following to your vimrc:
31"    let g:sql_type_default = 'sqlanywhere'
32
33
34" Only do this when not done yet for this buffer
35if exists("b:did_ftplugin")
36  finish
37endif
38
39let s:save_cpo = &cpo
40set cpo=
41
42" Functions/Commands to allow the user to change SQL syntax dialects
43" through the use of :SQLSetType <tab> for completion.
44" This works with both Vim 6 and 7.
45
46if !exists("*SQL_SetType")
47    " NOTE: You cannot use function! since this file can be
48    " sourced from within this function.  That will result in
49    " an error reported by Vim.
50    function SQL_GetList(ArgLead, CmdLine, CursorPos)
51
52        if !exists('s:sql_list')
53            " Grab a list of files that contain "sql" in their names
54            let list_indent   = globpath(&runtimepath, 'indent/*sql*')
55            let list_syntax   = globpath(&runtimepath, 'syntax/*sql*')
56            let list_ftplugin = globpath(&runtimepath, 'ftplugin/*sql*')
57
58            let sqls = "\n".list_indent."\n".list_syntax."\n".list_ftplugin."\n"
59
60            " Strip out everything (path info) but the filename
61            " Regex
62            "    From between two newline characters
63            "    Non-greedily grab all characters
64            "    Followed by a valid filename \w\+\.\w\+ (sql.vim)
65            "    Followed by a newline, but do not include the newline
66            "
67            "    Replace it with just the filename (get rid of PATH)
68            "
69            "    Recursively, since there are many filenames that contain
70            "    the word SQL in the indent, syntax and ftplugin directory
71            let sqls = substitute( sqls,
72                        \ '[\n]\%(.\{-}\)\(\w\+\.\w\+\)\n\@=',
73                        \ '\1\n',
74                        \ 'g'
75                        \ )
76
77            " Remove duplicates, since sqlanywhere.vim can exist in the
78            " sytax, indent and ftplugin directory, yet we only want
79            " to display the option once
80            let index = match(sqls, '.\{-}\ze\n')
81            while index > -1
82                " Get the first filename
83                let file = matchstr(sqls, '.\{-}\ze\n', index)
84                " Recursively replace any *other* occurrence of that
85                " filename with nothing (ie remove it)
86                let sqls = substitute(sqls, '\%>'.(index+strlen(file)).'c\<'.file.'\>\n', '', 'g')
87                " Move on to the next filename
88                let index = match(sqls, '.\{-}\ze\n', (index+strlen(file)+1))
89            endwhile
90
91            " Sort the list if using version 7
92            if v:version >= 700
93                let mylist = split(sqls, "\n")
94                let mylist = sort(mylist)
95                let sqls   = join(mylist, "\n")
96            endif
97
98            let s:sql_list = sqls
99        endif
100
101        return s:sql_list
102
103    endfunction
104
105    function SQL_SetType(name)
106
107        " User has decided to override default SQL scripts and
108        " specify a vendor specific version
109        " (ie Oracle, Informix, SQL Anywhere, ...)
110        " So check for an remove any settings that prevent the
111        " scripts from being executed, and then source the
112        " appropriate Vim scripts.
113        if exists("b:did_ftplugin")
114            unlet b:did_ftplugin
115        endif
116        if exists("b:current_syntax")
117            " echomsg 'SQLSetType - clearing syntax'
118            syntax clear
119        endif
120        if exists("b:did_indent")
121            " echomsg 'SQLSetType - clearing indent'
122            unlet b:did_indent
123            " Set these values to their defaults
124            setlocal indentkeys&
125            setlocal indentexpr&
126        endif
127
128        " Ensure the name is in the correct format
129        let new_sql_type = substitute(a:name,
130                    \ '\s*\([^\.]\+\)\(\.\w\+\)\?', '\L\1', '')
131
132        " Do not specify a buffer local variable if it is
133        " the default value
134        if new_sql_type == 'sql'
135          let new_sql_type = 'sqloracle'
136        endif
137        let b:sql_type_override = new_sql_type
138
139        " Vim will automatically source the correct files if we
140        " change the filetype.  You cannot do this with setfiletype
141        " since that command will only execute if a filetype has
142        " not already been set.  In this case we want to override
143        " the existing filetype.
144        let &filetype = 'sql'
145    endfunction
146    command! -nargs=* -complete=custom,SQL_GetList SQLSetType :call SQL_SetType(<q-args>)
147
148endif
149
150if exists("b:sql_type_override")
151    " echo 'sourcing buffer ftplugin/'.b:sql_type_override.'.vim'
152    if globpath(&runtimepath, 'ftplugin/'.b:sql_type_override.'.vim') != ''
153        exec 'runtime ftplugin/'.b:sql_type_override.'.vim'
154    " else
155    "     echomsg 'ftplugin/'.b:sql_type_override.' not exist, using default'
156    endif
157elseif exists("g:sql_type_default")
158    " echo 'sourcing global ftplugin/'.g:sql_type_default.'.vim'
159    if globpath(&runtimepath, 'ftplugin/'.g:sql_type_default.'.vim') != ''
160        exec 'runtime ftplugin/'.g:sql_type_default.'.vim'
161    " else
162    "     echomsg 'ftplugin/'.g:sql_type_default.'.vim not exist, using default'
163    endif
164endif
165
166" If the above runtime command succeeded, do not load the default settings
167if exists("b:did_ftplugin")
168  finish
169endif
170
171let b:undo_ftplugin = "setl comments<"
172
173" Don't load another plugin for this buffer
174let b:did_ftplugin     = 1
175let b:current_ftplugin = 'sql'
176
177" Win32 can filter files in the browse dialog
178if has("gui_win32") && !exists("b:browsefilter")
179    let b:browsefilter = "SQL Files (*.sql)\t*.sql\n" .
180	  \ "All Files (*.*)\t*.*\n"
181endif
182
183" Some standard expressions for use with the matchit strings
184let s:notend = '\%(\<end\s\+\)\@<!'
185let s:when_no_matched_or_others = '\%(\<when\>\%(\s\+\%(\%(\<not\>\s\+\)\?<matched\>\)\|\<others\>\)\@!\)'
186let s:or_replace = '\%(or\s\+replace\s\+\)\?'
187
188" Define patterns for the matchit macro
189if !exists("b:match_words")
190    " SQL is generally case insensitive
191    let b:match_ignorecase = 1
192
193    " Handle the following:
194    " if
195    " elseif | elsif
196    " else [if]
197    " end if
198    "
199    " [while condition] loop
200    "     leave
201    "     break
202    "     continue
203    "     exit
204    " end loop
205    "
206    " for
207    "     leave
208    "     break
209    "     continue
210    "     exit
211    " end loop
212    "
213    " do
214    "     statements
215    " doend
216    "
217    " case
218    " when
219    " when
220    " default
221    " end case
222    "
223    " merge
224    " when not matched
225    " when matched
226    "
227    " EXCEPTION
228    " WHEN column_not_found THEN
229    " WHEN OTHERS THEN
230    "
231    " create[ or replace] procedure|function|event
232
233    let b:match_words =
234		\ '\<begin\>:\<end\>\W*$,'.
235		\
236                \ s:notend . '\<if\>:'.
237                \ '\<elsif\>\|\<elseif\>\|\<else\>:'.
238                \ '\<end\s\+if\>,'.
239                \
240                \ '\<do\>\|'.
241                \ '\<while\>\|'.
242                \ '\%(' . s:notend . '\<loop\>\)\|'.
243                \ '\%(' . s:notend . '\<for\>\):'.
244                \ '\<exit\>\|\<leave\>\|\<break\>\|\<continue\>:'.
245                \ '\%(\<end\s\+\%(for\|loop\>\)\)\|\<doend\>,'.
246                \
247                \ '\%('. s:notend . '\<case\>\):'.
248                \ '\%('.s:when_no_matched_or_others.'\):'.
249                \ '\%(\<when\s\+others\>\|\<end\s\+case\>\),' .
250                \
251                \ '\<merge\>:' .
252                \ '\<when\s\+not\s\+matched\>:' .
253                \ '\<when\s\+matched\>,' .
254                \
255                \ '\%(\<create\s\+' . s:or_replace . '\)\?'.
256                \ '\%(function\|procedure\|event\):'.
257                \ '\<returns\?\>'
258                " \ '\<begin\>\|\<returns\?\>:'.
259                " \ '\<end\>\(;\)\?\s*$'
260                " \ '\<exception\>:'.s:when_no_matched_or_others.
261                " \ ':\<when\s\+others\>,'.
262		"
263                " \ '\%(\<exception\>\|\%('. s:notend . '\<case\>\)\):'.
264                " \ '\%(\<default\>\|'.s:when_no_matched_or_others.'\):'.
265                " \ '\%(\%(\<when\s\+others\>\)\|\<end\s\+case\>\),' .
266endif
267
268" Define how to find the macro definition of a variable using the various
269" [d, [D, [_CTRL_D and so on features
270" Match these values ignoring case
271" ie  DECLARE varname INTEGER
272let &l:define = '\c\<\(VARIABLE\|DECLARE\|IN\|OUT\|INOUT\)\>'
273
274
275" Mappings to move to the next BEGIN ... END block
276" \W - no characters or digits
277nmap <buffer> <silent> ]] :call search('\\c^\\s*begin\\>', 'W' )<CR>
278nmap <buffer> <silent> [[ :call search('\\c^\\s*begin\\>', 'bW' )<CR>
279nmap <buffer> <silent> ][ :call search('\\c^\\s*end\\W*$', 'W' )<CR>
280nmap <buffer> <silent> [] :call search('\\c^\\s*end\\W*$', 'bW' )<CR>
281vmap <buffer> <silent> ]] /\\c^\\s*begin\\><CR>
282vmap <buffer> <silent> [[ ?\\c^\\s*begin\\><CR>
283vmap <buffer> <silent> ][ /\\c^\\s*end\\W*$<CR>
284vmap <buffer> <silent> [] ?\\c^\\s*end\\W*$<CR>
285
286
287" By default only look for CREATE statements, but allow
288" the user to override
289if !exists('g:ftplugin_sql_statements')
290    let g:ftplugin_sql_statements = 'create'
291endif
292
293" Predefined SQL objects what are used by the below mappings using
294" the ]} style maps.
295" This global variable allows the users to override it's value
296" from within their vimrc.
297" Note, you cannot use \?, since these patterns can be used to search
298" backwards, you must use \{,1}
299if !exists('g:ftplugin_sql_objects')
300    let g:ftplugin_sql_objects = 'function,procedure,event,' .
301                \ '\\(existing\\\\|global\\s\\+temporary\\s\\+\\)\\\{,1}' .
302                \ 'table,trigger' .
303                \ ',schema,service,publication,database,datatype,domain' .
304                \ ',index,subscription,synchronization,view,variable'
305endif
306
307" Replace all ,'s with bars, except ones with numbers after them.
308" This will most likely be a \{,1} string.
309let s:ftplugin_sql_objects =
310            \ '\\c^\\s*' .
311            \ '\\(\\(' .
312            \ substitute(g:ftplugin_sql_statements, ',\d\@!', '\\\\\\|', 'g') .
313            \ '\\)\\s\\+\\(or\\s\\+replace\\\s\+\\)\\{,1}\\)\\{,1}' .
314            \ '\\<\\(' .
315            \ substitute(g:ftplugin_sql_objects, ',\d\@!', '\\\\\\|', 'g') .
316            \ '\\)\\>'
317
318" Mappings to move to the next CREATE ... block
319exec "nmap <buffer> <silent> ]} :call search('".s:ftplugin_sql_objects."', 'W')<CR>"
320exec "nmap <buffer> <silent> [{ :call search('".s:ftplugin_sql_objects."', 'bW')<CR>"
321" Could not figure out how to use a :call search() string in visual mode
322" without it ending visual mode
323" Unfortunately, this will add a entry to the search history
324exec 'vmap <buffer> <silent> ]} /'.s:ftplugin_sql_objects.'<CR>'
325exec 'vmap <buffer> <silent> [{ ?'.s:ftplugin_sql_objects.'<CR>'
326
327" Mappings to move to the next COMMENT
328"
329" Had to double the \ for the \| separator since this has a special
330" meaning on maps
331let b:comment_leader = '\\(--\\\|\\/\\/\\\|\\*\\\|\\/\\*\\\|\\*\\/\\)'
332" Find the start of the next comment
333let b:comment_start  = '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
334            \ '\\(\\s*'.b:comment_leader.'\\)'
335" Find the end of the previous comment
336let b:comment_end = '\\(^\\s*'.b:comment_leader.'.*\\n\\)'.
337            \ '\\(^\\s*'.b:comment_leader.'\\)\\@!'
338" Skip over the comment
339let b:comment_jump_over  = "call search('".
340            \ '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
341            \ "', 'W')"
342let b:comment_skip_back  = "call search('".
343            \ '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
344            \ "', 'bW')"
345" Move to the start and end of comments
346exec 'nnoremap <silent><buffer> ]" /'.b:comment_start.'<CR>'
347exec 'nnoremap <silent><buffer> [" /'.b:comment_end.'<CR>'
348exec 'vnoremap <silent><buffer> ]" /'.b:comment_start.'<CR>'
349exec 'vnoremap <silent><buffer> [" /'.b:comment_end.'<CR>'
350
351" Comments can be of the form:
352"   /*
353"    *
354"    */
355" or
356"   --
357" or
358"   //
359setlocal comments=s1:/*,mb:*,ex:*/,:--,://
360
361" Set completion with CTRL-X CTRL-O to autoloaded function.
362if exists('&omnifunc')
363    " Since the SQL completion plugin can be used in conjunction
364    " with other completion filetypes it must record the previous
365    " OMNI function prior to setting up the SQL OMNI function
366    let b:sql_compl_savefunc = &omnifunc
367
368    " This is used by the sqlcomplete.vim plugin
369    " Source it for it's global functions
370    runtime autoload/syntaxcomplete.vim
371
372    setlocal omnifunc=sqlcomplete#Complete
373    " Prevent the intellisense plugin from loading
374    let b:sql_vis = 1
375    if !exists('g:omni_sql_no_default_maps')
376        " Static maps which use populate the completion list
377        " using Vim's syntax highlighting rules
378        imap <buffer> <c-c>a <C-\><C-O>:call sqlcomplete#Map('syntax')<CR><C-X><C-O>
379        imap <buffer> <c-c>k <C-\><C-O>:call sqlcomplete#Map('sqlKeyword')<CR><C-X><C-O>
380        imap <buffer> <c-c>f <C-\><C-O>:call sqlcomplete#Map('sqlFunction')<CR><C-X><C-O>
381        imap <buffer> <c-c>o <C-\><C-O>:call sqlcomplete#Map('sqlOption')<CR><C-X><C-O>
382        imap <buffer> <c-c>T <C-\><C-O>:call sqlcomplete#Map('sqlType')<CR><C-X><C-O>
383        imap <buffer> <c-c>s <C-\><C-O>:call sqlcomplete#Map('sqlStatement')<CR><C-X><C-O>
384        " Dynamic maps which use populate the completion list
385        " using the dbext.vim plugin
386        imap <buffer> <c-c>t <C-\><C-O>:call sqlcomplete#Map('table')<CR><C-X><C-O>
387        imap <buffer> <c-c>p <C-\><C-O>:call sqlcomplete#Map('procedure')<CR><C-X><C-O>
388        imap <buffer> <c-c>v <C-\><C-O>:call sqlcomplete#Map('view')<CR><C-X><C-O>
389        imap <buffer> <c-c>c <C-\><C-O>:call sqlcomplete#Map('column')<CR><C-X><C-O>
390        imap <buffer> <c-c>l <C-\><C-O>:call sqlcomplete#Map('column_csv')<CR><C-X><C-O>
391        " The next 3 maps are only to be used while the completion window is
392        " active due to the <CR> at the beginning of the map
393        imap <buffer> <c-c>L <C-Y><C-\><C-O>:call sqlcomplete#Map('column_csv')<CR><C-X><C-O>
394        " <C-Right> is not recognized on most Unix systems, so only create
395        " these additional maps on the Windows platform.
396        " If you would like to use these maps, choose a different key and make
397        " the same map in your vimrc.
398        if has('win32')
399            imap <buffer> <c-right>  <C-R>=sqlcomplete#DrillIntoTable()<CR>
400            imap <buffer> <c-left>  <C-R>=sqlcomplete#DrillOutOfColumns()<CR>
401        endif
402        " Remove any cached items useful for schema changes
403        imap <buffer> <c-c>R <C-\><C-O>:call sqlcomplete#Map('resetCache')<CR><C-X><C-O>
404    endif
405
406    if b:sql_compl_savefunc != ""
407        " We are changing the filetype to SQL from some other filetype
408        " which had OMNI completion defined.  We need to activate the
409        " SQL completion plugin in order to cache some of the syntax items
410        " while the syntax rules for SQL are active.
411        call sqlcomplete#PreCacheSyntax()
412    endif
413endif
414
415let &cpo = s:save_cpo
416
417" vim:sw=4:
418
419