Skip to content

Commit 5cca59f

Browse files
committed
feat(vscode): introduce setting for modifing the diagnostics severity
With these settings, you can override the default configuration for all diagnostic messages. By combining file, block, and line diagnostic modifiers, you can precisely control how specific errors are displayed. - **`robotcode.analysis.diagnosticModifiers.ignore`**: Suppresses specific diagnostics from being displayed. You can specify one or more error codes, like `MultipleKeywords` or `[multiple-keywords, VariableNotFound]`. Use `*` to ignore all errors and then maybe add specific error codes to other modifiers, such as `information`, to selectively show them. - **`robotcode.analysis.diagnosticModifiers.error`**: Treats selected diagnostics as errors. - **`robotcode.analysis.diagnosticModifiers.warning`**: Displays chosen diagnostics as warnings. - **`robotcode.analysis.diagnosticModifiers.information`**: Shows specified diagnostics as information - **`robotcode.analysis.diagnosticModifiers.hint`**: Marks selected diagnostics as hints These settings allow you to tailor the diagnostic outputs to meet the specific needs of your project. closes #267 closes #297
1 parent f3cd887 commit 5cca59f

File tree

6 files changed

+180
-12
lines changed

6 files changed

+180
-12
lines changed

package.json

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,51 @@
748748
"markdownDescription": "Specifies a global [search order](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#specifying-explicit-priority-between-libraries-and-resources) for libraries and resources. This is usefull when you have libraries containing keywords with the same name. **RobotCode** is unable to analyze the library search order in a file specified with [`Set Library Search Order`](https://robotframework.org/robotframework/latest/libraries/BuiltIn.html#Set%20Library%20Search%20Order), so you can define a global order here. Just make sure to call the `Set Library Search Order` keyword somewhere in your robot file or internally in your library.",
749749
"scope": "resource"
750750
},
751+
"robotcode.analysis.diagnosticModifiers.ignore": {
752+
"type": "array",
753+
"default": [],
754+
"items": {
755+
"type": "string"
756+
},
757+
"markdownDescription": "Specifies the error codes that should be ignored.\n\n**Example:** if you want to ignore all errors with the code `MultipleKeywords` you can add `MultipleKeywords` to the list. \n\n_Note:_ add a `*` to ignore all errors.",
758+
"scope": "resource"
759+
},
760+
"robotcode.analysis.diagnosticModifiers.error": {
761+
"type": "array",
762+
"default": [],
763+
"items": {
764+
"type": "string"
765+
},
766+
"markdownDescription": "Specifies the error codes that should be treated as error.\n\n**Example:** if you ignore all errors with a `*` in the `ignore` setting, put the code `MultipleKeywords` to enable only `MultipleKeywords` errors.",
767+
"scope": "resource"
768+
},
769+
"robotcode.analysis.diagnosticModifiers.warning": {
770+
"type": "array",
771+
"default": [],
772+
"items": {
773+
"type": "string"
774+
},
775+
"markdownDescription": "Specifies the error codes that should be a warning.\n\n**Example:** if you want that errors with the code `MultipleKeywords` should be a warning instead you can add `MultipleKeywords` to the list.",
776+
"scope": "resource"
777+
},
778+
"robotcode.analysis.diagnosticModifiers.information": {
779+
"type": "array",
780+
"default": [],
781+
"items": {
782+
"type": "string"
783+
},
784+
"markdownDescription": "Specify the error codes that should be displayed as information.\n\n**Example**: If you want errors with the code `MultipleKeywords` to be shown as information instead of errors, you can add `MultipleKeywords` to the list.",
785+
"scope": "resource"
786+
},
787+
"robotcode.analysis.diagnosticModifiers.hint": {
788+
"type": "array",
789+
"default": [],
790+
"items": {
791+
"type": "string"
792+
},
793+
"markdownDescription": "Specify the error codes that should be displayed as hint\n\n**Example**: If you want errors with the code `MultipleKeywords` to be shown as hint instead of errors, you can add `MultipleKeywords` to the list.",
794+
"scope": "resource"
795+
},
751796
"robotcode.analysis.findUnusedReferences": {
752797
"type": "boolean",
753798
"default": false,
@@ -1599,4 +1644,4 @@
15991644
"workspaces": [
16001645
"docs"
16011646
]
1602-
}
1647+
}

