Skip to content

Commit b0b8820

Browse files
committed
fix(analyze): tighten path filtering and refine code output
- Path arguments to `robotcode analyze code` are now matched segment-wise: passing `tests/api` no longer pulls in siblings like `tests/api_v2`. - Workspace-level diagnostics (e.g. variable/library import errors) are now prefixed with `.:` instead of being printed without any source marker; their related-information lines keep their line/column. - The summary line picks its color from the highest severity present (red/yellow/blue/cyan) instead of only highlighting errors. - Unused-keyword/variable collectors are only registered when `--collect-unused` is set, and empty document reports from the analysis pass are no longer emitted.
1 parent d2f8114 commit b0b8820

3 files changed

Lines changed: 35 additions & 36 deletions

File tree

packages/analyze/src/robotcode/analyze/code/cli.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def _validate_load_library_timeout(ctx: click.Context, param: click.Option, valu
158158
@click.option(
159159
"-f",
160160
"--filter",
161-
"filter",
161+
"filter_patterns",
162162
metavar="PATTERN",
163163
type=str,
164164
multiple=True,
@@ -279,7 +279,7 @@ def _validate_load_library_timeout(ctx: click.Context, param: click.Option, valu
279279
@pass_application
280280
def code(
281281
app: Application,
282-
filter: Tuple[str, ...],
282+
filter_patterns: Tuple[str, ...],
283283
variable: Tuple[str, ...],
284284
variablefile: Tuple[str, ...],
285285
pythonpath: Tuple[str, ...],
@@ -290,7 +290,7 @@ def code(
290290
modifiers_hint: Tuple[str, ...],
291291
exit_code_mask: ExitCodeMask,
292292
extend_exit_code_mask: ExitCodeMask,
293-
paths: Tuple[Path],
293+
paths: Tuple[Path, ...],
294294
load_library_timeout: Optional[int],
295295
collect_unused: Optional[bool],
296296
cache_namespaces: Optional[bool],
@@ -425,25 +425,31 @@ def code(
425425
),
426426
)
427427
try:
428-
for e in analyzer.run(paths=paths, filter=filter):
428+
for e in analyzer.run(paths=paths, filter_patterns=filter_patterns):
429429
result_collector.add_diagnostics_report(e)
430430

431431
for e in result_collector.diagnostics:
432432
if isinstance(e, FolderDiagnosticReport):
433433
if e.items:
434-
_print_diagnostics(app, root_folder, e.items, e.folder.uri.to_path())
434+
folder_path = try_get_relative_path(e.folder.uri.to_path(), root_folder)
435+
_print_diagnostics(app, root_folder, e.items, folder_path, print_range=False)
435436
elif isinstance(e, DocumentDiagnosticReport):
436-
doc_path = (
437-
e.document.uri.to_path().relative_to(root_folder) if root_folder else e.document.uri.to_path()
438-
)
437+
doc_path = try_get_relative_path(e.document.uri.to_path(), root_folder)
439438
if e.items:
440439
_print_diagnostics(app, root_folder, e.items, doc_path)
441440
finally:
442441
result_collector.stop()
443442

444443
statistics_str = str(result_collector)
445-
if result_collector.errors > 0:
446-
statistics_str = click.style(statistics_str, fg="red")
444+
for count, severity in (
445+
(result_collector.errors, DiagnosticSeverity.ERROR),
446+
(result_collector.warnings, DiagnosticSeverity.WARNING),
447+
(result_collector.infos, DiagnosticSeverity.INFORMATION),
448+
(result_collector.hints, DiagnosticSeverity.HINT),
449+
):
450+
if count > 0:
451+
statistics_str = click.style(statistics_str, fg=SEVERITY_COLORS[severity])
452+
break
447453

448454
app.echo(statistics_str)
449455

@@ -469,7 +475,7 @@ def _print_diagnostics(
469475
f"{folder_path}:"
470476
+ (f"{item.range.start.line + 1}:{item.range.start.character + 1}: " if print_range else " ")
471477
)
472-
if folder_path and folder_path != root_folder
478+
if folder_path
473479
else ""
474480
)
475481
+ click.style(f"[{severity.name[0]}] {item.code}", fg=SEVERITY_COLORS[severity])
@@ -482,10 +488,6 @@ def _print_diagnostics(
482488

483489
app.echo(
484490
f" {related_path}:"
485-
+ (
486-
f"{related.location.range.start.line + 1}:{related.location.range.start.character + 1}: "
487-
if print_range
488-
else " "
489-
)
490-
+ f"{indent(related.message, prefix=' ').strip()}",
491+
f"{related.location.range.start.line + 1}:{related.location.range.start.character + 1}: "
492+
f"{indent(related.message, prefix=' ').strip()}",
491493
)

packages/analyze/src/robotcode/analyze/code/code_analyzer.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from robotcode.core.lsp.types import Diagnostic
77
from robotcode.core.text_document import TextDocument
88
from robotcode.core.uri import Uri
9-
from robotcode.core.utils.path import normalized_path
9+
from robotcode.core.utils.path import normalized_path, path_is_relative_to
1010
from robotcode.core.workspace import Workspace, WorkspaceFolder
1111
from robotcode.plugin import Application
1212
from robotcode.robot.config.model import RobotBaseProfile
@@ -85,7 +85,7 @@ def diagnostics(self) -> DiagnosticHandlers:
8585
return self._dispatcher
8686

8787
def run(
88-
self, paths: Iterable[Path] = {}, filter: Iterable[str] = {}
88+
self, paths: Iterable[Path] = (), filter_patterns: Iterable[str] = ()
8989
) -> Iterable[Union[DocumentDiagnosticReport, FolderDiagnosticReport]]:
9090
for folder in self.workspace.workspace_folders:
9191
self.app.verbose(f"Initialize folder {folder.uri.to_path()}")
@@ -102,7 +102,7 @@ def run(
102102

103103
yield FolderDiagnosticReport(folder, diagnostics)
104104

105-
documents = self.collect_documents(folder, paths=paths, filter=filter)
105+
documents = self.collect_documents(folder, paths=paths, filter_patterns=filter_patterns)
106106

107107
with self.app.progressbar(documents, label="Analyzing Documents") as progressbar:
108108
for document in progressbar:
@@ -117,13 +117,14 @@ def run(
117117
else:
118118
diagnostics.extend(item)
119119

120-
yield DocumentDiagnosticReport(document, diagnostics)
120+
if diagnostics:
121+
yield DocumentDiagnosticReport(document, diagnostics)
121122

122123
with self.app.progressbar(documents, label="Collecting Diagnostics") as progressbar:
123124
for document in progressbar:
124125
analyze_result = self.diagnostics.collect_diagnostics(document)
126+
diagnostics = []
125127
if analyze_result is not None:
126-
diagnostics = []
127128
for item in analyze_result:
128129
if item is None:
129130
continue
@@ -132,15 +133,16 @@ def run(
132133
else:
133134
diagnostics.extend(item)
134135

135-
yield DocumentDiagnosticReport(document, diagnostics)
136+
# Always yield, even when empty: the result collector uses this to count analyzed files.
137+
yield DocumentDiagnosticReport(document, diagnostics)
136138

137139
def collect_documents(
138-
self, folder: WorkspaceFolder, paths: Iterable[Path] = {}, filter: Iterable[str] = {}
140+
self, folder: WorkspaceFolder, paths: Iterable[Path] = (), filter_patterns: Iterable[str] = ()
139141
) -> List[TextDocument]:
140142
folder_root = folder.uri.to_path()
141-
full_paths = [normalized_path(p).as_posix() for p in paths]
143+
full_paths = [normalized_path(p) for p in paths]
142144

143-
ignore_spec = IgnoreSpec.from_list(filter, folder_root)
145+
ignore_spec = IgnoreSpec.from_list(filter_patterns, folder_root)
144146

145147
documents: List[TextDocument] = []
146148

@@ -151,8 +153,8 @@ def collect_documents(
151153
file_counter += 1
152154
try:
153155
document = self.workspace.documents.get_or_open_document(file)
154-
document_path = normalized_path(document.uri.to_path()).as_posix()
155-
if full_paths and not any(document_path.startswith(p) for p in full_paths):
156+
document_path = normalized_path(document.uri.to_path())
157+
if full_paths and not any(path_is_relative_to(document_path, p) for p in full_paths):
156158
continue
157159

158160
if ignore_spec.rules and not ignore_spec.matches(document.uri.to_path()):

packages/analyze/src/robotcode/analyze/code/robot_framework_language_provider.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ def __init__(self, diagnostics_context: DiagnosticsContext) -> None:
5858
self.diagnostics_context.diagnostics.folder_analyzers.add(self.analyze_folder)
5959
self.diagnostics_context.diagnostics.document_analyzers.add(self.analyze_document)
6060
self.diagnostics_context.diagnostics.document_collectors.add(self.collect_diagnostics)
61-
self.diagnostics_context.diagnostics.document_collectors.add(self.collect_unused_keywords)
62-
self.diagnostics_context.diagnostics.document_collectors.add(self.collect_unused_variables)
61+
if self.diagnostics_context.collect_unused:
62+
self.diagnostics_context.diagnostics.document_collectors.add(self.collect_unused_keywords)
63+
self.diagnostics_context.diagnostics.document_collectors.add(self.collect_unused_variables)
6364

6465
def _update_python_path(self) -> None:
6566
root_path = (
@@ -125,9 +126,6 @@ def collect_diagnostics(self, sender: Any, document: TextDocument) -> Optional[L
125126
return self._document_cache.get_diagnostic_modifier(document).modify_diagnostics(namespace.diagnostics)
126127

127128
def collect_unused_keywords(self, sender: Any, document: TextDocument) -> Optional[List[Diagnostic]]:
128-
if not self.diagnostics_context.collect_unused:
129-
return None
130-
131129
namespace = self._document_cache.get_namespace(document)
132130

133131
project_index = self._document_cache.get_project_index(document)
@@ -150,9 +148,6 @@ def collect_unused_keywords(self, sender: Any, document: TextDocument) -> Option
150148
return result
151149

152150
def collect_unused_variables(self, sender: Any, document: TextDocument) -> Optional[List[Diagnostic]]:
153-
if not self.diagnostics_context.collect_unused:
154-
return None
155-
156151
result: List[Diagnostic] = []
157152

158153
namespace = self._document_cache.get_namespace(document)

0 commit comments

Comments
 (0)