xref: /vim-8.2.3635/runtime/ftplugin/sql.vim (revision e2f98b95)
1" SQL filetype plugin file
2" Language:    SQL (Common for Oracle, Microsoft SQL Server, Sybase)
3" Version:     1.0
4" Maintainer:  David Fishburn <fishburn at ianywhere dot com>
5" Last Change: Tue Mar 28 2006 2:26:48 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    " This is used by the sqlcomplete.vim plugin
364    " Source it for it's global functions
365    runtime autoload/syntaxcomplete.vim
366
367    setlocal omnifunc=sqlcomplete#Complete
368    " Prevent the intellisense plugin from loading
369    let b:sql_vis = 1
370    if !exists('g:omni_sql_no_default_maps')
371        " Static maps which use populate the completion list
372        " using Vim's syntax highlighting rules
373        imap <buffer> <c-c>a <C-\><C-O>:let b:sql_compl_type='syntax'<CR><C-X><C-O>
374        imap <buffer> <c-c>k <C-\><C-O>:let b:sql_compl_type='sqlKeyword'<CR><C-X><C-O>
375        imap <buffer> <c-c>f <C-\><C-O>:let b:sql_compl_type='sqlFunction'<CR><C-X><C-O>
376        imap <buffer> <c-c>o <C-\><C-O>:let b:sql_compl_type='sqlOption'<CR><C-X><C-O>
377        imap <buffer> <c-c>T <C-\><C-O>:let b:sql_compl_type='sqlType'<CR><C-X><C-O>
378        imap <buffer> <c-c>s <C-\><C-O>:let b:sql_compl_type='sqlStatement'<CR><C-X><C-O>
379        " Dynamic maps which use populate the completion list
380        " using the dbext.vim plugin
381        imap <buffer> <c-c>t <C-\><C-O>:let b:sql_compl_type='table'<CR><C-X><C-O>
382        imap <buffer> <c-c>p <C-\><C-O>:let b:sql_compl_type='procedure'<CR><C-X><C-O>
383        imap <buffer> <c-c>v <C-\><C-O>:let b:sql_compl_type='view'<CR><C-X><C-O>
384        imap <buffer> <c-c>c <C-\><C-O>:let b:sql_compl_type='column'<CR><C-X><C-O>
385        imap <buffer> <c-c>l <C-\><C-O>:let b:sql_compl_type='column_csv'<CR><C-X><C-O>
386        " The next 3 maps are only to be used while the completion window is
387        " active due to the <CR> at the beginning of the map
388        imap <buffer> <c-c>L <CR><C-\><C-O>:let b:sql_compl_type='column_csv'<CR><C-X><C-O>
389        if has('win32')
390            imap <buffer> <c-right>  <CR><C-\><C-O>:let b:sql_compl_type='column'<CR><C-X><C-O>
391            imap <buffer> <c-left>   <C-\><C-O>:let b:sql_compl_type='tableReset'<CR><C-X><C-O>
392        endif
393        " Remove any cached items useful for schema changes
394        imap <buffer> <c-c>R <C-\><C-O>:let b:sql_compl_type='resetCache'<CR><C-X><C-O>
395    endif
396endif
397
398let &cpo = s:save_cpo
399
400" vim:sw=4:
401
402