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