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