From 0deeefd08e9f1f4338776c2f58c045fbc6e75f1c Mon Sep 17 00:00:00 2001 From: LCD 47 Date: Sun, 7 Apr 2013 22:10:26 +0300 Subject: [PATCH] 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. --- plugin/syntastic.vim | 120 ++++-------------------------- plugin/syntastic/balloons.vim | 47 ++++++++++++ plugin/syntastic/highlighting.vim | 63 ++++++++++++++++ plugin/syntastic/loclist.vim | 25 ++++++- plugin/syntastic/notifiers.vim | 34 +++++++++ plugin/syntastic/signer.vim | 79 ++++++++++++-------- 6 files changed, 229 insertions(+), 139 deletions(-) create mode 100644 plugin/syntastic/balloons.vim create mode 100644 plugin/syntastic/highlighting.vim create mode 100644 plugin/syntastic/notifiers.vim diff --git a/plugin/syntastic.vim b/plugin/syntastic.vim index 780977b5..74d109b0 100644 --- a/plugin/syntastic.vim +++ b/plugin/syntastic.vim @@ -21,22 +21,6 @@ runtime! plugin/syntastic/*.vim 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") let g:syntastic_echo_current_error = 1 endif @@ -70,9 +54,9 @@ if !exists("g:syntastic_loc_list_height") endif let s:registry = g:SyntasticRegistry.Instance() -let s:signer = g:SyntasticSigner.New() -call s:signer.SetUpSignStyles() +let s:notifiers = g:SyntasticNotifiers.New() let s:modemap = g:SyntasticModeMap.Instance() +let s:old_line = -1 function! s:CompleteCheckerName(argLead, cmdLine, cursorPos) let checker_names = [] @@ -93,7 +77,7 @@ highlight link SyntasticWarning SpellCap augroup syntastic if g:syntastic_echo_current_error - autocmd cursormoved * call s:EchoCurrentError() + autocmd CursorMoved * call s:EchoCurrentError() endif autocmd BufReadPost * if g:syntastic_check_on_open | call s:UpdateErrors(1) | endif @@ -118,17 +102,7 @@ function! s:UpdateErrors(auto_invoked, ...) endif end - if g:syntastic_enable_balloons - 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 + call s:notifiers.refresh(s:LocList()) let loclist = s:LocList() if g:syntastic_always_populate_loc_list && loclist.hasErrorsOrWarningsToDisplay() @@ -172,6 +146,7 @@ endfunction "clear the loc list for the buffer function! s:ClearCache() unlet! b:syntastic_loclist + let s:old_line = -1 endfunction function! s:CurrentFiletypes() @@ -242,58 +217,6 @@ function! s:HideLocList() endif 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 function! s:WideMsg(msg) let old_ruler = &ruler @@ -321,23 +244,18 @@ endfunction "echo out the first error we find for the current line in the cmd window function! s:EchoCurrentError() + let l = line('.') + if l == s:old_line + return + endif + let s:old_line = l + let loclist = s:LocList() - "If we have an error or warning at the current line, show it - let errors = loclist.filter({'lnum': line("."), "type": 'e'}) - let warnings = loclist.filter({'lnum': line("."), "type": 'w'}) - - 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 + let messages = loclist.messages() + if has_key(messages, l) + return s:WideMsg(messages[l]) + else echo - let b:syntastic_echoing_error = 0 endif endfunction @@ -484,14 +402,6 @@ function! SyntasticMake(options) 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 - "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) diff --git a/plugin/syntastic/balloons.vim b/plugin/syntastic/balloons.vim new file mode 100644 index 00000000..78b1ceb6 --- /dev/null +++ b/plugin/syntastic/balloons.vim @@ -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: diff --git a/plugin/syntastic/highlighting.vim b/plugin/syntastic/highlighting.vim new file mode 100644 index 00000000..61ce5b2c --- /dev/null +++ b/plugin/syntastic/highlighting.vim @@ -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: diff --git a/plugin/syntastic/loclist.vim b/plugin/syntastic/loclist.vim index 8f6f8037..db581028 100644 --- a/plugin/syntastic/loclist.vim +++ b/plugin/syntastic/loclist.vim @@ -63,13 +63,36 @@ function! g:SyntasticLoclist.errors() return self._cachedErrors endfunction -function! SyntasticLoclist.warnings() +function! g:SyntasticLoclist.warnings() if !exists("self._cachedWarnings") let self._cachedWarnings = self.filter({'type': "W"}) endif return self._cachedWarnings 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 "e.g. " .filter({'bufnr': 10, 'type': 'e'}) diff --git a/plugin/syntastic/notifiers.vim b/plugin/syntastic/notifiers.vim new file mode 100644 index 00000000..c865d3f4 --- /dev/null +++ b/plugin/syntastic/notifiers.vim @@ -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: diff --git a/plugin/syntastic/signer.vim b/plugin/syntastic/signer.vim index 0aefdc03..4388af5d 100644 --- a/plugin/syntastic/signer.vim +++ b/plugin/syntastic/signer.vim @@ -1,7 +1,7 @@ -if exists("g:loaded_syntastic_signer") +if exists("g:loaded_syntastic_notifier_signs") finish endif -let g:loaded_syntastic_signer=1 +let g:loaded_syntastic_notifier_signs=1 if !exists("g:syntastic_enable_signs") let g:syntastic_enable_signs = 1 @@ -28,21 +28,40 @@ if !has('signs') 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) +" 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 -let g:SyntasticSigner = {} +let g:SyntasticNotifierSigns = {} -"Public methods {{{1 -function! g:SyntasticSigner.New() +let s:setup_done = 0 + +" Public methods {{{1 + +function! g:SyntasticNotifierSigns.New() let newObj = copy(self) + + if !s:setup_done + call self._setup() + let s:setup_done = 1 + endif + return newObj 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 !hlexists('SyntasticErrorSign') highlight link SyntasticErrorSign error @@ -63,26 +82,20 @@ function! g:SyntasticSigner.SetUpSignStyles() highlight link SyntasticStyleWarningLine SyntasticWarningLine endif - "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 SyntasticWarning text='.g:syntastic_warning_symbol.' 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' + " 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 SyntasticWarning text=' . g:syntastic_warning_symbol . + \ ' 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 endfunction -"update the error signs -function! g:SyntasticSigner.refreshSigns(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) +" Place signs by all syntax errs in the buffer +function! g:SyntasticNotifierSigns._signErrors(loclist) let loclist = a:loclist if loclist.hasErrorsOrWarningsToDisplay() @@ -111,9 +124,9 @@ function! g:SyntasticSigner._signErrors(loclist) endif endfunction -"return true if the given error item is a warning that, if signed, would -"potentially mask an error if displayed at the same time -function! g:SyntasticSigner._warningMasksError(error, llist) +" Return true if the given error item is a warning that, if signed, would +" potentially mask an error if displayed at the same time +function! g:SyntasticNotifierSigns._warningMasksError(error, llist) if a:error['type'] !=? 'w' return 0 endif @@ -122,16 +135,16 @@ function! g:SyntasticSigner._warningMasksError(error, llist) return len(loclist.filter({ 'type': "E", 'lnum': a:error['lnum'] })) > 0 endfunction -"remove the signs with the given ids from this buffer -function! g:SyntasticSigner._removeSigns(ids) +" Remove the signs with the given ids from this buffer +function! g:SyntasticNotifierSigns._removeSigns(ids) for i in a:ids exec "sign unplace " . i call remove(self._bufSignIds(), index(self._bufSignIds(), i)) endfor endfunction -"get all the ids of the SyntaxError signs in the buffer -function! g:SyntasticSigner._bufSignIds() +" Get all the ids of the SyntaxError signs in the buffer +function! g:SyntasticNotifierSigns._bufSignIds() if !exists("b:syntastic_sign_ids") let b:syntastic_sign_ids = [] endif