diff --git a/anaconda_lib/linting/sublime.py b/anaconda_lib/linting/sublime.py index 23999a78..02a3224b 100644 --- a/anaconda_lib/linting/sublime.py +++ b/anaconda_lib/linting/sublime.py @@ -330,6 +330,35 @@ def get_lineno_msgs(view, lineno): return errors_msg +def get_specific_lineno_msgs(view, lineno): + """Get lineno error messages and return them by message type + """ + + ERRORS = ANACONDA.get('ERRORS') + WARNINGS = ANACONDA.get('WARNINGS') + VIOLATIONS = ANACONDA.get('VIOLATIONS') + + specific_errors_msg = {} + + if lineno is not None: + def check_and_delete_if_empty(dct: dict, key: str): + if not dct.get(key): + del dct[key] + + vid = view.id() + if vid in ERRORS: + specific_errors_msg['ERRORS'] = ERRORS[vid].get(lineno, []) + check_and_delete_if_empty(specific_errors_msg, 'ERRORS') + if vid in WARNINGS: + specific_errors_msg['WARNINGS'] = WARNINGS[vid].get(lineno, []) + check_and_delete_if_empty(specific_errors_msg, 'WARNINGS') + if vid in VIOLATIONS: + specific_errors_msg['VIOLATIONS'] = VIOLATIONS[vid].get(lineno, []) + check_and_delete_if_empty(specific_errors_msg, 'VIOLATIONS') + + return specific_errors_msg + + def run_linter(view=None, hook=None): """Run the linter for the given view """ diff --git a/anaconda_lib/tooltips.py b/anaconda_lib/tooltips.py index 18370c0b..c48e5a8c 100644 --- a/anaconda_lib/tooltips.py +++ b/anaconda_lib/tooltips.py @@ -33,7 +33,7 @@ def __init__(self, theme: str) -> None: self._load_tooltips() Tooltip.loaded = True - def show_tooltip(self, view: sublime.View, tooltip: str, content: Dict[str, str], fallback: Callable) -> None: # noqa + def show_tooltip(self, view: sublime.View, tooltip: str, content: Dict[str, str], fallback: Callable, **kwargs: dict) -> None: # noqa """Generates and display a tooltip or pass execution to fallback """ @@ -42,14 +42,15 @@ def show_tooltip(self, view: sublime.View, tooltip: str, content: Dict[str, str] return fallback() width = get_settings(view, 'font_size', 8) * 75 - kwargs = {'location': -1, 'max_width': width if width < 900 else 900} + popup_kwargs = {'location': kwargs.get('location', -1), + 'max_width': kwargs.get('max_width', width if width < 900 else 900)} if st_ver >= 3071: kwargs['flags'] = sublime.COOPERATE_WITH_AUTO_COMPLETE text = self._generate(tooltip, content) if text is None: return fallback() - return view.show_popup(text, **kwargs) + return view.show_popup(text, **popup_kwargs) def _generate(self, tooltip: str, content: Dict[str, str]) -> Union[Dict[str, str], None]: # noqa """Generate a tooltip with the given text @@ -114,3 +115,43 @@ def _load_css(self, css_file: str) -> str: self.themes[theme_name] = resource.read() return theme_name + + +class TooltipHelper(Tooltip): + loaded = False + themes = {} # type: Dict[str, bytes] + tooltips = {} # type: Dict[str, str] + + def __init__(self, theme: str) -> None: + self.theme = theme + + if int(sublime.version()) < 3070: + return + + if TooltipHelper.loaded is False: + self._load_css_themes() + self._load_tooltips() + TooltipHelper.loaded = True + + def _load_tooltips(self) -> None: + """Load tooltips templates from anaconda tooltips templates + """ + + template_files_pattern = os.path.join( + os.path.dirname(__file__), os.pardir, + 'templates', 'tooltips', '*.tpl') + for template_file in glob.glob(template_files_pattern): + with open(template_file, 'r', encoding='utf8') as tplfile: + tplname = os.path.basename(template_file).split('.tpl')[0] + self.tooltips[tplname] = Template(tplfile.read()) + + def generate_no_css(self, tooltip: str, content: Dict[str, str]) -> Union[Dict[str, str], None]: + try: + data = self.tooltips[tooltip].safe_substitute(content) + return data + except KeyError as err: + logging.error( + 'while generating tooltip without css: tooltip {} don\'t exists'.format( + str(err)) + ) + return None diff --git a/css/popup.css b/css/popup.css index 10aa857a..1e70b6a5 100644 --- a/css/popup.css +++ b/css/popup.css @@ -19,3 +19,15 @@ div.anaconda { font-weight: bold; font-size: 1.05em; } + +.anaconda .error_warning .errors { + color: red; +} + +.anaconda .error_warning .warnings { + color: orange; +} + +.anaconda .error_warning .violations { + color: blue; +} diff --git a/listeners/linting.py b/listeners/linting.py index d1f09272..e915cb5d 100644 --- a/listeners/linting.py +++ b/listeners/linting.py @@ -7,6 +7,7 @@ import sublime import sublime_plugin +from ..anaconda_lib.tooltips import Tooltip, TooltipHelper from ..anaconda_lib._typing import Callable, Dict, Any from ..anaconda_lib.helpers import ( check_linting, get_settings, check_linting_behaviour, @@ -15,7 +16,7 @@ from ..anaconda_lib.linting.sublime import ( ANACONDA, erase_lint_marks, run_linter, last_selected_lineno, update_statusbar, - get_lineno_msgs + get_specific_lineno_msgs ) @@ -154,28 +155,30 @@ def _erase_marks(self, view: sublime.View) -> None: erase_lint_marks(view) - def on_hover(self, view: sublime.View, point: int, hover_zone: int): - # Planned to make my own listener class(sublime_plugin.ViewEventListener) for this function - # but couldn't figure out how to register them - # Tell me if I need to move this, or if it can piggyback under this listener - - # from anaconda_lib.tooltips:show_tooltips - st_ver = int(sublime.version()) - if st_ver < 3070: - return - - if hover_zone == sublime.HOVER_TEXT: - if get_settings(view, 'anaconda_linter_hover_message', False): - rowcol = view.rowcol(point) - line = rowcol[0] # tuple (lineno, col) - messages = get_lineno_msgs(view, line) - - if messages: - # Not sure how to properly choose the height & width values, but this works fine on my laptop - # Also unsure as to how to format it so its pretty and colorful (Tooltip._generate ?) - max_width = len(messages) * 840 - max_height = len(max(messages)) - message = "\n".join(messages) - # Newline is not rendered , errors all on one line :( help - view.show_popup(message, location=point, max_width=max_width, max_height=max_height) - # amount of time of popup? + def on_hover(self, view: sublime.View, point: int, hover_zone: int) -> None: + """Called when user hovers cursor above a line + """ + if hover_zone == sublime.HOVER_TEXT and get_settings(view, 'anaconda_linter_hover_message', False): + rowcol = view.rowcol(point) + line = rowcol[0] # tuple (lineno, col) + messages_with_type = get_specific_lineno_msgs(view, line) + + if messages_with_type: + css = get_settings(view, 'anaconda_tooltip_theme', 'popup') + main_tooltip = Tooltip(css) + tooltip_helper = TooltipHelper(css) + helper_content = [] + + for key in messages_with_type.keys(): + for msg in messages_with_type.get(key): + tooltip_data = {'level': key.lower(), 'messages': msg} + helper_content.append(tooltip_helper.generate_no_css('error_warning_helper', tooltip_data)) + + def do_nothing(): + return + + messages = "
".join(helper_content) + tooltip_name = 'error_warning' + main_content = {'helper_content': messages} + kwargs = {'location': point} + main_tooltip.show_tooltip(view, tooltip_name, main_content, do_nothing, **kwargs) diff --git a/templates/tooltips/error_warning.tpl b/templates/tooltips/error_warning.tpl new file mode 100644 index 00000000..f0d8db6c --- /dev/null +++ b/templates/tooltips/error_warning.tpl @@ -0,0 +1,5 @@ +
+
+ ${helper_content} +
+
diff --git a/templates/tooltips/error_warning_helper.tpl b/templates/tooltips/error_warning_helper.tpl new file mode 100644 index 00000000..af8d3783 --- /dev/null +++ b/templates/tooltips/error_warning_helper.tpl @@ -0,0 +1 @@ +
${messages}