packages/language_server/src/robotcode/language_server/robotframework/configuration.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33

44
from robotcode.core.workspace import ConfigBase, config_section
55
from robotcode.language_server.common.parts.diagnostics import AnalysisProgressMode, DiagnosticsMode
6-
from robotcode.robot.diagnostics.workspace_config import AnalysisRobotConfig, CacheConfig, RobotConfig
6+
from robotcode.robot.diagnostics.workspace_config import (
7+
AnalysisDiagnosticModifiersConfig,
8+
AnalysisRobotConfig,
9+
CacheConfig,
10+
RobotConfig,
11+
)
712

813

914
@config_section("robotcode.languageServer")
@@ -68,6 +73,7 @@ class AnalysisConfig(ConfigBase):
6873
find_unused_references: bool = False
6974
cache: CacheConfig = field(default_factory=CacheConfig)
7075
robot: AnalysisRobotConfig = field(default_factory=AnalysisRobotConfig)
76+
modifiers: AnalysisDiagnosticModifiersConfig = field(default_factory=AnalysisDiagnosticModifiersConfig)
7177

7278

7379
@config_section("robotcode")

packages/robot/src/robotcode/robot/diagnostics/diagnostics_modifier.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import re as re
33
from ast import AST
44
from collections import defaultdict
5-
from dataclasses import dataclass
5+
from dataclasses import dataclass, field
66
from typing import Dict, List, Optional, Set, Union
77

88
from robot.parsing.lexer.tokens import Token
@@ -12,7 +12,7 @@
1212

1313
from ..utils.visitor import Visitor
1414

15-
ACTIONS = ["ignore", "error", "warn", "information", "hint", "reset"]
15+
ACTIONS = ["ignore", "error", "warning", "information", "hint", "reset"]
1616

1717
ROBOTCODE_ACTION_AND_CODES_PATTERN = re.compile(rf"(?P<action>{'|'.join(ACTIONS)})(\[(?P<codes>[^\]]*?)\])?")
1818

@@ -153,8 +153,25 @@ def _handle_comment(self, node: Comment) -> None:
153153
self.rules_and_codes.actions[i][code] = action
154154

155155

156+
@dataclass
157+
class DiagnosticModifiersConfig:
158+
ignore: List[str] = field(default_factory=list)
159+
error: List[str] = field(default_factory=list)
160+
warning: List[str] = field(default_factory=list)
161+
information: List[str] = field(default_factory=list)
162+
hint: List[str] = field(default_factory=list)
163+
164+
156165
class DiagnosticsModifier:
157-
def __init__(self, model: AST) -> None:
166+
def __init__(self, model: AST, config: Optional[DiagnosticModifiersConfig] = None) -> None:
167+
self.config = config or DiagnosticModifiersConfig()
168+
169+
self.config.ignore = [i.translate(_translation_table).lower() for i in self.config.ignore]
170+
self.config.error = [i.translate(_translation_table).lower() for i in self.config.error]
171+
self.config.warning = [i.translate(_translation_table).lower() for i in self.config.warning]
172+
self.config.information = [i.translate(_translation_table).lower() for i in self.config.information]
173+
self.config.hint = [i.translate(_translation_table).lower() for i in self.config.hint]
174+
158175
self.model = model
159176

160177
@functools.cached_property
@@ -165,7 +182,7 @@ def rules_and_codes(self) -> RulesAndCodes:
165182

