da78242123
Many of the syntax checkers were setting the 'bufnr' key for each error manually and one was setting 'text' if it didnt exist too. Add the 'defaults' option to SyntasticMake to set default values in the returned error list - it only sets values if they are empty.
455 lines
13 KiB
VimL
455 lines
13 KiB
VimL
"============================================================================
|
|
"File: syntastic.vim
|
|
"Description: vim plugin for on the fly syntax checking
|
|
"Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
|
|
"Version: 2.0.0
|
|
"Last Change: 2 Dec, 2011
|
|
"License: This program is free software. It comes without any warranty,
|
|
" to the extent permitted by applicable law. You can redistribute
|
|
" it and/or modify it under the terms of the Do What The Fuck You
|
|
" Want To Public License, Version 2, as published by Sam Hocevar.
|
|
" See http://sam.zoy.org/wtfpl/COPYING for more details.
|
|
"
|
|
"============================================================================
|
|
|
|
if exists("g:loaded_syntastic_plugin")
|
|
finish
|
|
endif
|
|
let g:loaded_syntastic_plugin = 1
|
|
|
|
let s:running_windows = has("win16") || has("win32") || has("win64")
|
|
|
|
if !s:running_windows
|
|
let s:uname = system('uname')
|
|
endif
|
|
|
|
if !exists("g:syntastic_enable_signs")
|
|
let g:syntastic_enable_signs = 1
|
|
endif
|
|
if !has('signs')
|
|
let g:syntastic_enable_signs = 0
|
|
endif
|
|
|
|
if !exists("g:syntastic_enable_balloons")
|
|
let g:syntastic_enable_balloons = 1
|
|
endif
|
|
if !has('balloon_eval')
|
|
let g:syntastic_enable_balloons = 0
|
|
endif
|
|
|
|
if !exists("g:syntastic_enable_highlighting")
|
|
let g:syntastic_enable_highlighting = 1
|
|
endif
|
|
|
|
if !exists("g:syntastic_auto_loc_list")
|
|
let g:syntastic_auto_loc_list = 2
|
|
endif
|
|
|
|
if !exists("g:syntastic_auto_jump")
|
|
let syntastic_auto_jump=0
|
|
endif
|
|
|
|
if !exists("g:syntastic_quiet_warnings")
|
|
let g:syntastic_quiet_warnings = 0
|
|
endif
|
|
|
|
if !exists("g:syntastic_stl_format")
|
|
let g:syntastic_stl_format = '[Syntax: line:%F (%t)]'
|
|
endif
|
|
|
|
if !exists("g:syntastic_mode_map")
|
|
let g:syntastic_mode_map = {}
|
|
endif
|
|
|
|
if !has_key(g:syntastic_mode_map, "mode")
|
|
let g:syntastic_mode_map['mode'] = 'active'
|
|
endif
|
|
|
|
if !has_key(g:syntastic_mode_map, "active_filetypes")
|
|
let g:syntastic_mode_map['active_filetypes'] = []
|
|
endif
|
|
|
|
if !has_key(g:syntastic_mode_map, "passive_filetypes")
|
|
let g:syntastic_mode_map['passive_filetypes'] = []
|
|
endif
|
|
|
|
command! -nargs=0 SyntasticToggleMode call s:ToggleMode()
|
|
command! -nargs=0 SyntasticCheck call s:UpdateErrors(0) <bar> redraw!
|
|
command! Errors call s:ShowLocList()
|
|
|
|
"refresh and redraw all the error info for this buf when saving or reading
|
|
autocmd bufreadpost,bufwritepost * call s:UpdateErrors(1)
|
|
function! s:UpdateErrors(auto_invoked)
|
|
if &buftype == 'quickfix'
|
|
return
|
|
endif
|
|
|
|
if !a:auto_invoked || s:ModeMapAllowsAutoChecking()
|
|
call s:CacheErrors()
|
|
end
|
|
|
|
if g:syntastic_enable_balloons
|
|
call s:RefreshBalloons()
|
|
endif
|
|
|
|
if g:syntastic_enable_signs
|
|
call s:RefreshSigns()
|
|
endif
|
|
|
|
if s:BufHasErrorsOrWarningsToDisplay()
|
|
call setloclist(0, b:syntastic_loclist)
|
|
if g:syntastic_auto_jump
|
|
silent! ll
|
|
endif
|
|
elseif g:syntastic_auto_loc_list == 2
|
|
lclose
|
|
endif
|
|
|
|
if g:syntastic_auto_loc_list == 1
|
|
if s:BufHasErrorsOrWarningsToDisplay()
|
|
call s:ShowLocList()
|
|
else
|
|
"TODO: this will close the loc list window if one was opened by
|
|
"something other than syntastic
|
|
lclose
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
"detect and cache all syntax errors in this buffer
|
|
"
|
|
"depends on a function called SyntaxCheckers_{&ft}_GetLocList() existing
|
|
"elsewhere
|
|
function! s:CacheErrors()
|
|
let b:syntastic_loclist = []
|
|
|
|
if filereadable(expand("%"))
|
|
for ft in split(&ft, '\.')
|
|
if s:Checkable(ft)
|
|
let b:syntastic_loclist = extend(b:syntastic_loclist, SyntaxCheckers_{ft}_GetLocList())
|
|
endif
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
"toggle the g:syntastic_mode_map['mode']
|
|
function! s:ToggleMode()
|
|
if g:syntastic_mode_map['mode'] == "active"
|
|
let g:syntastic_mode_map['mode'] = "passive"
|
|
else
|
|
let g:syntastic_mode_map['mode'] = "active"
|
|
endif
|
|
|
|
echo "Syntastic: " . g:syntastic_mode_map['mode'] . " mode enabled"
|
|
endfunction
|
|
|
|
"check the current filetypes against g:syntastic_mode_map to determine whether
|
|
"active mode syntax checking should be done
|
|
function! s:ModeMapAllowsAutoChecking()
|
|
if g:syntastic_mode_map['mode'] == 'passive'
|
|
|
|
"check at least one filetype is active
|
|
for ft in split(&ft, '\.')
|
|
if index(g:syntastic_mode_map['active_filetypes'], ft) != -1
|
|
return 1
|
|
endif
|
|
return 0
|
|
endfor
|
|
else
|
|
|
|
"check no filetypes are passive
|
|
for ft in split(&ft, '\.')
|
|
if index(g:syntastic_mode_map['passive_filetypes'], ft) != -1
|
|
return 0
|
|
endif
|
|
return 1
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
"return true if there are cached errors/warnings for this buf
|
|
function! s:BufHasErrorsOrWarnings()
|
|
return exists("b:syntastic_loclist") && !empty(b:syntastic_loclist)
|
|
endfunction
|
|
|
|
"return true if there are cached errors for this buf
|
|
function! s:BufHasErrors()
|
|
return len(s:ErrorsForType('E')) > 0
|
|
endfunction
|
|
|
|
function! s:BufHasErrorsOrWarningsToDisplay()
|
|
return s:BufHasErrors() || (!g:syntastic_quiet_warnings && s:BufHasErrorsOrWarnings())
|
|
endfunction
|
|
|
|
function! s:ErrorsForType(type)
|
|
if !exists("b:syntastic_loclist")
|
|
return []
|
|
endif
|
|
return filter(copy(b:syntastic_loclist), 'v:val["type"] ==? "' . a:type . '"')
|
|
endfunction
|
|
|
|
function s:Errors()
|
|
return extend(s:ErrorsForType("E"), s:ErrorsForType(''))
|
|
endfunction
|
|
|
|
function s:Warnings()
|
|
return s:ErrorsForType("W")
|
|
endfunction
|
|
|
|
if g:syntastic_enable_signs
|
|
"use >> to display syntax errors in the sign column
|
|
sign define SyntasticError text=>> texthl=error
|
|
sign define SyntasticWarning text=>> texthl=todo
|
|
endif
|
|
|
|
"start counting sign ids at 5000, start here to hopefully avoid conflicting
|
|
"with any other code that places signs (not sure if this precaution is
|
|
"actually needed)
|
|
let s:first_sign_id = 5000
|
|
let s:next_sign_id = s:first_sign_id
|
|
|
|
"place signs by all syntax errs in the buffer
|
|
function s:SignErrors()
|
|
if s:BufHasErrorsOrWarningsToDisplay()
|
|
for i in b:syntastic_loclist
|
|
if i['bufnr'] != bufnr("")
|
|
continue
|
|
endif
|
|
|
|
let sign_type = 'SyntasticError'
|
|
if i['type'] == 'W'
|
|
let sign_type = 'SyntasticWarning'
|
|
endif
|
|
|
|
exec "sign place ". s:next_sign_id ." line=". i['lnum'] ." name=". sign_type ." file=". expand("%:p")
|
|
call add(s:BufSignIds(), s:next_sign_id)
|
|
let s:next_sign_id += 1
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
"remove the signs with the given ids from this buffer
|
|
function! s:RemoveSigns(ids)
|
|
for i in a:ids
|
|
exec "sign unplace " . i
|
|
call remove(s:BufSignIds(), index(s:BufSignIds(), i))
|
|
endfor
|
|
endfunction
|
|
|
|
"get all the ids of the SyntaxError signs in the buffer
|
|
function! s:BufSignIds()
|
|
if !exists("b:syntastic_sign_ids")
|
|
let b:syntastic_sign_ids = []
|
|
endif
|
|
return b:syntastic_sign_ids
|
|
endfunction
|
|
|
|
"update the error signs
|
|
function! s:RefreshSigns()
|
|
let old_signs = copy(s:BufSignIds())
|
|
call s:SignErrors()
|
|
call s:RemoveSigns(old_signs)
|
|
let s:first_sign_id = s:next_sign_id
|
|
endfunction
|
|
|
|
"display the cached errors for this buf in the location list
|
|
function! s:ShowLocList()
|
|
if exists("b:syntastic_loclist")
|
|
let num = winnr()
|
|
lopen
|
|
if num != winnr()
|
|
wincmd p
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
"remove all error highlights from the window
|
|
function! s:ClearErrorHighlights()
|
|
for i in s:ErrorHighlightIds()
|
|
call matchdelete(i)
|
|
endfor
|
|
let w:syntastic_error_highlight_ids = []
|
|
endfunction
|
|
|
|
"add an error highlight to the window
|
|
function! s:HighlightError(group, pattern)
|
|
call add(s:ErrorHighlightIds(), matchadd(a:group, a:pattern))
|
|
endfunction
|
|
|
|
"get (and/or init) the array of error highlights for the current window
|
|
function! s:ErrorHighlightIds()
|
|
if !exists("w:syntastic_error_highlight_ids")
|
|
let w:syntastic_error_highlight_ids = []
|
|
endif
|
|
return w:syntastic_error_highlight_ids
|
|
endfunction
|
|
|
|
"check if a syntax checker exists for the given filetype - and attempt to
|
|
"load one
|
|
function! s:Checkable(ft)
|
|
if !exists("g:loaded_" . a:ft . "_syntax_checker")
|
|
exec "runtime syntax_checkers/" . a:ft . ".vim"
|
|
endif
|
|
|
|
return exists("*SyntaxCheckers_". a:ft ."_GetLocList")
|
|
endfunction
|
|
|
|
"set up error ballons for the current set of errors
|
|
function! s:RefreshBalloons()
|
|
let b:syntastic_balloons = {}
|
|
if s:BufHasErrorsOrWarningsToDisplay()
|
|
for i in b:syntastic_loclist
|
|
let b:syntastic_balloons[i['lnum']] = i['text']
|
|
endfor
|
|
set beval bexpr=SyntasticErrorBalloonExpr()
|
|
endif
|
|
endfunction
|
|
|
|
"return a string representing the state of buffer according to
|
|
"g:syntastic_stl_format
|
|
"
|
|
"return '' if no errors are cached for the buffer
|
|
function! SyntasticStatuslineFlag()
|
|
if s:BufHasErrorsOrWarningsToDisplay()
|
|
let errors = s:Errors()
|
|
let warnings = s:Warnings()
|
|
|
|
let output = g:syntastic_stl_format
|
|
|
|
"hide stuff wrapped in %E(...) unless there are errors
|
|
let output = substitute(output, '\C%E{\([^}]*\)}', len(errors) ? '\1' : '' , 'g')
|
|
|
|
"hide stuff wrapped in %W(...) unless there are warnings
|
|
let output = substitute(output, '\C%W{\([^}]*\)}', len(warnings) ? '\1' : '' , 'g')
|
|
|
|
"hide stuff wrapped in %B(...) unless there are both errors and warnings
|
|
let output = substitute(output, '\C%B{\([^}]*\)}', (len(warnings) && len(errors)) ? '\1' : '' , 'g')
|
|
|
|
"sub in the total errors/warnings/both
|
|
let output = substitute(output, '\C%w', len(warnings), 'g')
|
|
let output = substitute(output, '\C%e', len(errors), 'g')
|
|
let output = substitute(output, '\C%t', len(b:syntastic_loclist), 'g')
|
|
|
|
"first error/warning line num
|
|
let output = substitute(output, '\C%F', b:syntastic_loclist[0]['lnum'], 'g')
|
|
|
|
"first error line num
|
|
let output = substitute(output, '\C%fe', len(errors) ? errors[0]['lnum'] : '', 'g')
|
|
|
|
"first warning line num
|
|
let output = substitute(output, '\C%fw', len(warnings) ? warnings[0]['lnum'] : '', 'g')
|
|
|
|
return output
|
|
else
|
|
return ''
|
|
endif
|
|
endfunction
|
|
|
|
"A wrapper for the :lmake command. Sets up the make environment according to
|
|
"the options given, runs make, resets the environment, returns the location
|
|
"list
|
|
"
|
|
"a:options can contain the following keys:
|
|
" 'makeprg'
|
|
" 'errorformat'
|
|
"
|
|
"The corresponding options are set for the duration of the function call. They
|
|
"are set with :let, so dont escape spaces.
|
|
"
|
|
"a:options may also contain:
|
|
" 'defaults' - a dict containing default values for the returned errors
|
|
function! SyntasticMake(options)
|
|
let old_loclist = getloclist(0)
|
|
let old_makeprg = &makeprg
|
|
let old_shellpipe = &shellpipe
|
|
let old_shell = &shell
|
|
let old_errorformat = &errorformat
|
|
|
|
if !s:running_windows && (s:uname !~ "FreeBSD")
|
|
"this is a hack to stop the screen needing to be ':redraw'n when
|
|
"when :lmake is run. Otherwise the screen flickers annoyingly
|
|
let &shellpipe='&>'
|
|
let &shell = '/bin/bash'
|
|
endif
|
|
|
|
if has_key(a:options, 'makeprg')
|
|
let &makeprg = a:options['makeprg']
|
|
endif
|
|
|
|
if has_key(a:options, 'errorformat')
|
|
let &errorformat = a:options['errorformat']
|
|
endif
|
|
|
|
silent lmake!
|
|
let errors = getloclist(0)
|
|
|
|
call setloclist(0, old_loclist)
|
|
let &makeprg = old_makeprg
|
|
let &errorformat = old_errorformat
|
|
let &shellpipe=old_shellpipe
|
|
let &shell=old_shell
|
|
|
|
if !s:running_windows && s:uname =~ "FreeBSD"
|
|
redraw!
|
|
endif
|
|
|
|
if has_key(a:options, 'defaults')
|
|
call SyntasticAddToErrors(errors, a:options['defaults'])
|
|
endif
|
|
|
|
return errors
|
|
endfunction
|
|
|
|
"get the error balloon for the current mouse position
|
|
function! SyntasticErrorBalloonExpr()
|
|
if !exists('b:syntastic_balloons')
|
|
return ''
|
|
endif
|
|
return get(b:syntastic_balloons, v:beval_lnum, '')
|
|
endfunction
|
|
|
|
"highlight the list of errors (a:errors) using matchadd()
|
|
"
|
|
"a:termfunc is provided to highlight errors that do not have a 'col' key (and
|
|
"hence cant be done automatically). This function must take one arg (an error
|
|
"item) and return a regex to match that item in the buffer.
|
|
"
|
|
"an optional boolean third argument can be provided to force a:termfunc to be
|
|
"used regardless of whether a 'col' key is present for the error
|
|
function! SyntasticHighlightErrors(errors, termfunc, ...)
|
|
if !g:syntastic_enable_highlighting
|
|
return
|
|
endif
|
|
|
|
call s:ClearErrorHighlights()
|
|
|
|
let force_callback = a:0 && a:1
|
|
for item in a:errors
|
|
let group = item['type'] == 'E' ? 'SpellBad' : 'SpellCap'
|
|
if item['col'] && !force_callback
|
|
let lastcol = col([item['lnum'], '$'])
|
|
let lcol = min([lastcol, item['col']])
|
|
call s:HighlightError(group, '\%'.item['lnum'].'l\%'.lcol.'c')
|
|
else
|
|
let term = a:termfunc(item)
|
|
if len(term) > 0
|
|
call s:HighlightError(group, '\%' . item['lnum'] . 'l' . term)
|
|
endif
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
"take a list of errors and add default values to them from a:options
|
|
function! SyntasticAddToErrors(errors, options)
|
|
for i in range(0, len(a:errors)-1)
|
|
for key in keys(a:options)
|
|
if empty(a:errors[i][key])
|
|
let a:errors[i][key] = a:options[key]
|
|
endif
|
|
endfor
|
|
endfor
|
|
return a:errors
|
|
endfunction
|
|
|
|
" vim: set et sts=4 sw=4:
|