1+ import functools
2+ import time
13from enum import Flag
24from pathlib import Path
35from textwrap import indent
@@ -36,43 +38,73 @@ class ReturnCode(Flag):
3638 HINTS = 8
3739
3840
39- class Statistic :
41+ class ResultCollector :
4042 def __init__ (self , exit_code_mask : ExitCodeMask ) -> None :
4143 self .exit_code_mask = exit_code_mask
4244 self ._folders : Set [WorkspaceFolder ] = set ()
4345 self ._files : Set [TextDocument ] = set ()
44- self ._diagnostics : List [Union [DocumentDiagnosticReport , FolderDiagnosticReport ]] = []
46+ self .diagnostics : List [Union [DocumentDiagnosticReport , FolderDiagnosticReport ]] = []
47+ self ._start_time = time .time ()
48+ self ._end_time = self ._start_time
49+
50+ @staticmethod
51+ def _format_duration (seconds : float ) -> str :
52+ total_seconds = max (0.0 , seconds )
53+
54+ hours , remainder = divmod (total_seconds , 3600.0 )
55+ minutes , seconds_remainder = divmod (remainder , 60.0 )
56+
57+ hours_int = int (hours )
58+ minutes_int = int (minutes )
59+
60+ if hours_int > 0 :
61+ return f"{ hours_int } h { minutes_int } m { seconds_remainder :.2f} s"
62+ if minutes_int > 0 :
63+ return f"{ minutes_int } m { seconds_remainder :.2f} s"
64+ return f"{ seconds_remainder :.2f} s"
65+
66+ def start (self ) -> float :
67+ self ._start_time = time .time ()
68+ return self ._start_time
69+
70+ def stop (self ) -> float :
71+ self ._end_time = time .time ()
72+ return self ._end_time
4573
4674 @property
75+ def elapsed (self ) -> float :
76+ return self ._end_time - self ._start_time
77+
78+ @functools .cached_property
4779 def errors (self ) -> int :
4880 return sum (
49- len ([i for i in e .items if i .severity == DiagnosticSeverity .ERROR ]) for e in self ._diagnostics if e .items
81+ len ([i for i in e .items if i .severity == DiagnosticSeverity .ERROR ]) for e in self .diagnostics if e .items
5082 )
5183
52- @property
84+ @functools . cached_property
5385 def warnings (self ) -> int :
5486 return sum (
55- len ([i for i in e .items if i .severity == DiagnosticSeverity .WARNING ]) for e in self ._diagnostics if e .items
87+ len ([i for i in e .items if i .severity == DiagnosticSeverity .WARNING ]) for e in self .diagnostics if e .items
5688 )
5789
58- @property
90+ @functools . cached_property
5991 def infos (self ) -> int :
6092 return sum (
6193 len ([i for i in e .items if i .severity == DiagnosticSeverity .INFORMATION ])
62- for e in self ._diagnostics
94+ for e in self .diagnostics
6395 if e .items
6496 )
6597
66- @property
98+ @functools . cached_property
6799 def hints (self ) -> int :
68100 return sum (
69- len ([i for i in e .items if i .severity == DiagnosticSeverity .HINT ]) for e in self ._diagnostics if e .items
101+ len ([i for i in e .items if i .severity == DiagnosticSeverity .HINT ]) for e in self .diagnostics if e .items
70102 )
71103
72104 def add_diagnostics_report (
73105 self , diagnostics_report : Union [DocumentDiagnosticReport , FolderDiagnosticReport ]
74106 ) -> None :
75- self ._diagnostics .append (diagnostics_report )
107+ self .diagnostics .append (diagnostics_report )
76108
77109 if isinstance (diagnostics_report , FolderDiagnosticReport ):
78110 self ._folders .add (diagnostics_report .folder )
@@ -83,6 +115,7 @@ def __str__(self) -> str:
83115 return (
84116 f"Files: { len (self ._files )} , Errors: { self .errors } , Warnings: { self .warnings } , "
85117 f"Infos: { self .infos } , Hints: { self .hints } "
118+ f" (in { self ._format_duration (self .elapsed )} )"
86119 )
87120
88121 def calculate_return_code (self ) -> ReturnCode :
@@ -363,32 +396,37 @@ def code(
363396 app .verbose (f"Using analyzer_config: { analyzer_config } " )
364397 app .verbose (f"Using exit code mask: { mask } " )
365398
366- statistics = Statistic (mask )
367- for e in CodeAnalyzer (
368- app = app ,
369- analysis_config = analyzer_config .to_workspace_analysis_config (),
370- robot_profile = robot_profile ,
371- root_folder = root_folder ,
372- ).run (paths = paths , filter = filter ):
373- statistics .add_diagnostics_report (e )
374-
375- if isinstance (e , FolderDiagnosticReport ):
376- if e .items :
377- _print_diagnostics (app , root_folder , e .items , e .folder .uri .to_path ())
378- elif isinstance (e , DocumentDiagnosticReport ):
379- doc_path = (
380- e .document .uri .to_path ().relative_to (root_folder ) if root_folder else e .document .uri .to_path ()
381- )
382- if e .items :
383- _print_diagnostics (app , root_folder , e .items , doc_path )
399+ result_collector = ResultCollector (mask )
400+ result_collector .start ()
401+ try :
402+ for e in CodeAnalyzer (
403+ app = app ,
404+ analysis_config = analyzer_config .to_workspace_analysis_config (),
405+ robot_profile = robot_profile ,
406+ root_folder = root_folder ,
407+ ).run (paths = paths , filter = filter ):
408+ result_collector .add_diagnostics_report (e )
409+
410+ for e in result_collector .diagnostics :
411+ if isinstance (e , FolderDiagnosticReport ):
412+ if e .items :
413+ _print_diagnostics (app , root_folder , e .items , e .folder .uri .to_path ())
414+ elif isinstance (e , DocumentDiagnosticReport ):
415+ doc_path = (
416+ e .document .uri .to_path ().relative_to (root_folder ) if root_folder else e .document .uri .to_path ()
417+ )
418+ if e .items :
419+ _print_diagnostics (app , root_folder , e .items , doc_path )
420+ finally :
421+ result_collector .stop ()
384422
385- statistics_str = str (statistics )
386- if statistics .errors > 0 :
423+ statistics_str = str (result_collector )
424+ if result_collector .errors > 0 :
387425 statistics_str = click .style (statistics_str , fg = "red" )
388426
389427 app .echo (statistics_str )
390428
391- app .exit (statistics .calculate_return_code ().value )
429+ app .exit (result_collector .calculate_return_code ().value )
392430
393431 except (TypeError , ValueError ) as e :
394432 raise click .ClickException (str (e )) from e
0 commit comments