166183
def modify_diagnostic(self, diagnostic: Diagnostic) -> Optional[Diagnostic]:
167184
if diagnostic.code is not None:
168-
code = (
185+
code = orig_code = (
169186
str(diagnostic.code).translate(_translation_table).lower()
170187
if diagnostic.code is not None
171188
else "unknowncode"
@@ -177,6 +194,7 @@ def modify_diagnostic(self, diagnostic: Diagnostic) -> Optional[Diagnostic]:
177194
code = "*"
178195
lines = self.rules_and_codes.codes.get(code)
179196

197+
modified = False
180198
if lines is not None and diagnostic.range.start.line in lines:
181199
actions = self.rules_and_codes.actions.get(diagnostic.range.start.line)
182200
if actions is not None:
@@ -185,16 +203,42 @@ def modify_diagnostic(self, diagnostic: Diagnostic) -> Optional[Diagnostic]:
185203
if action == "ignore":
186204
return None
187205
if action == "reset":
188-
pass # do nothing
206+
pass
189207
elif action == "error":
208+
modified = True
190209
diagnostic.severity = DiagnosticSeverity.ERROR
191-
elif action == "warn":
210+
elif action == "warning":
211+
modified = True
192212
diagnostic.severity = DiagnosticSeverity.WARNING
193213
elif action == "information":
214+
modified = True
194215
diagnostic.severity = DiagnosticSeverity.INFORMATION
195216
elif action == "hint":
217+
modified = True
196218
diagnostic.severity = DiagnosticSeverity.HINT
197219

220+
if not modified:
221+
if orig_code in self.config.ignore:
222+
return None
223+
if orig_code in self.config.error:
224+
diagnostic.severity = DiagnosticSeverity.ERROR
225+
elif orig_code in self.config.warning:
226+
diagnostic.severity = DiagnosticSeverity.WARNING
227+
elif orig_code in self.config.information:
228+
diagnostic.severity = DiagnosticSeverity.INFORMATION
229+
elif orig_code in self.config.hint:
230+
diagnostic.severity = DiagnosticSeverity.HINT
231+
elif "*" in self.config.hint:
232+
diagnostic.severity = DiagnosticSeverity.HINT
233+
elif "*" in self.config.information:
234+
diagnostic.severity = DiagnosticSeverity.INFORMATION
235+
elif "*" in self.config.warning:
236+
diagnostic.severity = DiagnosticSeverity.WARNING
237+
elif "*" in self.config.error:
238+
diagnostic.severity = DiagnosticSeverity.ERROR
239+
elif "*" in self.config.ignore:
240+
return None
241+
198242
return diagnostic
199243

200244
def modify_diagnostics(self, diagnostics: List[Diagnostic]) -> List[Diagnostic]:

packages/robot/src/robotcode/robot/diagnostics/document_cache_helper.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
from robotcode.core.uri import Uri
2727
from robotcode.core.utils.logging import LoggingDescriptor
2828
from robotcode.core.workspace import Workspace, WorkspaceFolder
29-
from robotcode.robot.diagnostics.diagnostics_modifier import DiagnosticsModifier
29+
from robotcode.robot.diagnostics.diagnostics_modifier import DiagnosticModifiersConfig, DiagnosticsModifier
3030

3131
from ..config.model import RobotBaseProfile
3232
from ..utils import get_robot_version
3333
from ..utils.stubs import Languages
3434
from .imports_manager import ImportsManager
3535
from .library_doc import LibraryDoc
3636
from .namespace import DocumentType, Namespace
37-
from .workspace_config import AnalysisRobotConfig, CacheConfig, RobotConfig
37+
from .workspace_config import AnalysisDiagnosticModifiersConfig, AnalysisRobotConfig, CacheConfig, RobotConfig
3838

3939

4040
class UnknownFileTypeError(Exception):
@@ -583,4 +583,14 @@ def get_diagnostic_modifier(self, document: TextDocument) -> DiagnosticsModifier
583583
return document.get_cache(self.__get_diagnostic_modifier)
584584

585585
def __get_diagnostic_modifier(self, document: TextDocument) -> DiagnosticsModifier:
586-
return DiagnosticsModifier(self.get_model(document, False))
586+
analysis_config = self.workspace.get_configuration(AnalysisDiagnosticModifiersConfig, document.uri)
587+
return DiagnosticsModifier(
588+
self.get_model(document, False),
589+
DiagnosticModifiersConfig(
590+
ignore=analysis_config.ignore,
591+
error=analysis_config.error,
592+
warning=analysis_config.warning,
593+
information=analysis_config.information,
594+
hint=analysis_config.hint,
595+
),
596+
)

packages/robot/src/robotcode/robot/diagnostics/workspace_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,13 @@ class CacheConfig(ConfigBase):
5555
@dataclass
5656
class AnalysisRobotConfig(ConfigBase):
5757
global_library_search_order: List[str] = field(default_factory=list)
58+
59+
60+
@config_section("robotcode.analysis.diagnosticModifiers")
61+
@dataclass
62+
class AnalysisDiagnosticModifiersConfig(ConfigBase):
63+
ignore: List[str] = field(default_factory=list)
64+
error: List[str] = field(default_factory=list)
65+
warning: List[str] = field(default_factory=list)
66+
information: List[str] = field(default_factory=list)
67+
hint: List[str] = field(default_factory=list)

tests/robotcode/robot/diagnostics/test_modifiers.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
from robot.api import get_model
66

77
from robotcode.core.lsp.types import Diagnostic, DiagnosticSeverity, Position, Range
8-
from robotcode.robot.diagnostics.diagnostics_modifier import DiagnosticsModifier, DisablersVisitor
8+
from robotcode.robot.diagnostics.diagnostics_modifier import (
9+
DiagnosticModifiersConfig,
10+
DiagnosticsModifier,
11+
DisablersVisitor,
12+
)
913

1014

1115
@pytest.mark.parametrize(
@@ -308,3 +312,52 @@ def test_diagnostics_modifier_should_work_on_block_level() -> None:
308312
severity=DiagnosticSeverity.HINT,
309313
)
310314
]
315+
316+
317+
def test_diagnostics_modifier_should_be_configurable() -> None:
318+
file = """\
319+
*** Test Cases ***
320+
first
321+
## robotcode: ignore[unknown-variable]
322+
# robotcode: ignore[message_1, message_2] hint[message 3, message 4]
323+
324+
unknown keyword
325+
unknown keyword1
326+
log ${unknown}
327+
log hello
328+
329+
"""
330+
model = get_model(io.StringIO(file))
331+
modifier = DiagnosticsModifier(model, DiagnosticModifiersConfig(ignore=["unknown-variable"]))
332+
333+
diagnostics = [
334+
Diagnostic(
335+
range=Range(start=Position(line=5, character=4), end=Position(line=5, character=12)),
336+
message="UnknownVariable",
337+
code="UnknownVariable",
338+
severity=DiagnosticSeverity.INFORMATION,
339+
),
340+
Diagnostic(
341+
range=Range(start=Position(line=6, character=4), end=Position(line=6, character=12)),
342+
message="Message1",
343+
code="message1",
344+
severity=DiagnosticSeverity.ERROR,
345+
),
346+
Diagnostic(
347+
range=Range(start=Position(line=6, character=4), end=Position(line=6, character=12)),
348+
message="Message3",
349+
code="Message3",
350+
severity=DiagnosticSeverity.ERROR,
351+
),
352+
]
353+
354+
result = modifier.modify_diagnostics(diagnostics)
355+
356+
assert result == [
357+
Diagnostic(
358+
range=Range(start=Position(line=6, character=4), end=Position(line=6, character=12)),
359+
message="Message3",
360+
code="Message3",
361+
severity=DiagnosticSeverity.HINT,
362+
)
363+
]

0 commit comments

Comments
 (0)