vibefpmgui.window.complexity
The Complexity panel: measuring the project's Fortran files (on a pool thread, while the panel is on view), again when one (or a file one includes) is saved; a file that hasn't changed since it was last measured, nor have the files it includes, isn't read again.
1"""The Complexity panel: measuring the project's Fortran files (on a pool 2thread, while the panel is on view), again when one (or a file one 3includes) is saved; a file that hasn't changed since it was last measured, 4nor have the files it includes, isn't read again. 5""" 6 7from __future__ import annotations 8 9import time 10from pathlib import Path 11 12from .. import complexity as cx 13from ..background import call_in_background 14from ..complexity_panel import ComplexityPanel 15from ..project_search import Query, iter_files, search_roots 16from ..project_tree import fpm_folders 17 18FORTRAN_GLOBS = ", ".join(f"*{ext}" for ext in sorted(cx.FORTRAN)) 19 20 21def _stamp(path: Path) -> tuple[int, int] | None: 22 """A file's (modification time, size); None if it's gone.""" 23 try: 24 stat = path.stat() 25 except OSError: 26 return None 27 return stat.st_mtime_ns, stat.st_size 28 29 30def _key(path: Path, metrics: cx.FileMetrics) -> tuple: 31 return _stamp(path), tuple(_stamp(p) for p in metrics.includes) 32 33 34def measure(roots, dependencies: bool, cache: dict) -> tuple[list[cx.FileMetrics], dict]: 35 """Every Fortran file's metrics, and the cache for next time ({path: 36 (key, metrics)}: a file isn't read again while it and the files it 37 includes are unchanged, their modification times and sizes the same).""" 38 files, kept = [], {} 39 query = Query("", include=FORTRAN_GLOBS, dependencies=dependencies) 40 for _root, path in iter_files(roots, query): 41 if _stamp(path) is None: 42 continue 43 old = cache.get(path) 44 if old is not None and old[0] == _key(path, old[1]): 45 metrics = old[1] 46 else: 47 metrics = cx.analyze_file(path) 48 kept[path] = (_key(path, metrics), metrics) 49 files.append(metrics) 50 return files, kept 51 52 53class ComplexityMixin: 54 """The Complexity panel (a part of `vibefpmgui.main_window.MainWindow`).""" 55 56 def _build_complexity_panel(self): 57 """The Complexity panel.""" 58 self.complexity_panel = panel = ComplexityPanel() 59 panel.icon_for = self.project_tree.icon_for 60 panel.set_code_font(self._mono_font(editor=True)) 61 panel.tests_check.setChecked(self.settings.value("complexity_tests", False, type=bool)) 62 panel.tests_check.toggled.connect(lambda on: self.settings.setValue("complexity_tests", on)) 63 panel.dependencies_check.setChecked(self.settings.value("complexity_dependencies", False, type=bool)) 64 panel.dependencies_check.toggled.connect(lambda on: self.settings.setValue("complexity_dependencies", on)) 65 panel.refresh_requested.connect(self.refresh_complexity) 66 panel.open_requested.connect(lambda path, line: self.navigate_to(path, line, 0, preview=True)) 67 self._complexity_cache: dict = {} 68 self._complexity_request = 0 69 self._complexity_stale = True 70 71 def refresh_complexity(self): 72 """Measure the project's code again (or once the panel is on view).""" 73 panel = self.complexity_panel 74 if self.manifest_path is None: 75 panel.show_message("Open a project to see the complexity of its Fortran code here.") 76 return 77 if not self.panel_in_front(panel): 78 self._complexity_stale = True 79 return 80 self._complexity_stale = False 81 self._complexity_request += 1 82 request = self._complexity_request 83 project = self.manifest_path.parent 84 roots = search_roots(self.graph, project) 85 dependencies = panel.dependencies_check.isChecked() 86 cache = dict(self._complexity_cache) 87 if not panel.files: 88 panel.show_message("Measuring the code…") 89 call_in_background(lambda: measure(roots, dependencies, cache), 90 lambda result: self._complexity_measured(request, roots, *result), self) 91 92 def _complexity_measured(self, request: int, roots, files: list[cx.FileMetrics], cache: dict): 93 if request != self._complexity_request or self.manifest_path is None: 94 return 95 self._complexity_cache = cache 96 project = self.manifest_path.parent 97 tests = [Path(folder) for folder, role in fpm_folders(self.manifest_path).items() if role == "test"] 98 dependency_dirs = [r.directory for r in roots if r.dependency] 99 self.complexity_panel.show_metrics(files, project, tests, dependency_dirs, time.strftime("%H:%M")) 100 101 def _complexity_panel_shown(self, shown: bool): 102 if shown and self._complexity_stale: 103 self.refresh_complexity() 104 105 def _complexity_file_saved(self, path: str): 106 saved = Path(path) 107 if cx.is_fortran(saved) or any(saved.resolve() == p.resolve() 108 for f in self.complexity_panel.files for p in f.includes): 109 self.refresh_complexity()
FORTRAN_GLOBS =
'*.f, *.f03, *.f08, *.f18, *.f23, *.f77, *.f90, *.f95, *.for, *.fpp, *.ftn'
def
measure( roots, dependencies: bool, cache: dict) -> tuple[list[vibefpmgui.complexity.FileMetrics], dict]:
35def measure(roots, dependencies: bool, cache: dict) -> tuple[list[cx.FileMetrics], dict]: 36 """Every Fortran file's metrics, and the cache for next time ({path: 37 (key, metrics)}: a file isn't read again while it and the files it 38 includes are unchanged, their modification times and sizes the same).""" 39 files, kept = [], {} 40 query = Query("", include=FORTRAN_GLOBS, dependencies=dependencies) 41 for _root, path in iter_files(roots, query): 42 if _stamp(path) is None: 43 continue 44 old = cache.get(path) 45 if old is not None and old[0] == _key(path, old[1]): 46 metrics = old[1] 47 else: 48 metrics = cx.analyze_file(path) 49 kept[path] = (_key(path, metrics), metrics) 50 files.append(metrics) 51 return files, kept
Every Fortran file's metrics, and the cache for next time ({path: (key, metrics)}: a file isn't read again while it and the files it includes are unchanged, their modification times and sizes the same).
class
ComplexityMixin:
54class ComplexityMixin: 55 """The Complexity panel (a part of `vibefpmgui.main_window.MainWindow`).""" 56 57 def _build_complexity_panel(self): 58 """The Complexity panel.""" 59 self.complexity_panel = panel = ComplexityPanel() 60 panel.icon_for = self.project_tree.icon_for 61 panel.set_code_font(self._mono_font(editor=True)) 62 panel.tests_check.setChecked(self.settings.value("complexity_tests", False, type=bool)) 63 panel.tests_check.toggled.connect(lambda on: self.settings.setValue("complexity_tests", on)) 64 panel.dependencies_check.setChecked(self.settings.value("complexity_dependencies", False, type=bool)) 65 panel.dependencies_check.toggled.connect(lambda on: self.settings.setValue("complexity_dependencies", on)) 66 panel.refresh_requested.connect(self.refresh_complexity) 67 panel.open_requested.connect(lambda path, line: self.navigate_to(path, line, 0, preview=True)) 68 self._complexity_cache: dict = {} 69 self._complexity_request = 0 70 self._complexity_stale = True 71 72 def refresh_complexity(self): 73 """Measure the project's code again (or once the panel is on view).""" 74 panel = self.complexity_panel 75 if self.manifest_path is None: 76 panel.show_message("Open a project to see the complexity of its Fortran code here.") 77 return 78 if not self.panel_in_front(panel): 79 self._complexity_stale = True 80 return 81 self._complexity_stale = False 82 self._complexity_request += 1 83 request = self._complexity_request 84 project = self.manifest_path.parent 85 roots = search_roots(self.graph, project) 86 dependencies = panel.dependencies_check.isChecked() 87 cache = dict(self._complexity_cache) 88 if not panel.files: 89 panel.show_message("Measuring the code…") 90 call_in_background(lambda: measure(roots, dependencies, cache), 91 lambda result: self._complexity_measured(request, roots, *result), self) 92 93 def _complexity_measured(self, request: int, roots, files: list[cx.FileMetrics], cache: dict): 94 if request != self._complexity_request or self.manifest_path is None: 95 return 96 self._complexity_cache = cache 97 project = self.manifest_path.parent 98 tests = [Path(folder) for folder, role in fpm_folders(self.manifest_path).items() if role == "test"] 99 dependency_dirs = [r.directory for r in roots if r.dependency] 100 self.complexity_panel.show_metrics(files, project, tests, dependency_dirs, time.strftime("%H:%M")) 101 102 def _complexity_panel_shown(self, shown: bool): 103 if shown and self._complexity_stale: 104 self.refresh_complexity() 105 106 def _complexity_file_saved(self, path: str): 107 saved = Path(path) 108 if cx.is_fortran(saved) or any(saved.resolve() == p.resolve() 109 for f in self.complexity_panel.files for p in f.includes): 110 self.refresh_complexity()
The Complexity panel (a part of vibefpmgui.main_window.MainWindow).
def
refresh_complexity(self):
72 def refresh_complexity(self): 73 """Measure the project's code again (or once the panel is on view).""" 74 panel = self.complexity_panel 75 if self.manifest_path is None: 76 panel.show_message("Open a project to see the complexity of its Fortran code here.") 77 return 78 if not self.panel_in_front(panel): 79 self._complexity_stale = True 80 return 81 self._complexity_stale = False 82 self._complexity_request += 1 83 request = self._complexity_request 84 project = self.manifest_path.parent 85 roots = search_roots(self.graph, project) 86 dependencies = panel.dependencies_check.isChecked() 87 cache = dict(self._complexity_cache) 88 if not panel.files: 89 panel.show_message("Measuring the code…") 90 call_in_background(lambda: measure(roots, dependencies, cache), 91 lambda result: self._complexity_measured(request, roots, *result), self)
Measure the project's code again (or once the panel is on view).