1# Copyright (C) 2016 YouCompleteMe contributors 2# 3# This file is part of YouCompleteMe. 4# 5# YouCompleteMe is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# YouCompleteMe is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>. 17 18import re 19 20 21class DiagnosticFilter: 22 def __init__( self, config_or_filters ): 23 self._filters : list = config_or_filters 24 25 26 def IsAllowed( self, diagnostic ): 27 return not any( filterMatches( diagnostic ) 28 for filterMatches in self._filters ) 29 30 31 @staticmethod 32 def CreateFromOptions( user_options ): 33 all_filters = user_options[ 'filter_diagnostics' ] 34 compiled_by_type = {} 35 for type_spec, filter_value in all_filters.items(): 36 filetypes = type_spec.split( ',' ) 37 for filetype in filetypes: 38 compiled_by_type[ filetype ] = _CompileFilters( filter_value ) 39 40 return _MasterDiagnosticFilter( compiled_by_type ) 41 42 43class _MasterDiagnosticFilter: 44 45 def __init__( self, all_filters ): 46 self._all_filters = all_filters 47 self._cache = {} 48 49 50 def SubsetForTypes( self, filetypes ): 51 # check cache 52 cache_key = ','.join( filetypes ) 53 cached = self._cache.get( cache_key ) 54 if cached is not None: 55 return cached 56 57 # build a new DiagnosticFilter merging all filters 58 # for the provided filetypes 59 spec = [] 60 for filetype in filetypes: 61 type_specific = self._all_filters.get( filetype, [] ) 62 spec.extend( type_specific ) 63 64 new_filter = DiagnosticFilter( spec ) 65 self._cache[ cache_key ] = new_filter 66 return new_filter 67 68 69def _ListOf( config_entry ): 70 if isinstance( config_entry, list ): 71 return config_entry 72 73 return [ config_entry ] 74 75 76def CompileRegex( raw_regex ): 77 pattern = re.compile( raw_regex, re.IGNORECASE ) 78 79 def FilterRegex( diagnostic ): 80 return pattern.search( diagnostic[ 'text' ] ) is not None 81 82 return FilterRegex 83 84 85def CompileLevel( level ): 86 # valid kinds are WARNING and ERROR; 87 # expected input levels are `warning` and `error` 88 # NOTE: we don't validate the input... 89 expected_kind = level.upper() 90 91 def FilterLevel( diagnostic ): 92 return diagnostic[ 'kind' ] == expected_kind 93 94 return FilterLevel 95 96 97FILTER_COMPILERS = { 'regex' : CompileRegex, 98 'level' : CompileLevel } 99 100 101def _CompileFilters( config ): 102 """Given a filter config dictionary, return a list of compiled filters""" 103 filters = [] 104 105 for filter_type, filter_pattern in config.items(): 106 compiler = FILTER_COMPILERS.get( filter_type ) 107 108 if compiler is not None: 109 for filter_config in _ListOf( filter_pattern ): 110 compiledFilter = compiler( filter_config ) 111 filters.append( compiledFilter ) 112 113 return filters 114