summaryrefslogtreecommitdiff
path: root/runtime/ftplugin/sql.vim
blob: 9f40b019af5da1970a34336a9db61ba51aa065cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
" SQL filetype plugin file
" Language:    SQL (Common for Oracle, Microsoft SQL Server, Sybase)
" Version:     4.0
" Maintainer:  David Fishburn <fishburn at ianywhere dot com>
" Last Change: Wed 27 Feb 2008 04:35:21 PM Eastern Standard Time
" Download:    http://vim.sourceforge.net/script.php?script_id=454

" For more details please use:
"        :h sql.txt
"
" This file should only contain values that are common to all SQL languages
" Oracle, Microsoft SQL Server, Sybase ASA/ASE, MySQL, and so on
" If additional features are required create:
"        vimfiles/after/ftplugin/sql.vim (Windows)
"        .vim/after/ftplugin/sql.vim     (Unix)
" to override and add any of your own settings.


" This file also creates a command, SQLSetType, which allows you to change
" SQL dialects on the fly.  For example, if I open an Oracle SQL file, it
" is color highlighted appropriately.  If I open an Informix SQL file, it
" will still be highlighted according to Oracles settings.  By running:
"     :SQLSetType sqlinformix
"
" All files called sqlinformix.vim will be loaded from the indent and syntax
" directories.  This allows you to easily flip SQL dialects on a per file
" basis.  NOTE: you can also use completion:
"     :SQLSetType <tab>
"
" To change the default dialect, add the following to your vimrc:
"    let g:sql_type_default = 'sqlanywhere'


" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
  finish
endif

let s:save_cpo = &cpo
set cpo=

" Disable autowrapping for code, but enable for comments
" t	Auto-wrap text using textwidth
" c     Auto-wrap comments using textwidth, inserting the current comment
"       leader automatically.
setlocal formatoptions-=t
setlocal formatoptions-=c

" Functions/Commands to allow the user to change SQL syntax dialects
" through the use of :SQLSetType <tab> for completion.
" This works with both Vim 6 and 7.

if !exists("*SQL_SetType")
    " NOTE: You cannot use function! since this file can be 
    " sourced from within this function.  That will result in
    " an error reported by Vim.
    function SQL_GetList(ArgLead, CmdLine, CursorPos)

        if !exists('s:sql_list')
            " Grab a list of files that contain "sql" in their names
            let list_indent   = globpath(&runtimepath, 'indent/*sql*')
            let list_syntax   = globpath(&runtimepath, 'syntax/*sql*')
            let list_ftplugin = globpath(&runtimepath, 'ftplugin/*sql*')

            let sqls = "\n".list_indent."\n".list_syntax."\n".list_ftplugin."\n"

            " Strip out everything (path info) but the filename
            " Regex
            "    From between two newline characters
            "    Non-greedily grab all characters
            "    Followed by a valid filename \w\+\.\w\+ (sql.vim)
            "    Followed by a newline, but do not include the newline
            "
            "    Replace it with just the filename (get rid of PATH)
            "
            "    Recursively, since there are many filenames that contain
            "    the word SQL in the indent, syntax and ftplugin directory
            let sqls = substitute( sqls, 
                        \ '[\n]\%(.\{-}\)\(\w\+\.\w\+\)\n\@=', 
                        \ '\1\n', 
                        \ 'g'
                        \ )

            " Remove duplicates, since sqlanywhere.vim can exist in the
            " sytax, indent and ftplugin directory, yet we only want
            " to display the option once
            let index = match(sqls, '.\{-}\ze\n')
            while index > -1
                " Get the first filename
                let file = matchstr(sqls, '.\{-}\ze\n', index)
                " Recursively replace any *other* occurrence of that
                " filename with nothing (ie remove it)
                let sqls = substitute(sqls, '\%>'.(index+strlen(file)).'c\<'.file.'\>\n', '', 'g')
                " Move on to the next filename
                let index = match(sqls, '.\{-}\ze\n', (index+strlen(file)+1))
            endwhile

            " Sort the list if using version 7
            if v:version >= 700
                let mylist = split(sqls, "\n")
                let mylist = sort(mylist)
                let sqls   = join(mylist, "\n")
            endif

            let s:sql_list = sqls
        endif

        return s:sql_list

    endfunction

    function SQL_SetType(name)

        " User has decided to override default SQL scripts and
        " specify a vendor specific version 
        " (ie Oracle, Informix, SQL Anywhere, ...)
        " So check for an remove any settings that prevent the
        " scripts from being executed, and then source the 
        " appropriate Vim scripts.
        if exists("b:did_ftplugin")
            unlet b:did_ftplugin
        endif
        if exists("b:current_syntax")
            " echomsg 'SQLSetType - clearing syntax'
            syntax clear
        endif
        if exists("b:did_indent")
            " echomsg 'SQLSetType - clearing indent'
            unlet b:did_indent
            " Set these values to their defaults
            setlocal indentkeys&
            setlocal indentexpr&
        endif

        " Ensure the name is in the correct format
        let new_sql_type = substitute(a:name, 
                    \ '\s*\([^\.]\+\)\(\.\w\+\)\?', '\L\1', '')

        " Do not specify a buffer local variable if it is 
        " the default value
        if new_sql_type == 'sql'
          let new_sql_type = 'sqloracle'
        endif
        let b:sql_type_override = new_sql_type

        " Vim will automatically source the correct files if we
        " change the filetype.  You cannot do this with setfiletype
        " since that command will only execute if a filetype has
        " not already been set.  In this case we want to override
        " the existing filetype.
        let &filetype = 'sql'
    endfunction
    command! -nargs=* -complete=custom,SQL_GetList SQLSetType :call SQL_SetType(<q-args>)

