Refactor filter spec to filter_diagnostics; update tests, README
This commit is contained in:
parent
3869830a65
commit
bb793826b6
32
README.md
32
README.md
@ -1803,6 +1803,38 @@ Default: `1`
|
|||||||
let g:ycm_echo_current_diagnostic = 1
|
let g:ycm_echo_current_diagnostic = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### The `g:ycm_filter_diagnostics` option
|
||||||
|
|
||||||
|
This option controls which diagnostics will be rendered by YCM. This option holds
|
||||||
|
a dictionary of key-values, where the keys are Vim's filetype strings delimited by
|
||||||
|
commas and values are dictionaries describing the filter.
|
||||||
|
|
||||||
|
A filter is a dictionary of key-values, where the keys are the type of filter,
|
||||||
|
and the value is a list of arguments to that filter. In the case of just a single
|
||||||
|
item in the list, you may omit the brackets and just provide the argument directly.
|
||||||
|
If any filter matches a diagnostic, it will be dropped and YCM will not render it.
|
||||||
|
|
||||||
|
The following filter types are supported:
|
||||||
|
|
||||||
|
- "regex": Accepts a string [regular expression][python-re]. This type matches
|
||||||
|
when the regex (treated as case-insensitive) is found in the diagnostic text.
|
||||||
|
- "level": Accepts a string level, either "warning" or "error." This type matches
|
||||||
|
when the diagnostic has the same level.
|
||||||
|
|
||||||
|
NOTE: The regex syntax is **NOT** Vim's, it's [Python's][python-re].
|
||||||
|
|
||||||
|
Default: `{}`
|
||||||
|
|
||||||
|
```viml
|
||||||
|
let g:ycm_filter_diagnostics = {
|
||||||
|
\ "java": {
|
||||||
|
\ "regex": [ ".*taco.*", ... ],
|
||||||
|
\ "level": "error",
|
||||||
|
\ ...
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
```
|
||||||
|
|
||||||
### The `g:ycm_always_populate_location_list` option
|
### The `g:ycm_always_populate_location_list` option
|
||||||
|
|
||||||
When this option is set, YCM will populate the location list automatically every
|
When this option is set, YCM will populate the location list automatically every
|
||||||
|
@ -121,10 +121,6 @@ let g:ycm_warning_symbol =
|
|||||||
\ get( g:, 'ycm_warning_symbol',
|
\ get( g:, 'ycm_warning_symbol',
|
||||||
\ get( g:, 'syntastic_warning_symbol', '>>' ) )
|
\ get( g:, 'syntastic_warning_symbol', '>>' ) )
|
||||||
|
|
||||||
let g:ycm_quiet_messages =
|
|
||||||
\ get( g:, 'ycm_quiet_messages',
|
|
||||||
\ get( g:, 'syntastic_quiet_messages', {} ) )
|
|
||||||
|
|
||||||
let g:ycm_goto_buffer_command =
|
let g:ycm_goto_buffer_command =
|
||||||
\ get( g:, 'ycm_goto_buffer_command', 'same-buffer' )
|
\ get( g:, 'ycm_goto_buffer_command', 'same-buffer' )
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
from future.utils import iterkeys
|
from future.utils import iterkeys, iteritems
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
@ -32,23 +32,18 @@ class DiagnosticFilter( object ):
|
|||||||
self._filters = []
|
self._filters = []
|
||||||
|
|
||||||
for filter_type in iterkeys( config ):
|
for filter_type in iterkeys( config ):
|
||||||
wrapper = _AsIs
|
|
||||||
actual_filter_type = filter_type
|
actual_filter_type = filter_type
|
||||||
|
|
||||||
if filter_type[ 0 ] == '!':
|
|
||||||
wrapper = _Not
|
|
||||||
filter_type = filter_type[ 1 : ]
|
|
||||||
compiler = FILTER_COMPILERS.get( filter_type )
|
compiler = FILTER_COMPILERS.get( filter_type )
|
||||||
|
|
||||||
if compiler is not None:
|
if compiler is not None:
|
||||||
for filter_config in _ListOf( config[ actual_filter_type ] ):
|
for filter_config in _ListOf( config[ actual_filter_type ] ):
|
||||||
fn = wrapper( compiler( filter_config ) )
|
fn = compiler( filter_config )
|
||||||
self._filters.append( fn )
|
self._filters.append( fn )
|
||||||
|
|
||||||
|
|
||||||
def IsAllowed( self, diagnostic ):
|
def IsAllowed( self, diagnostic ):
|
||||||
# NOTE: a diagnostic IsAllowed() ONLY if
|
# NOTE: a diagnostic IsAllowed() ONLY if
|
||||||
# no filters match it
|
# NO filters match it
|
||||||
for f in self._filters:
|
for f in self._filters:
|
||||||
if f( diagnostic ):
|
if f( diagnostic ):
|
||||||
return False
|
return False
|
||||||
@ -58,30 +53,34 @@ class DiagnosticFilter( object ):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_filetype( user_options, filetypes ):
|
def from_filetype( user_options, filetypes ):
|
||||||
base = dict( user_options.get( 'quiet_messages', {} ) )
|
spec = {}
|
||||||
|
all_filters = dict( user_options.get( 'filter_diagnostics', {} ) )
|
||||||
|
for typeSpec, filterValue in iteritems( dict( all_filters ) ):
|
||||||
|
if typeSpec.find(',') != -1:
|
||||||
|
for ft in typeSpec.split(','):
|
||||||
|
all_filters[ ft ] = filterValue
|
||||||
|
|
||||||
for filetype in filetypes:
|
for filetype in filetypes:
|
||||||
type_specific = user_options.get( filetype + '_quiet_messages', {} )
|
type_specific = all_filters.get( filetype, {} )
|
||||||
base.update( type_specific )
|
spec = _Merge( spec, type_specific )
|
||||||
return DiagnosticFilter( base )
|
return DiagnosticFilter( spec )
|
||||||
|
|
||||||
|
|
||||||
def _ListOf( config_entry ):
|
def _ListOf( config_entry ):
|
||||||
if isinstance( config_entry, list ):
|
if isinstance( config_entry, list ):
|
||||||
return config_entry
|
return config_entry
|
||||||
|
|
||||||
|
if config_entry is None:
|
||||||
|
return []
|
||||||
|
|
||||||
return [ config_entry ]
|
return [ config_entry ]
|
||||||
|
|
||||||
|
|
||||||
def _AsIs( fn ):
|
def _Merge( into, other ):
|
||||||
return fn
|
for k in iterkeys( other ):
|
||||||
|
into[k] = _ListOf( into.get( k ) ) + _ListOf( other[ k ] )
|
||||||
|
|
||||||
|
return into
|
||||||
def _Not( fn ):
|
|
||||||
def Inverted( diagnostic ):
|
|
||||||
return not fn( diagnostic )
|
|
||||||
|
|
||||||
return Inverted
|
|
||||||
|
|
||||||
|
|
||||||
def _CompileRegex( raw_regex ):
|
def _CompileRegex( raw_regex ):
|
||||||
@ -95,9 +94,9 @@ def _CompileRegex( raw_regex ):
|
|||||||
|
|
||||||
def _CompileLevel( level ):
|
def _CompileLevel( level ):
|
||||||
# valid kinds are WARNING and ERROR;
|
# valid kinds are WARNING and ERROR;
|
||||||
# expected input levels are `warnings` and `errors`
|
# expected input levels are `warning` and `error`
|
||||||
# NB: we don't validate the input...
|
# NB: we don't validate the input...
|
||||||
expected_kind = level.upper()[ : -1 ]
|
expected_kind = level.upper()
|
||||||
|
|
||||||
def FilterLevel( diagnostic ):
|
def FilterLevel( diagnostic ):
|
||||||
return diagnostic[ 'kind' ] == expected_kind
|
return diagnostic[ 'kind' ] == expected_kind
|
||||||
|
@ -45,54 +45,25 @@ def _assert_rejects( filter, text ):
|
|||||||
_assert_accept_equals( filter, text, False )
|
_assert_accept_equals( filter, text, False )
|
||||||
|
|
||||||
|
|
||||||
class ConfigPriority_test():
|
def _JavaFilter( config ):
|
||||||
|
return { 'filter_diagnostics' : { 'java': config } }
|
||||||
|
|
||||||
def ConfigPriority_Global_test( self ):
|
|
||||||
opts = { 'quiet_messages': { 'regex': 'taco' } }
|
def RegexFilter_test():
|
||||||
|
opts = _JavaFilter( { 'regex' : 'taco' } )
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
_assert_rejects( f, 'This is a Taco' )
|
_assert_rejects( f, 'This is a Taco' )
|
||||||
_assert_accepts( f, 'This is a Burrito' )
|
_assert_accepts( f, 'This is a Burrito' )
|
||||||
|
|
||||||
|
|
||||||
def ConfigPriority_Filetype_test( self ):
|
|
||||||
opts = { 'quiet_messages' : {},
|
|
||||||
'java_quiet_messages' : { 'regex': 'taco' } }
|
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
|
||||||
|
|
||||||
_assert_rejects( f, 'This is a Taco' )
|
|
||||||
_assert_accepts( f, 'This is a Burrito' )
|
|
||||||
|
|
||||||
|
|
||||||
def ConfigPriority_FiletypeOverridesGlobal_test( self ):
|
|
||||||
# NB: if the filetype doesn't override the global,
|
|
||||||
# we would reject burrito and accept taco
|
|
||||||
opts = { 'quiet_messages' : { 'regex': 'burrito'},
|
|
||||||
'java_quiet_messages' : { 'regex': 'taco' } }
|
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
|
||||||
|
|
||||||
_assert_rejects( f, 'This is a Taco' )
|
|
||||||
_assert_accepts( f, 'This is a Burrito' )
|
|
||||||
|
|
||||||
|
|
||||||
def ConfigPriority_FiletypeDisablesGlobal_test( self ):
|
|
||||||
# NB: if the filetype doesn't override the global,
|
|
||||||
# we would reject burrito and accept taco
|
|
||||||
opts = { 'quiet_messages' : { 'regex': 'taco'},
|
|
||||||
'java_quiet_messages' : { 'regex': [] } }
|
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
|
||||||
|
|
||||||
_assert_accepts( f, 'This is a Taco' )
|
|
||||||
_assert_accepts( f, 'This is a Burrito' )
|
|
||||||
|
|
||||||
|
|
||||||
class ListOrSingle_test():
|
class ListOrSingle_test():
|
||||||
# NB: we already test the single config above
|
# NB: we already test the single config above
|
||||||
|
|
||||||
def ListOrSingle_SingleList_test( self ):
|
def ListOrSingle_SingleList_test( self ):
|
||||||
# NB: if the filetype doesn't override the global,
|
# NB: if the filetype doesn't override the global,
|
||||||
# we would reject burrito and accept taco
|
# we would reject burrito and accept taco
|
||||||
opts = { 'quiet_messages' : { 'regex': [ 'taco' ] } }
|
opts = _JavaFilter( { 'regex' : [ 'taco' ] } )
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
_assert_rejects( f, 'This is a Taco' )
|
_assert_rejects( f, 'This is a Taco' )
|
||||||
@ -102,38 +73,68 @@ class ListOrSingle_test():
|
|||||||
def ListOrSingle_MultiList_test( self ):
|
def ListOrSingle_MultiList_test( self ):
|
||||||
# NB: if the filetype doesn't override the global,
|
# NB: if the filetype doesn't override the global,
|
||||||
# we would reject burrito and accept taco
|
# we would reject burrito and accept taco
|
||||||
opts = { 'quiet_messages' : { 'regex': [ 'taco', 'burrito' ] } }
|
opts = _JavaFilter( { 'regex' : [ 'taco', 'burrito' ] } )
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
_assert_rejects( f, 'This is a Taco' )
|
_assert_rejects( f, 'This is a Taco' )
|
||||||
_assert_rejects( f, 'This is a Burrito' )
|
_assert_rejects( f, 'This is a Burrito' )
|
||||||
|
|
||||||
|
|
||||||
def Invert_test():
|
|
||||||
opts = { 'quiet_messages' : { '!regex': 'taco' } }
|
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
|
||||||
|
|
||||||
_assert_accepts( f, 'This is a Taco' )
|
|
||||||
_assert_rejects( f, 'This is a Burrito' )
|
|
||||||
|
|
||||||
|
|
||||||
class Level_test():
|
class Level_test():
|
||||||
|
|
||||||
def Level_warnings_test( self ):
|
def Level_warnings_test( self ):
|
||||||
opts = { 'quiet_messages' : { 'level': 'warnings' } }
|
opts = _JavaFilter( { 'level' : 'warning' } )
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
_assert_rejects( f, { 'text': 'This is an unimportant taco',
|
_assert_rejects( f, { 'text' : 'This is an unimportant taco',
|
||||||
'kind': 'WARNING' } )
|
'kind' : 'WARNING' } )
|
||||||
_assert_accepts( f, { 'text': 'This taco will be shown',
|
_assert_accepts( f, { 'text' : 'This taco will be shown',
|
||||||
'kind': 'ERROR' } )
|
'kind' : 'ERROR' } )
|
||||||
|
|
||||||
|
|
||||||
def Level_errors_test( self ):
|
def Level_errors_test( self ):
|
||||||
opts = { 'quiet_messages' : { 'level': 'errors' } }
|
opts = _JavaFilter( { 'level' : 'error' } )
|
||||||
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
_assert_accepts( f, { 'text': 'This is an IMPORTANT taco',
|
_assert_accepts( f, { 'text' : 'This is an IMPORTANT taco',
|
||||||
'kind': 'WARNING' } )
|
'kind' : 'WARNING' } )
|
||||||
_assert_rejects( f, { 'text': 'This taco will NOT be shown',
|
_assert_rejects( f, { 'text' : 'This taco will NOT be shown',
|
||||||
'kind': 'ERROR' } )
|
'kind' : 'ERROR' } )
|
||||||
|
|
||||||
|
|
||||||
|
def MultipleFilterTypesTypeTest_test():
|
||||||
|
|
||||||
|
opts = _JavaFilter( { 'regex' : '.*taco.*',
|
||||||
|
'level' : 'warning' } )
|
||||||
|
f = DiagnosticFilter.from_filetype( opts, [ 'java' ] )
|
||||||
|
|
||||||
|
_assert_rejects( f, { 'text' : 'This is an unimportant taco',
|
||||||
|
'kind' : 'WARNING' } )
|
||||||
|
_assert_rejects( f, { 'text' : 'This taco will NOT be shown',
|
||||||
|
'kind' : 'ERROR' } )
|
||||||
|
_assert_accepts( f, { 'text' : 'This burrito WILL be shown',
|
||||||
|
'kind' : 'ERROR' } )
|
||||||
|
|
||||||
|
|
||||||
|
def MergeMultipleFiletypes_test():
|
||||||
|
|
||||||
|
opts = { 'filter_diagnostics' : {
|
||||||
|
'java' : { 'regex' : '.*taco.*' },
|
||||||
|
'xml' : { 'regex' : '.*burrito.*' } } }
|
||||||
|
|
||||||
|
f = DiagnosticFilter.from_filetype( opts, [ 'java', 'xml' ] )
|
||||||
|
|
||||||
|
_assert_rejects( f, 'This is a Taco' )
|
||||||
|
_assert_rejects( f, 'This is a Burrito' )
|
||||||
|
_assert_accepts( f, 'This is some Nachos' )
|
||||||
|
|
||||||
|
|
||||||
|
def CommaSeparatedFiletypes_test():
|
||||||
|
|
||||||
|
opts = { 'filter_diagnostics' : {
|
||||||
|
'java,c,cs' : { 'regex' : '.*taco.*' } } }
|
||||||
|
|
||||||
|
f = DiagnosticFilter.from_filetype( opts, [ 'cs' ] )
|
||||||
|
|
||||||
|
_assert_rejects( f, 'This is a Taco' )
|
||||||
|
_assert_accepts( f, 'This is a Burrito' )
|
||||||
|
Loading…
Reference in New Issue
Block a user