Skip to content

Commit

Permalink
Auto merge of ycm-core#2377 - dhleong:dhleong/ycm-core#2021-quiet_mes…
Browse files Browse the repository at this point in the history
…sages, r=micbou

Implement ycm_quiet_messages options (See ycm-core#2021)

# PR Prelude

- [x] I have read and understood YCM's [CONTRIBUTING][cont] document.
- [x] I have read and understood YCM's [CODE_OF_CONDUCT][code] document.
- [x] I have included tests for the changes in my PR. If not, I have included a
  rationale for why I haven't.
- [x] **I understand my PR may be closed if it becomes obvious I didn't
  actually perform all of these steps.**

# Why this change is necessary and useful

See issue ycm-core#2021. This is a partial implementation based on the syntastic option referenced, supporting the `!` flag and filters of type `regex` and `level`. Also supports filetype specific filters using `ycm_<ft>_quiet_messages`, but I couldn't think of a great way to fall back to syntastic configs for this one.

In terms of usefulness: I've been playing with C# recently, which has a bunch of style warnings that I don't want to follow, and prefer to only have the gutter showing if there are actually errors, or warnings that I *do* want to follow.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/2377)
<!-- Reviewable:end -->
  • Loading branch information
homu committed Oct 24, 2016
2 parents 4da8868 + 7e12c12 commit f27787f
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 48 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,39 @@ Default: `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

When this option is set, YCM will populate the location list automatically every
Expand Down
112 changes: 73 additions & 39 deletions doc/youcompleteme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,40 +89,41 @@ Contents ~
10. The |g:ycm_enable_diagnostic_signs| option
11. The |g:ycm_enable_diagnostic_highlighting| option
12. The |g:ycm_echo_current_diagnostic| option
13. The |g:ycm_always_populate_location_list| option
14. The |g:ycm_open_loclist_on_ycm_diags| option
15. The |g:ycm_allow_changing_updatetime| option
16. The |g:ycm_complete_in_comments| option
17. The |g:ycm_complete_in_strings| option
18. The |g:ycm_collect_identifiers_from_comments_and_strings| option
19. The |g:ycm_collect_identifiers_from_tags_files| option
20. The |g:ycm_seed_identifiers_with_syntax| option
21. The |g:ycm_extra_conf_vim_data| option
22. The |g:ycm_server_python_interpreter| option
23. The |g:ycm_server_keep_logfiles| option
24. The |g:ycm_server_log_level| option
25. The |g:ycm_auto_start_csharp_server| option
26. The |g:ycm_auto_stop_csharp_server| option
27. The |g:ycm_csharp_server_port| option
28. The |g:ycm_csharp_insert_namespace_expr| option
29. The |g:ycm_add_preview_to_completeopt| option
30. The |g:ycm_autoclose_preview_window_after_completion| option
31. The |g:ycm_autoclose_preview_window_after_insertion| option
32. The |g:ycm_max_diagnostics_to_display| option
33. The |g:ycm_key_list_select_completion| option
34. The |g:ycm_key_list_previous_completion| option
35. The |g:ycm_key_invoke_completion| option
36. The |g:ycm_key_detailed_diagnostics| option
37. The |g:ycm_global_ycm_extra_conf| option
38. The |g:ycm_confirm_extra_conf| option
39. The |g:ycm_extra_conf_globlist| option
40. The |g:ycm_filepath_completion_use_working_dir| option
41. The |g:ycm_semantic_triggers| option
42. The |g:ycm_cache_omnifunc| option
43. The |g:ycm_use_ultisnips_completer| option
44. The |g:ycm_goto_buffer_command| option
45. The |g:ycm_disable_for_files_larger_than_kb| option
46. The |g:ycm_python_binary_path| option
13. The |g:ycm_filter_diagnostics| option
14. The |g:ycm_always_populate_location_list| option
15. The |g:ycm_open_loclist_on_ycm_diags| option
16. The |g:ycm_allow_changing_updatetime| option
17. The |g:ycm_complete_in_comments| option
18. The |g:ycm_complete_in_strings| option
19. The |g:ycm_collect_identifiers_from_comments_and_strings| option
20. The |g:ycm_collect_identifiers_from_tags_files| option
21. The |g:ycm_seed_identifiers_with_syntax| option
22. The |g:ycm_extra_conf_vim_data| option
23. The |g:ycm_server_python_interpreter| option
24. The |g:ycm_server_keep_logfiles| option
25. The |g:ycm_server_log_level| option
26. The |g:ycm_auto_start_csharp_server| option
27. The |g:ycm_auto_stop_csharp_server| option
28. The |g:ycm_csharp_server_port| option
29. The |g:ycm_csharp_insert_namespace_expr| option
30. The |g:ycm_add_preview_to_completeopt| option
31. The |g:ycm_autoclose_preview_window_after_completion| option
32. The |g:ycm_autoclose_preview_window_after_insertion| option
33. The |g:ycm_max_diagnostics_to_display| option
34. The |g:ycm_key_list_select_completion| option
35. The |g:ycm_key_list_previous_completion| option
36. The |g:ycm_key_invoke_completion| option
37. The |g:ycm_key_detailed_diagnostics| option
38. The |g:ycm_global_ycm_extra_conf| option
39. The |g:ycm_confirm_extra_conf| option
40. The |g:ycm_extra_conf_globlist| option
41. The |g:ycm_filepath_completion_use_working_dir| option
42. The |g:ycm_semantic_triggers| option
43. The |g:ycm_cache_omnifunc| option
44. The |g:ycm_use_ultisnips_completer| option
45. The |g:ycm_goto_buffer_command| option
46. The |g:ycm_disable_for_files_larger_than_kb| option
47. The |g:ycm_python_binary_path| option
11. FAQ |youcompleteme-faq|
1. I used to be able to 'import vim' in '.ycm_extra_conf.py', but now can't |import-vim|
2. On very rare occasions Vim crashes when I tab through the completion menu |youcompleteme-on-very-rare-occasions-vim-crashes-when-i-tab-through-completion-menu|
Expand Down Expand Up @@ -2080,6 +2081,39 @@ Default: '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 [54]. 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 [54].

Default: '{}'
>
let g:ycm_filter_diagnostics = {
\ "java": {
\ "regex": [ ".*taco.*", ... ],
\ "level": "error",
\ ...
\ }
\ }
<
-------------------------------------------------------------------------------
The *g:ycm_always_populate_location_list* option

When this option is set, YCM will populate the location list automatically
Expand Down Expand Up @@ -2174,7 +2208,7 @@ from the 'tagfiles()' Vim function which examines the 'tags' Vim option. See

YCM will re-index your tags files if it detects that they have been modified.

The only supported tag format is the Exuberant Ctags format [54]. The format
The only supported tag format is the Exuberant Ctags format [55]. The format
from "plain" ctags is NOT supported. Ctags needs to be called with the '--
fields=+l' option (that's a lowercase 'L', not a one) because YCM needs the
'language:<lang>' field in the tags output.
Expand Down Expand Up @@ -2541,7 +2575,7 @@ It's also possible to use a regular expression as a trigger. You have to prefix
your trigger with 're!' to signify it's a regex trigger. For instance,
're!\w+\.' would only trigger after the '\w+\.' regex matches.

NOTE: The regex syntax is **NOT** Vim's, it's Python's [55].
NOTE: The regex syntax is **NOT** Vim's, it's Python's [54].

Default: '[see next line]'
>
Expand Down Expand Up @@ -2869,7 +2903,7 @@ YCM does not read identifiers from my tags files ~
First, put 'let g:ycm_collect_identifiers_from_tags_files = 1' in your vimrc.

Make sure you are using Exuberant Ctags [57] to produce your tags files since
the only supported tag format is the Exuberant Ctags format [54]. The format
the only supported tag format is the Exuberant Ctags format [55]. The format
from "plain" ctags is NOT supported. The output of 'ctags --version' should
list "Exuberant Ctags".

Expand Down Expand Up @@ -3212,8 +3246,8 @@ References ~
[51] https://github.com/Valloric/ycmd/blob/master/ycmd/completers/completer.py
[52] https://github.com/Valloric/ListToggle
[53] https://github.com/itchyny/lightline.vim
[54] http://ctags.sourceforge.net/FORMAT
[55] https://docs.python.org/2/library/re.html#regular-expression-syntax
[54] https://docs.python.org/2/library/re.html#regular-expression-syntax
[55] http://ctags.sourceforge.net/FORMAT
[56] https://github.com/Valloric/YouCompleteMe/issues/18
[57] http://ctags.sourceforge.net/
[58] https://github.com/Raimondi/delimitMate
Expand Down
151 changes: 151 additions & 0 deletions python/ycm/diagnostic_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Copyright (C) 2016 YouCompleteMe contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.

from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()
from builtins import * # noqa

from future.utils import iterkeys, iteritems
from ycm import vimsupport
import re


class DiagnosticFilter( object ):
def __init__( self, config_or_filters ):
if isinstance( config_or_filters, list ):
self._filters = config_or_filters

else:
self._filters = _CompileFilters( config_or_filters )


def IsAllowed( self, diagnostic ):
# NOTE: a diagnostic IsAllowed() ONLY if NO filters match it
for filterMatches in self._filters:
if filterMatches( diagnostic ):
return False

return True


def SubsetForTypes( self, filetypes ):
"""Return a sub-filter limited to the given filetypes"""
# NOTE: actually, this class is already filtered
return self


@staticmethod
def CreateFromOptions( user_options ):
all_filters = dict( user_options.get( 'filter_diagnostics', {} ) )
compiled_by_type = {}
for type_spec, filter_value in iteritems( dict( all_filters ) ):
filetypes = [ type_spec ]
if type_spec.find( ',' ) != -1:
filetypes = type_spec.split( ',' )
for filetype in filetypes:
compiled_by_type[ filetype ] = _CompileFilters( filter_value )

return _MasterDiagnosticFilter( compiled_by_type )


class _MasterDiagnosticFilter( object ):

def __init__( self, all_filters ):
self._all_filters = all_filters
self._cache = {}


def IsAllowed( self, diagnostic ):
# NOTE: in this class's implementation, we ask vimsupport for
# the current filetypes and delegate automatically; it is probably,
# more efficient, however, to call SubsetForTypes() and reuse
# the returned DiagnosticFilter if it will be checked repeatedly.
filetypes = vimsupport.CurrentFiletypes()
return self.SubsetForTypes( filetypes ).IsAllowed( diagnostic )


def SubsetForTypes( self, filetypes ):
# check cache
cache_key = ','.join( filetypes )
cached = self._cache.get( cache_key )
if cached is not None:
return cached

# build a new DiagnosticFilter merging all filters
# for the provided filetypes
spec = []
for filetype in filetypes:
type_specific = self._all_filters.get( filetype, [] )
spec.extend( type_specific )

new_filter = DiagnosticFilter( spec )
self._cache[ cache_key ] = new_filter
return new_filter


def _ListOf( config_entry ):
if isinstance( config_entry, list ):
return config_entry

if config_entry is None:
return []

return [ config_entry ]


def CompileRegex( raw_regex ):
pattern = re.compile( raw_regex, re.IGNORECASE )

def FilterRegex( diagnostic ):
return pattern.search( diagnostic[ 'text' ] ) is not None

return FilterRegex


def CompileLevel( level ):
# valid kinds are WARNING and ERROR;
# expected input levels are `warning` and `error`
# NOTE: we don't validate the input...
expected_kind = level.upper()

def FilterLevel( diagnostic ):
return diagnostic[ 'kind' ] == expected_kind

return FilterLevel


FILTER_COMPILERS = { 'regex' : CompileRegex,
'level' : CompileLevel }


def _CompileFilters( config ):
"""Given a filter config dictionary, return a list of compiled filters"""
filters = []

for filter_type in iterkeys( config ):
compiler = FILTER_COMPILERS.get( filter_type )

if compiler is not None:
for filter_config in _ListOf( config[ filter_type ] ):
compiledFilter = compiler( filter_config )
filters.append( compiledFilter )

return filters
Loading

0 comments on commit f27787f

Please sign in to comment.