endif

if exists("b:sql_type_override")
    " echo 'sourcing buffer ftplugin/'.b:sql_type_override.'.vim'
    if globpath(&runtimepath, 'ftplugin/'.b:sql_type_override.'.vim') != ''
        exec 'runtime ftplugin/'.b:sql_type_override.'.vim'
    " else
    "     echomsg 'ftplugin/'.b:sql_type_override.' not exist, using default'
    endif
elseif exists("g:sql_type_default")
    " echo 'sourcing global ftplugin/'.g:sql_type_default.'.vim'
    if globpath(&runtimepath, 'ftplugin/'.g:sql_type_default.'.vim') != ''
        exec 'runtime ftplugin/'.g:sql_type_default.'.vim'
    " else
    "     echomsg 'ftplugin/'.g:sql_type_default.'.vim not exist, using default'
    endif
endif

" If the above runtime command succeeded, do not load the default settings
if exists("b:did_ftplugin")
  finish
endif

let b:undo_ftplugin = "setl comments<"

" Don't load another plugin for this buffer
let b:did_ftplugin     = 1
let b:current_ftplugin = 'sql'

" Win32 can filter files in the browse dialog
if has("gui_win32") && !exists("b:browsefilter")
    let b:browsefilter = "SQL Files (*.sql)\t*.sql\n" .
	  \ "All Files (*.*)\t*.*\n"
endif

" Some standard expressions for use with the matchit strings
let s:notend = '\%(\<end\s\+\)\@<!'
let s:when_no_matched_or_others = '\%(\<when\>\%(\s\+\%(\%(\<not\>\s\+\)\?<matched\>\)\|\<others\>\)\@!\)'
let s:or_replace = '\%(or\s\+replace\s\+\)\?'

" Define patterns for the matchit macro
if !exists("b:match_words")
    " SQL is generally case insensitive
    let b:match_ignorecase = 1

    " Handle the following:
    " if
    " elseif | elsif
    " else [if]
    " end if
    "
    " [while condition] loop
    "     leave
    "     break
    "     continue
    "     exit
    " end loop
    "
    " for
    "     leave
    "     break
    "     continue
    "     exit
    " end loop
    "
    " do
    "     statements
    " doend
    "
    " case
    " when 
    " when
    " default
    " end case
    "
    " merge
    " when not matched
    " when matched
    "
    " EXCEPTION
    " WHEN column_not_found THEN
    " WHEN OTHERS THEN
    "
    " create[ or replace] procedure|function|event

    let b:match_words =
		\ '\<begin\>:\<end\>\W*$,'.
		\
                \ s:notend . '\<if\>:'.
                \ '\<elsif\>\|\<elseif\>\|\<else\>:'.
                \ '\<end\s\+if\>,'.
                \
                \ '\<do\>\|'.
                \ '\<while\>\|'.
                \ '\%(' . s:notend . '\<loop\>\)\|'.
                \ '\%(' . s:notend . '\<for\>\):'.
                \ '\<exit\>\|\<leave\>\|\<break\>\|\<continue\>:'.
                \ '\%(\<end\s\+\%(for\|loop\>\)\)\|\<doend\>,'.
                \
                \ '\%('. s:notend . '\<case\>\):'.
                \ '\%('.s:when_no_matched_or_others.'\):'.
                \ '\%(\<when\s\+others\>\|\<end\s\+case\>\),' .
                \
                \ '\<merge\>:' .
                \ '\<when\s\+not\s\+matched\>:' .
                \ '\<when\s\+matched\>,' .
                \
                \ '\%(\<create\s\+' . s:or_replace . '\)\?'.
                \ '\%(function\|procedure\|event\):'.
                \ '\<returns\?\>'
                " \ '\<begin\>\|\<returns\?\>:'.
                " \ '\<end\>\(;\)\?\s*$'
                " \ '\<exception\>:'.s:when_no_matched_or_others.
                " \ ':\<when\s\+others\>,'.
		"
                " \ '\%(\<exception\>\|\%('. s:notend . '\<case\>\)\):'.
                " \ '\%(\<default\>\|'.s:when_no_matched_or_others.'\):'.
                " \ '\%(\%(\<when\s\+others\>\)\|\<end\s\+case\>\),' .
