diff --git a/annotations.css b/annotations.css index e20ed8866..7481028b3 100644 --- a/annotations.css +++ b/annotations.css @@ -2,15 +2,15 @@ margin: 0; border-width: 0; } -.lsp_annotation .errors { +.lsp_annotation .error { color: color(var(--redish) alpha(0.85)); } -.lsp_annotation .warnings { +.lsp_annotation .warning { color: color(var(--yellowish) alpha(0.85)); } -.lsp_annotation .info { +.lsp_annotation .information { color: color(var(--bluish) alpha(0.85)); } -.lsp_annotation .hints { +.lsp_annotation .hint { color: color(var(--bluish) alpha(0.85)); } diff --git a/plugin/core/constants.py b/plugin/core/constants.py index e0e677e23..2eeb11e50 100644 --- a/plugin/core/constants.py +++ b/plugin/core/constants.py @@ -85,6 +85,7 @@ class RegionKey(StrEnum): SHOW_DEFINITIONS_KEY = 'show_definitions' # Region flags +DIAGNOSTIC_ICON_FLAGS = sublime.RegionFlags.HIDE_ON_MINIMAP | sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.NO_UNDO # noqa: E501 DOCUMENT_LINK_FLAGS = sublime.RegionFlags.HIDE_ON_MINIMAP | sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.DRAW_SOLID_UNDERLINE | sublime.RegionFlags.NO_UNDO # noqa: E501 REGIONS_INITIALIZE_FLAGS = sublime.RegionFlags.HIDDEN | sublime.RegionFlags.NO_UNDO SEMANTIC_TOKEN_FLAGS = sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.NO_UNDO diff --git a/plugin/core/types.py b/plugin/core/types.py index a0ecc6450..c0b7dce63 100644 --- a/plugin/core/types.py +++ b/plugin/core/types.py @@ -414,7 +414,7 @@ def diagnostics_highlight_style_flags(self) -> list[sublime.RegionFlags | None]: flags.append(self._style_str_to_flag(user_style)) return flags else: - # Defaults are defined in DIAGNOSTIC_SEVERITY in plugin/core/views.py + # Defaults are defined in DIAGNOSTIC_STYLES in plugin/core/views.py return [None] * 4 # default styling diff --git a/plugin/core/views.py b/plugin/core/views.py index 5675daa7b..afe495001 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -53,6 +53,7 @@ from .types import ClientConfig from .url import parse_uri from .workspace import is_subpath_of +from dataclasses import dataclass from typing import Any from typing import Callable from typing import cast @@ -76,34 +77,66 @@ _baseflags = sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.DRAW_EMPTY_AS_OVERWRITE | sublime.RegionFlags.NO_UNDO # noqa: E501 _multilineflags = sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.NO_UNDO -DIAGNOSTIC_SEVERITY: list[tuple[str, str, str, str, sublime.RegionFlags, sublime.RegionFlags]] = [ - # Kind CSS class Scope for color Icon resource add_regions flags for single-line diagnostic multi-line diagnostic # noqa: E501 - ("error", "errors", "region.redish markup.error.lsp", "Packages/LSP/icons/error.png", _baseflags | sublime.RegionFlags.DRAW_SQUIGGLY_UNDERLINE, _multilineflags), # noqa: E501 - ("warning", "warnings", "region.yellowish markup.warning.lsp", "Packages/LSP/icons/warning.png", _baseflags | sublime.RegionFlags.DRAW_SQUIGGLY_UNDERLINE, _multilineflags), # noqa: E501 - ("info", "info", "region.bluish markup.info.lsp", "Packages/LSP/icons/info.png", _baseflags | sublime.RegionFlags.DRAW_STIPPLED_UNDERLINE, _multilineflags), # noqa: E501 - ("hint", "hints", "region.bluish markup.info.hint.lsp", "", _baseflags | sublime.RegionFlags.DRAW_STIPPLED_UNDERLINE, _multilineflags), # noqa: E501 -] + +@dataclass +class DiagnosticStyle: + kind: str + css_class: str + region_scope: str + icon_resource: str + single_line_region_flags: sublime.RegionFlags + multi_line_region_flags: sublime.RegionFlags + + +DIAGNOSTIC_STYLES: dict[DiagnosticSeverity, DiagnosticStyle] = { + DiagnosticSeverity.Error: DiagnosticStyle( + 'error', + 'error', + 'region.redish markup.error.lsp', + 'Packages/LSP/icons/error.png', + _baseflags | sublime.RegionFlags.DRAW_SQUIGGLY_UNDERLINE, + _multilineflags + ), + DiagnosticSeverity.Warning: DiagnosticStyle( + 'warning', + 'warning', + 'region.yellowish markup.warning.lsp', + 'Packages/LSP/icons/warning.png', + _baseflags | sublime.RegionFlags.DRAW_SQUIGGLY_UNDERLINE, + _multilineflags + ), + DiagnosticSeverity.Information: DiagnosticStyle( + 'info', + 'information', + 'region.bluish markup.info.lsp', + 'Packages/LSP/icons/info.png', + _baseflags | sublime.RegionFlags.DRAW_STIPPLED_UNDERLINE, + _multilineflags + ), + DiagnosticSeverity.Hint: DiagnosticStyle( + 'hint', + 'hint', + 'region.bluish markup.info.hint.lsp', + '', + _baseflags | sublime.RegionFlags.DRAW_STIPPLED_UNDERLINE, + _multilineflags + ), +} class DiagnosticSeverityData: - __slots__ = ('regions', 'regions_with_tag', 'annotations', 'scope', 'icon') + __slots__ = ('regions', 'regions_with_tag', 'annotations') - def __init__(self, severity: int) -> None: + def __init__(self) -> None: self.regions: list[sublime.Region] = [] self.regions_with_tag: dict[DiagnosticTag, list[sublime.Region]] = {} self.annotations: list[str] = [] - _, _, self.scope, self.icon, _, _ = DIAGNOSTIC_SEVERITY[severity - 1] - if userprefs().diagnostics_gutter_marker != "sign": - self.icon = "" if severity == DiagnosticSeverity.Hint else userprefs().diagnostics_gutter_marker class InvalidUriSchemeException(Exception): def __init__(self, uri: str) -> None: - self.uri = uri - - def __str__(self) -> str: - return f"invalid URI scheme: {self.uri}" + super().__init__(f"invalid URI scheme: {uri}") def get_line(window: sublime.Window, file_name: str, row: int, strip: bool = True) -> str: @@ -698,16 +731,17 @@ def document_color_params(view: sublime.View) -> DocumentColorParams: return {"textDocument": text_document_identifier(view)} -def format_severity(severity: int) -> str: - if 1 <= severity <= len(DIAGNOSTIC_SEVERITY): - return DIAGNOSTIC_SEVERITY[severity - 1][0] - return "???" - - def diagnostic_severity(diagnostic: Diagnostic) -> DiagnosticSeverity: return diagnostic.get("severity", DiagnosticSeverity.Error) +def diagnostic_icon(severity: DiagnosticSeverity) -> str: + if userprefs().diagnostics_gutter_marker == "sign": + return DIAGNOSTIC_STYLES[severity].icon_resource + else: + return "" if severity == DiagnosticSeverity.Hint else userprefs().diagnostics_gutter_marker + + def format_diagnostics_for_annotation(diagnostics: list[Diagnostic], css_class: str) -> list[str]: annotations = [] for diagnostic in diagnostics: @@ -735,7 +769,7 @@ def format_diagnostic_for_panel(diagnostic: Diagnostic) -> tuple[str, int | None result = " {:>4}:{:<4}{:<8}{}".format( diagnostic["range"]["start"]["line"] + 1, diagnostic["range"]["start"]["character"] + 1, - format_severity(diagnostic_severity(diagnostic)), + DIAGNOSTIC_STYLES[diagnostic_severity(diagnostic)].kind, lines[0] ) if formatted != "" or code is not None: @@ -851,7 +885,7 @@ def format_diagnostic_for_html(config: ClientConfig, diagnostic: Diagnostic, bas if related_infos := diagnostic.get("relatedInformation"): info = "
".join(_format_diagnostic_related_info(config, info, base_dir) for info in related_infos) html += '
' + _html_element("pre", info, class_name="related_info", escape=False) - severity_class = DIAGNOSTIC_SEVERITY[diagnostic_severity(diagnostic) - 1][1] + severity_class = DIAGNOSTIC_STYLES[diagnostic_severity(diagnostic)].css_class return _html_element("pre", html, class_name=severity_class, escape=False) diff --git a/plugin/diagnostics.py b/plugin/diagnostics.py index 267b738ca..501a14074 100644 --- a/plugin/diagnostics.py +++ b/plugin/diagnostics.py @@ -12,8 +12,8 @@ from .core.settings import userprefs from .core.types import DocumentSelector_ from .core.url import normalize_uri -from .core.views import DIAGNOSTIC_SEVERITY from .core.views import diagnostic_severity +from .core.views import DIAGNOSTIC_STYLES from .core.views import format_diagnostics_for_annotation from typing import Union import itertools @@ -141,7 +141,7 @@ def draw(self, diagnostics: list[tuple[Diagnostic, sublime.Region]]) -> None: continue matching_diagnostics[0].append(diagnostic) matching_diagnostics[1].append(region) - css_class = DIAGNOSTIC_SEVERITY[severity - 1][1] + css_class = DIAGNOSTIC_STYLES[severity].css_class annotations = format_diagnostics_for_annotation(matching_diagnostics[0], css_class) color = self._severity_colors[severity] self._view.add_regions( diff --git a/plugin/session_buffer.py b/plugin/session_buffer.py index 1ccab23f3..6b5e35a6a 100644 --- a/plugin/session_buffer.py +++ b/plugin/session_buffer.py @@ -3,6 +3,7 @@ from ..protocol import CodeLens from ..protocol import ColorInformation from ..protocol import Diagnostic +from ..protocol import DiagnosticSeverity from ..protocol import DiagnosticTag from ..protocol import DocumentDiagnosticParams from ..protocol import DocumentDiagnosticReport @@ -158,7 +159,7 @@ def __init__(self, session_view: SessionViewProtocol, buffer_id: int, uri: Docum self._pending_changes: PendingChanges | None = None self.pending_refreshes: RequestFlags = RequestFlags.NONE self._diagnostics: list[tuple[Diagnostic, sublime.Region]] = [] - self.diagnostics_data_per_severity: dict[tuple[int, bool], DiagnosticSeverityData] = {} + self.diagnostics_data_per_severity: dict[tuple[DiagnosticSeverity, bool], DiagnosticSeverityData] = {} self._diagnostics_versions: dict[DiagnosticsIdentifier, int] = {} self.diagnostics_flags = 0 self._diagnostics_are_visible = False @@ -694,14 +695,14 @@ def on_diagnostics_async( return diagnostics_version = version diagnostics: list[tuple[Diagnostic, sublime.Region]] = [] - data_per_severity: dict[tuple[int, bool], DiagnosticSeverityData] = {} + data_per_severity: dict[tuple[DiagnosticSeverity, bool], DiagnosticSeverityData] = {} for diagnostic in raw_diagnostics: region = range_to_region(diagnostic["range"], view) severity = diagnostic_severity(diagnostic) key = (severity, len(view.split_by_newlines(region)) > 1) data = data_per_severity.get(key) if data is None: - data = DiagnosticSeverityData(severity) + data = DiagnosticSeverityData() data_per_severity[key] = data if tags := diagnostic.get('tags', []): for tag in tags: diff --git a/plugin/session_view.py b/plugin/session_view.py index b04bfafa3..08e3f44c9 100644 --- a/plugin/session_view.py +++ b/plugin/session_view.py @@ -1,9 +1,11 @@ from __future__ import annotations from ..protocol import Command +from ..protocol import DiagnosticSeverity from ..protocol import DocumentHighlightKind from ..protocol import DocumentUri from .core.active_request import ActiveRequest +from .core.constants import DIAGNOSTIC_ICON_FLAGS from .core.constants import DIAGNOSTIC_TAG_SCOPES from .core.constants import HOVER_ENABLED_KEY from .core.constants import RegionKey @@ -16,7 +18,8 @@ from .core.sessions import Session from .core.settings import userprefs from .core.views import ChangeEventAction -from .core.views import DIAGNOSTIC_SEVERITY +from .core.views import diagnostic_icon +from .core.views import DIAGNOSTIC_STYLES from .core.views import document_highlight_key from .core.views import make_command_link from .core.views import range_to_region @@ -94,7 +97,7 @@ def on_before_remove(self) -> None: self.session.cancel_request_async(request_id) self.session.unregister_session_view_async(self) self.session.config.erase_view_status(self.view) - for severity in reversed(range(1, len(DIAGNOSTIC_SEVERITY) + 1)): + for severity in reversed(DIAGNOSTIC_STYLES.keys()): self.view.erase_regions(f"{self.diagnostics_key(severity, False)}_icon") self.view.erase_regions(f"{self.diagnostics_key(severity, False)}_underline") self.view.erase_regions(f"{self.diagnostics_key(severity, True)}_icon") @@ -294,7 +297,7 @@ def shutdown_async(self) -> None: if listener := self.listener(): listener.on_session_shutdown_async(self.session) - def diagnostics_key(self, severity: int, multiline: bool) -> str: + def diagnostics_key(self, severity: DiagnosticSeverity, multiline: bool) -> str: return "lsp{}d{}{}".format(self.session.config.name, "m" if multiline else "s", severity) def present_diagnostics_async(self, is_view_visible: bool) -> None: @@ -306,19 +309,18 @@ def _redraw_diagnostics_async(self) -> None: flags = userprefs().diagnostics_highlight_style_flags() # for single lines multiline_flags = None if userprefs().show_multiline_diagnostics_highlights else sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.NO_UNDO # noqa: E501 level = userprefs().show_diagnostics_severity_level - for sev in reversed(range(1, len(DIAGNOSTIC_SEVERITY) + 1)): - self._draw_diagnostics(sev, level, flags[sev - 1] or DIAGNOSTIC_SEVERITY[sev - 1][4], multiline=False) - self._draw_diagnostics(sev, level, multiline_flags or DIAGNOSTIC_SEVERITY[sev - 1][5], multiline=True) + for sev, style in reversed(DIAGNOSTIC_STYLES.items()): + self._draw_diagnostics(sev, level, flags[sev - 1] or style.single_line_region_flags, multiline=False) + self._draw_diagnostics(sev, level, multiline_flags or style.multi_line_region_flags, multiline=True) self._diagnostic_annotations.draw(self.session_buffer.diagnostics) def _draw_diagnostics( self, - severity: int, + severity: DiagnosticSeverity, max_severity_level: int, flags: sublime.RegionFlags, multiline: bool ) -> None: - ICON_FLAGS = sublime.RegionFlags.HIDE_ON_MINIMAP | sublime.RegionFlags.DRAW_NO_FILL | sublime.RegionFlags.DRAW_NO_OUTLINE | sublime.RegionFlags.NO_UNDO # noqa: E501 key = self.diagnostics_key(severity, multiline) tags = {tag: TagData(f'{key}_tags_{tag}') for tag in DIAGNOSTIC_TAG_SCOPES} data = self._session_buffer.diagnostics_data_per_severity.get((severity, multiline)) @@ -332,8 +334,10 @@ def _draw_diagnostics( tags[tag].scope = tag_scope else: non_tag_regions.extend(regions) - self.view.add_regions(f"{key}_icon", non_tag_regions, data.scope, data.icon, ICON_FLAGS) - self.view.add_regions(f"{key}_underline", non_tag_regions, data.scope, "", flags) + region_scope = DIAGNOSTIC_STYLES[severity].region_scope + icon = diagnostic_icon(severity) + self.view.add_regions(f"{key}_icon", non_tag_regions, region_scope, icon, DIAGNOSTIC_ICON_FLAGS) + self.view.add_regions(f"{key}_underline", non_tag_regions, region_scope, "", flags) else: self.view.erase_regions(f"{key}_icon") self.view.erase_regions(f"{key}_underline") diff --git a/popups.css b/popups.css index e323d300e..78bbe4e2c 100644 --- a/popups.css +++ b/popups.css @@ -36,30 +36,25 @@ margin-bottom: 0.5rem; font-family: var(--mdpopups-font-mono); } -.errors { +.error, +.warning, +.information, +.hint { border-width: 0; - background-color: color(var(--redish) alpha(0.25)); color: var(--foreground); padding: 0.5rem; - white-space: pre-wrap; } -.warnings { - border-width: 0; +.error { + background-color: color(var(--redish) alpha(0.25)); +} +.warning { background-color: color(var(--yellowish) alpha(0.25)); - color: var(--foreground); - padding: 0.5rem; } -.info { - border-width: 0; +.information { background-color: color(var(--bluish) alpha(0.25)); - color: var(--foreground); - padding: 0.5rem; } -.hints { - border-width: 0; +.hint { background-color: color(var(--bluish) alpha(0.25)); - color: var(--foreground); - padding: 0.5rem; } .actions, .code-actions { font-family: system;