Notifiers refactor.

Creates a notifier class.
Changes the existing signer class to fit the new notifier.
Moves balloons and highlighting to their own classes.
Caches and speeds up EchoCurrentError().
Adds all relevant messages to balloons rather than using the first one.
Fixes yet another (minor) bug related to g:syntastic_quiet_warnings.
This commit is contained in:
LCD 47 2013-04-07 22:10:26 +03:00
parent 01232979aa
commit 0deeefd08e
6 changed files with 229 additions and 139 deletions

View File

@ -21,22 +21,6 @@ runtime! plugin/syntastic/*.vim
let s:running_windows = has("win16") || has("win32") let s:running_windows = has("win16") || has("win32")
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
" highlighting requires getmatches introduced in 7.1.040
if v:version < 701 || (v:version == 701 && !has('patch040'))
let g:syntastic_enable_highlighting = 0
endif
if !exists("g:syntastic_echo_current_error") if !exists("g:syntastic_echo_current_error")
let g:syntastic_echo_current_error = 1 let g:syntastic_echo_current_error = 1
endif endif
@ -70,9 +54,9 @@ if !exists("g:syntastic_loc_list_height")
endif endif
let s:registry = g:SyntasticRegistry.Instance() let s:registry = g:SyntasticRegistry.Instance()
let s:signer = g:SyntasticSigner.New() let s:notifiers = g:SyntasticNotifiers.New()
call s:signer.SetUpSignStyles()
let s:modemap = g:SyntasticModeMap.Instance() let s:modemap = g:SyntasticModeMap.Instance()
let s:old_line = -1
function! s:CompleteCheckerName(argLead, cmdLine, cursorPos) function! s:CompleteCheckerName(argLead, cmdLine, cursorPos)
let checker_names = [] let checker_names = []
@ -93,7 +77,7 @@ highlight link SyntasticWarning SpellCap
augroup syntastic augroup syntastic
if g:syntastic_echo_current_error if g:syntastic_echo_current_error
autocmd cursormoved * call s:EchoCurrentError() autocmd CursorMoved * call s:EchoCurrentError()
endif endif
autocmd BufReadPost * if g:syntastic_check_on_open | call s:UpdateErrors(1) | endif autocmd BufReadPost * if g:syntastic_check_on_open | call s:UpdateErrors(1) | endif
@ -118,17 +102,7 @@ function! s:UpdateErrors(auto_invoked, ...)
endif endif
end end
if g:syntastic_enable_balloons call s:notifiers.refresh(s:LocList())
call s:RefreshBalloons()
endif
if g:syntastic_enable_signs
call s:signer.refreshSigns(s:LocList())
endif
if g:syntastic_enable_highlighting
call s:HighlightErrors()
endif
let loclist = s:LocList() let loclist = s:LocList()
if g:syntastic_always_populate_loc_list && loclist.hasErrorsOrWarningsToDisplay() if g:syntastic_always_populate_loc_list && loclist.hasErrorsOrWarningsToDisplay()
@ -172,6 +146,7 @@ endfunction
"clear the loc list for the buffer "clear the loc list for the buffer
function! s:ClearCache() function! s:ClearCache()
unlet! b:syntastic_loclist unlet! b:syntastic_loclist
let s:old_line = -1
endfunction endfunction
function! s:CurrentFiletypes() function! s:CurrentFiletypes()
@ -242,58 +217,6 @@ function! s:HideLocList()
endif endif
endfunction endfunction
"highlight the current errors using matchadd()
"
"The function `Syntastic_{filetype}_{checker}_GetHighlightRegex` is used
"to override default highlighting. This function must take one arg (an
"error item) and return a regex to match that item in the buffer.
function! s:HighlightErrors()
call s:ClearErrorHighlights()
let loclist = s:LocList()
let fts = substitute(&ft, '-', '_', 'g')
for ft in split(fts, '\.')
for item in loclist.filteredRaw()
let group = item['type'] == 'E' ? 'SyntasticError' : 'SyntasticWarning'
if has_key(item, 'hl')
call matchadd(group, '\%' . item['lnum'] . 'l' . item['hl'])
elseif get(item, 'col')
let lastcol = col([item['lnum'], '$'])
let lcol = min([lastcol, item['col']])
"a bug in vim can sometimes cause there to be no 'vcol' key,
"so check for its existence
let coltype = has_key(item, 'vcol') && item['vcol'] ? 'v' : 'c'
call matchadd(group, '\%' . item['lnum'] . 'l\%' . lcol . coltype)
endif
endfor
endfor
endfunction
"remove all error highlights from the window
function! s:ClearErrorHighlights()
for match in getmatches()
if stridx(match['group'], 'Syntastic') == 0
call matchdelete(match['id'])
endif
endfor
endfunction
"set up error ballons for the current set of errors
function! s:RefreshBalloons()
let b:syntastic_balloons = {}
let loclist = s:LocList()
if loclist.hasErrorsOrWarningsToDisplay()
for i in loclist.filteredRaw()
let b:syntastic_balloons[i['lnum']] = i['text']
endfor
set beval bexpr=SyntasticErrorBalloonExpr()
endif
endfunction
"print as much of a:msg as possible without "Press Enter" prompt appearing "print as much of a:msg as possible without "Press Enter" prompt appearing
function! s:WideMsg(msg) function! s:WideMsg(msg)
let old_ruler = &ruler let old_ruler = &ruler
@ -321,23 +244,18 @@ endfunction
"echo out the first error we find for the current line in the cmd window "echo out the first error we find for the current line in the cmd window
function! s:EchoCurrentError() function! s:EchoCurrentError()
let l = line('.')
if l == s:old_line
return
endif
let s:old_line = l
let loclist = s:LocList() let loclist = s:LocList()
"If we have an error or warning at the current line, show it let messages = loclist.messages()
let errors = loclist.filter({'lnum': line("."), "type": 'e'}) if has_key(messages, l)
let warnings = loclist.filter({'lnum': line("."), "type": 'w'}) return s:WideMsg(messages[l])
else
let b:syntastic_echoing_error = len(errors) || len(warnings)
if len(errors)
return s:WideMsg(errors[0]['text'])
endif
if len(warnings)
return s:WideMsg(warnings[0]['text'])
endif
"Otherwise, clear the status line
if b:syntastic_echoing_error
echo echo
let b:syntastic_echoing_error = 0
endif endif
endfunction endfunction
@ -484,14 +402,6 @@ function! SyntasticMake(options)
return errors return errors
endfunction 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
"take a list of errors and add default values to them from a:options "take a list of errors and add default values to them from a:options
function! SyntasticAddToErrors(errors, options) function! SyntasticAddToErrors(errors, options)
for i in range(0, len(a:errors)-1) for i in range(0, len(a:errors)-1)

View File

@ -0,0 +1,47 @@
if exists("g:loaded_syntastic_notifier_balloons")
finish
endif
let g:loaded_syntastic_notifier_balloons=1
if !exists("g:syntastic_enable_balloons")
let g:syntastic_enable_balloons = 1
endif
if !has('balloon_eval')
let g:syntastic_enable_balloons = 0
endif
let g:SyntasticNotifierBalloons = {}
" Public methods {{{1
function! g:SyntasticNotifierBalloons.New()
let newObj = copy(self)
return newObj
endfunction
" Update the error balloons
function! g:SyntasticNotifierBalloons.refresh(loclist)
let b:syntastic_balloons = {}
if a:loclist.hasErrorsOrWarningsToDisplay()
for i in a:loclist.filteredRaw()
if has_key(b:syntastic_balloons, i['lnum'])
let b:syntastic_balloons[i['lnum']] .= "\n" . i['text']
else
let b:syntastic_balloons[i['lnum']] = i['text']
endif
endfor
set beval bexpr=SyntasticNotifierBalloonsExpr()
endif
endfunction
" Private functions {{{1
function! SyntasticNotifierBalloonsExpr()
if !exists('b:syntastic_balloons')
return ''
endif
return get(b:syntastic_balloons, v:beval_lnum, '')
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View File

@ -0,0 +1,63 @@
if exists("g:loaded_syntastic_notifier_highlighting")
finish
endif
let g:loaded_syntastic_notifier_highlighting=1
if !exists("g:syntastic_enable_highlighting")
let g:syntastic_enable_highlighting = 1
endif
" Highlighting requires getmatches introduced in 7.1.040
if v:version < 701 || (v:version == 701 && !has('patch040'))
let g:syntastic_enable_highlighting = 0
endif
let g:SyntasticNotifierHighlighting = {}
" Public methods {{{1
function! g:SyntasticNotifierHighlighting.New()
let newObj = copy(self)
return newObj
endfunction
" The function `Syntastic_{filetype}_{checker}_GetHighlightRegex` is used
" to override default highlighting. This function must take one arg (an
" error item) and return a regex to match that item in the buffer.
function! g:SyntasticNotifierHighlighting.refresh(loclist)
call self._reset()
let fts = substitute(&ft, '-', '_', 'g')
for ft in split(fts, '\.')
for item in a:loclist.filteredRaw()
let group = item['type'] == 'E' ? 'SyntasticError' : 'SyntasticWarning'
if has_key(item, 'hl')
call matchadd(group, '\%' . item['lnum'] . 'l' . item['hl'])
elseif get(item, 'col')
let lastcol = col([item['lnum'], '$'])
let lcol = min([lastcol, item['col']])
" a bug in vim can sometimes cause there to be no 'vcol' key,
" so check for its existence
let coltype = has_key(item, 'vcol') && item['vcol'] ? 'v' : 'c'
call matchadd(group, '\%' . item['lnum'] . 'l\%' . lcol . coltype)
endif
endfor
endfor
endfunction
" Private functions {{{1
" Remove all error highlights from the window
function! g:SyntasticNotifierHighlighting._reset()
for match in getmatches()
if stridx(match['group'], 'Syntastic') == 0
call matchdelete(match['id'])
endif
endfor
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View File

@ -63,13 +63,36 @@ function! g:SyntasticLoclist.errors()
return self._cachedErrors return self._cachedErrors
endfunction endfunction
function! SyntasticLoclist.warnings() function! g:SyntasticLoclist.warnings()
if !exists("self._cachedWarnings") if !exists("self._cachedWarnings")
let self._cachedWarnings = self.filter({'type': "W"}) let self._cachedWarnings = self.filter({'type': "W"})
endif endif
return self._cachedWarnings return self._cachedWarnings
endfunction endfunction
" cache used by EchoCurrentError()
function! g:SyntasticLoclist.messages()
if !exists("self._cachedMessages")
let self._cachedMessages = {}
for e in self.errors()
if !has_key(self._cachedMessages, e['lnum'])
let self._cachedMessages[e['lnum']] = e['text']
endif
endfor
if !self._quietWarnings
for e in self.warnings()
if !has_key(self._cachedMessages, e['lnum'])
let self._cachedMessages[e['lnum']] = e['text']
endif
endfor
endif
endif
return self._cachedMessages
endfunction
"Filter the list and return new native loclist "Filter the list and return new native loclist
"e.g. "e.g.
" .filter({'bufnr': 10, 'type': 'e'}) " .filter({'bufnr': 10, 'type': 'e'})

View File

@ -0,0 +1,34 @@
if exists("g:loaded_syntastic_notifiers")
finish
endif
let g:loaded_syntastic_notifiers=1
let g:SyntasticNotifiers = {}
let s:notifier_types = ['signs', 'balloons', 'highlighting']
" Public methods {{{1
function! g:SyntasticNotifiers.New()
let newObj = copy(self)
let newObj._notifier = {}
for type in s:notifier_types
let class = substitute(type, '.*', 'SyntasticNotifier\u&', '')
let newObj._notifier[type] = g:{class}.New()
endfor
let newObj._enabled_types = copy(s:notifier_types)
return newObj
endfunction
function! g:SyntasticNotifiers.refresh(loclist)
for type in self._enabled_types
if ( exists('b:syntastic_enable_'.type) ? b:syntastic_enable_{type} : g:syntastic_enable_{type} )
call self._notifier[type].refresh(a:loclist)
endif
endfor
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View File

@ -1,7 +1,7 @@
if exists("g:loaded_syntastic_signer") if exists("g:loaded_syntastic_notifier_signs")
finish finish
endif endif
let g:loaded_syntastic_signer=1 let g:loaded_syntastic_notifier_signs=1
if !exists("g:syntastic_enable_signs") if !exists("g:syntastic_enable_signs")
let g:syntastic_enable_signs = 1 let g:syntastic_enable_signs = 1
@ -28,21 +28,40 @@ if !has('signs')
endif endif
"start counting sign ids at 5000, start here to hopefully avoid conflicting " 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 " with any other code that places signs (not sure if this precaution is
"actually needed) " actually needed)
let s:first_sign_id = 5000 let s:first_sign_id = 5000
let s:next_sign_id = s:first_sign_id let s:next_sign_id = s:first_sign_id
let g:SyntasticSigner = {} let g:SyntasticNotifierSigns = {}
"Public methods {{{1 let s:setup_done = 0
function! g:SyntasticSigner.New()
" Public methods {{{1
function! g:SyntasticNotifierSigns.New()
let newObj = copy(self) let newObj = copy(self)
if !s:setup_done
call self._setup()
let s:setup_done = 1
endif
return newObj return newObj
endfunction endfunction
function! g:SyntasticSigner.SetUpSignStyles() " Update the error signs
function! g:SyntasticNotifierSigns.refresh(loclist)
let old_signs = copy(self._bufSignIds())
call self._signErrors(a:loclist)
call self._removeSigns(old_signs)
let s:first_sign_id = s:next_sign_id
endfunction
" Private methods {{{1
function! g:SyntasticNotifierSigns._setup()
if has('signs') if has('signs')
if !hlexists('SyntasticErrorSign') if !hlexists('SyntasticErrorSign')
highlight link SyntasticErrorSign error highlight link SyntasticErrorSign error
@ -63,26 +82,20 @@ function! g:SyntasticSigner.SetUpSignStyles()
highlight link SyntasticStyleWarningLine SyntasticWarningLine highlight link SyntasticStyleWarningLine SyntasticWarningLine
endif endif
"define the signs used to display syntax and style errors/warns " define the signs used to display syntax and style errors/warns
exe 'sign define SyntasticError text='.g:syntastic_error_symbol.' texthl=SyntasticErrorSign linehl=SyntasticErrorLine' exe 'sign define SyntasticError text=' . g:syntastic_error_symbol .
exe 'sign define SyntasticWarning text='.g:syntastic_warning_symbol.' texthl=SyntasticWarningSign linehl=SyntasticWarningLine' \ ' texthl=SyntasticErrorSign linehl=SyntasticErrorLine'
exe 'sign define SyntasticStyleError text='.g:syntastic_style_error_symbol.' texthl=SyntasticStyleErrorSign linehl=SyntasticStyleErrorLine' exe 'sign define SyntasticWarning text=' . g:syntastic_warning_symbol .
exe 'sign define SyntasticStyleWarning text='.g:syntastic_style_warning_symbol.' texthl=SyntasticStyleWarningSign linehl=SyntasticStyleWarningLine' \ ' texthl=SyntasticWarningSign linehl=SyntasticWarningLine'
exe 'sign define SyntasticStyleError text=' . g:syntastic_style_error_symbol .
\ ' texthl=SyntasticStyleErrorSign linehl=SyntasticStyleErrorLine'
exe 'sign define SyntasticStyleWarning text=' . g:syntastic_style_warning_symbol .
\ ' texthl=SyntasticStyleWarningSign linehl=SyntasticStyleWarningLine'
endif endif
endfunction endfunction
"update the error signs " Place signs by all syntax errs in the buffer
function! g:SyntasticSigner.refreshSigns(loclist) function! g:SyntasticNotifierSigns._signErrors(loclist)
let old_signs = copy(self._bufSignIds())
call self._signErrors(a:loclist)
call self._removeSigns(old_signs)
let s:first_sign_id = s:next_sign_id
endfunction
"Private methods {{{1
"
"place signs by all syntax errs in the buffer
function! g:SyntasticSigner._signErrors(loclist)
let loclist = a:loclist let loclist = a:loclist
if loclist.hasErrorsOrWarningsToDisplay() if loclist.hasErrorsOrWarningsToDisplay()
@ -111,9 +124,9 @@ function! g:SyntasticSigner._signErrors(loclist)
endif endif
endfunction endfunction
"return true if the given error item is a warning that, if signed, would " Return true if the given error item is a warning that, if signed, would
"potentially mask an error if displayed at the same time " potentially mask an error if displayed at the same time
function! g:SyntasticSigner._warningMasksError(error, llist) function! g:SyntasticNotifierSigns._warningMasksError(error, llist)
if a:error['type'] !=? 'w' if a:error['type'] !=? 'w'
return 0 return 0
endif endif
@ -122,16 +135,16 @@ function! g:SyntasticSigner._warningMasksError(error, llist)
return len(loclist.filter({ 'type': "E", 'lnum': a:error['lnum'] })) > 0 return len(loclist.filter({ 'type': "E", 'lnum': a:error['lnum'] })) > 0
endfunction endfunction
"remove the signs with the given ids from this buffer " Remove the signs with the given ids from this buffer
function! g:SyntasticSigner._removeSigns(ids) function! g:SyntasticNotifierSigns._removeSigns(ids)
for i in a:ids for i in a:ids
exec "sign unplace " . i exec "sign unplace " . i
call remove(self._bufSignIds(), index(self._bufSignIds(), i)) call remove(self._bufSignIds(), index(self._bufSignIds(), i))
endfor endfor
endfunction endfunction
"get all the ids of the SyntaxError signs in the buffer " Get all the ids of the SyntaxError signs in the buffer
function! g:SyntasticSigner._bufSignIds() function! g:SyntasticNotifierSigns._bufSignIds()
if !exists("b:syntastic_sign_ids") if !exists("b:syntastic_sign_ids")
let b:syntastic_sign_ids = [] let b:syntastic_sign_ids = []
endif endif