endif

" Define how to find the macro definition of a variable using the various
" [d, [D, [_CTRL_D and so on features
" Match these values ignoring case
" ie  DECLARE varname INTEGER
let &l:define = '\c\<\(VARIABLE\|DECLARE\|IN\|OUT\|INOUT\)\>'


" Mappings to move to the next BEGIN ... END block
" \W - no characters or digits
nmap <buffer> <silent> ]] :call search('\\c^\\s*begin\\>', 'W' )<CR>
nmap <buffer> <silent> [[ :call search('\\c^\\s*begin\\>', 'bW' )<CR>
nmap <buffer> <silent> ][ :call search('\\c^\\s*end\\W*$', 'W' )<CR>
nmap <buffer> <silent> [] :call search('\\c^\\s*end\\W*$', 'bW' )<CR>
vmap <buffer> <silent> ]] :<C-U>exec "normal! gv"<Bar>call search('\\c^\\s*begin\\>', 'W' )<CR>
vmap <buffer> <silent> [[ :<C-U>exec "normal! gv"<Bar>call search('\\c^\\s*begin\\>', 'bW' )<CR>
vmap <buffer> <silent> ][ :<C-U>exec "normal! gv"<Bar>call search('\\c^\\s*end\\W*$', 'W' )<CR>
vmap <buffer> <silent> [] :<C-U>exec "normal! gv"<Bar>call search('\\c^\\s*end\\W*$', 'bW' )<CR>


" By default only look for CREATE statements, but allow
" the user to override
if !exists('g:ftplugin_sql_statements')
    let g:ftplugin_sql_statements = 'create'
endif

" Predefined SQL objects what are used by the below mappings using
" the ]} style maps.
" This global variable allows the users to override it's value
" from within their vimrc.
" Note, you cannot use \?, since these patterns can be used to search
" backwards, you must use \{,1}
if !exists('g:ftplugin_sql_objects')
    let g:ftplugin_sql_objects = 'function,procedure,event,' .
                \ '\\(existing\\\\|global\\s\\+temporary\\s\\+\\)\\\{,1}' .
                \ 'table,trigger' .
                \ ',schema,service,publication,database,datatype,domain' .
                \ ',index,subscription,synchronization,view,variable'
endif

" Replace all ,'s with bars, except ones with numbers after them.
" This will most likely be a \{,1} string.
let s:ftplugin_sql_objects = 
            \ '\\c^\\s*' .
            \ '\\(\\(' .
            \ substitute(g:ftplugin_sql_statements, ',\d\@!', '\\\\\\|', 'g') .
            \ '\\)\\s\\+\\(or\\s\\+replace\\\s\+\\)\\{,1}\\)\\{,1}' .
            \ '\\<\\(' .
            \ substitute(g:ftplugin_sql_objects, ',\d\@!', '\\\\\\|', 'g') .
            \ '\\)\\>' 

" Mappings to move to the next CREATE ... block
exec "nmap <buffer> <silent> ]} :call search('".s:ftplugin_sql_objects."', 'W')<CR>"
exec "nmap <buffer> <silent> [{ :call search('".s:ftplugin_sql_objects."', 'bW')<CR>"
" Could not figure out how to use a :call search() string in visual mode
" without it ending visual mode
" Unfortunately, this will add a entry to the search history
exec 'vmap <buffer> <silent> ]} /'.s:ftplugin_sql_objects.'<CR>'
exec 'vmap <buffer> <silent> [{ ?'.s:ftplugin_sql_objects.'<CR>'

" Mappings to move to the next COMMENT
"
" Had to double the \ for the \| separator since this has a special
" meaning on maps
let b:comment_leader = '\\(--\\\|\\/\\/\\\|\\*\\\|\\/\\*\\\|\\*\\/\\)'
" Find the start of the next comment
let b:comment_start  = '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
            \ '\\(\\s*'.b:comment_leader.'\\)'
" Find the end of the previous comment
let b:comment_end = '\\(^\\s*'.b:comment_leader.'.*\\n\\)'.
            \ '\\(^\\s*'.b:comment_leader.'\\)\\@!'
" Skip over the comment
let b:comment_jump_over  = "call search('".
            \ '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
            \ "', 'W')"
let b:comment_skip_back  = "call search('".
            \ '^\\(\\s*'.b:comment_leader.'.*\\n\\)\\@<!'.
            \ "', 'bW')"
" Move to the start and end of comments
exec 'nnoremap <silent><buffer> ]" :call search('."'".b:comment_start."'".', "W" )<CR>'
exec 'nnoremap <silent><buffer> [" :call search('."'".b:comment_end."'".', "W" )<CR>'
exec 'vnoremap <silent><buffer> ]" :<C-U>exec "normal! gv"<Bar>call search('."'".b:comment_start."'".', "W" )<CR>'
exec 'vnoremap <silent><buffer> [" :<C-U>exec "normal! gv"<Bar>call search('."'".b:comment_end."'".', "W" )<CR>'

" Comments can be of the form:
"   /*
"    *
"    */
" or
"   --
" or
"   // 
setlocal comments=s1:/*,mb:*,ex:*/,:--,://

" Set completion with CTRL-X CTRL-O to autoloaded function.
if exists('&omnifunc')
    " Since the SQL completion plugin can be used in conjunction
    " with other completion filetypes it must record the previous
    " OMNI function prior to setting up the SQL OMNI function
    let b:sql_compl_savefunc = &omnifunc

    " This is used by the sqlcomplete.vim plugin
    " Source it for it's global functions
    runtime autoload/syntaxcomplete.vim 

    setlocal omnifunc=sqlcomplete#Complete
    " Prevent the intellisense plugin from loading
    let b:sql_vis = 1
    if !exists('g:omni_sql_no_default_maps')
        " Static maps which use populate the completion list
        " using Vim's syntax highlighting rules
        imap <buffer> <c-c>a <C-\><C-O>:call sqlcomplete#Map('syntax')<CR><C-X><C-O>
        imap <buffer> <c-c>k <C-\><C-O>:call sqlcomplete#Map('sqlKeyword')<CR><C-X><C-O>
        imap <buffer> <c-c>f <C-\><C-O>:call sqlcomplete#Map('sqlFunction')<CR><C-X><C-O>
        imap <buffer> <c-c>o <C-\><C-O>:call sqlcomplete#Map('sqlOption')<CR><C-X><C-O>
        imap <buffer> <c-c>T <C-\><C-O>:call sqlcomplete#Map('sqlType')<CR><C-X><C-O>
        imap <buffer> <c-c>s <C-\><C-O>:call sqlcomplete#Map('sqlStatement')<CR><C-X><C-O>
        " Dynamic maps which use populate the completion list
        " using the dbext.vim plugin
        imap <buffer> <c-c>t <C-\><C-O>:call sqlcomplete#Map('table')<CR><C-X><C-O>
        imap <buffer> <c-c>p <C-\><C-O>:call sqlcomplete#Map('procedure')<CR><C-X><C-O>
        imap <buffer> <c-c>v <C-\><C-O>:call sqlcomplete#Map('view')<CR><C-X><C-O>
        imap <buffer> <c-c>c <C-\><C-O>:call sqlcomplete#Map('column')<CR><C-X><C-O>
        imap <buffer> <c-c>l <C-\><C-O>:call sqlcomplete#Map('column_csv')<CR><C-X><C-O>
        " The next 3 maps are only to be used while the completion window is
        " active due to the <CR> at the beginning of the map
        imap <buffer> <c-c>L <C-Y><C-\><C-O>:call sqlcomplete#Map('column_csv')<CR><C-X><C-O>
        " <C-Right> is not recognized on most Unix systems, so only create
        " these additional maps on the Windows platform.
        " If you would like to use these maps, choose a different key and make
        " the same map in your vimrc.
        if has('win32')
            imap <buffer> <c-right>  <C-R>=sqlcomplete#DrillIntoTable()<CR>
            imap <buffer> <c-left>  <C-R>=sqlcomplete#DrillOutOfColumns()<CR>
        endif
        " Remove any cached items useful for schema changes
        imap <buffer> <c-c>R <C-\><C-O>:call sqlcomplete#Map('resetCache')<CR><C-X><C-O>
    endif

    if b:sql_compl_savefunc != ""
        " We are changing the filetype to SQL from some other filetype
        " which had OMNI completion defined.  We need to activate the
        " SQL completion plugin in order to cache some of the syntax items
        " while the syntax rules for SQL are active.
        call sqlcomplete#PreCacheSyntax()
    endif
endif

let &cpo = s:save_cpo

" vim:sw=4: