Coverage for vibefpmgui/references_panel.py: 93%
51 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-01 20:51 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-01 20:51 +0000
1"""The "References" tab: every use of a name, from Find References.
3Results are grouped by file (project files first, then dependencies), shown
4like the *Search* tab's (`vibefpmgui.result_tree`): each file with its icon,
5each use with its line in the editor's syntax colors and the name
6highlighted. Click one to open the file there.
7"""
9from __future__ import annotations
11from pathlib import Path
13from qtpy.QtCore import Signal
14from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget
16from .result_tree import ResultTree, Root, file_labels, make_match, root_for
19def short_path(path: Path, project_dir: Path | None) -> str:
20 if project_dir is not None: 20 ↛ 25line 20 didn't jump to line 25 because the condition on line 20 was always true
21 try:
22 return str(path.relative_to(project_dir.resolve()))
23 except ValueError:
24 pass
25 return str(path)
28class ReferencesPanel(QWidget):
29 open_requested = Signal(str, int, int, int, int) # file, line, column, end line, end column
30 keep_requested = Signal(str) # a file to keep open (a result double-clicked)
31 HINT = "Use Find References (⇧F12) on a name in the editor to list its uses here."
33 def __init__(self, parent=None):
34 super().__init__(parent)
35 self.title = QLabel(self.HINT)
36 self.title.setWordWrap(True)
37 self.title.setContentsMargins(8, 6, 8, 2)
38 self.tree = ResultTree()
39 self.tree.open_requested.connect(self.open_requested)
40 self.tree.keep_requested.connect(self.keep_requested)
41 layout = QVBoxLayout(self)
42 layout.setContentsMargins(0, 0, 0, 0)
43 layout.setSpacing(2)
44 layout.addWidget(self.title)
45 layout.addWidget(self.tree, 1)
47 def reset(self):
48 """Another project was opened: no results."""
49 self.tree.clear_results()
50 self.title.setText(self.HINT)
52 def show_results(self, name: str, results: list[tuple[Path, int, int, int]], roots: list[Root],
53 project_dir: Path | None):
54 """``results``: (file, 1-based line, start column, end column);
55 ``roots``: the packages, to label the dependencies' files."""
56 self.tree.clear_results()
57 files: dict[Path, list[tuple[int, int, int]]] = {}
58 for path, line, start, end in results:
59 files.setdefault(Path(path).resolve(), []).append((line, start, end))
60 if not results:
61 self.title.setText(f"No references to <b>{name}</b> found.")
62 return
63 self.title.setText(f"<b>{name}</b>: {len(results)} reference{'s' * (len(results) != 1)} "
64 f"in {len(files)} file{'s' * (len(files) != 1)}")
65 rows = []
66 for path, places in files.items():
67 root = root_for(path, roots)
68 folder, package = file_labels(path, root, project_dir)
69 lines = self.tree.file_lines(path)
70 matches = [make_match(lines[line - 1] if 0 < line <= len(lines) else "", line, start, line, end)
71 for line, start, end in sorted(places)]
72 outside = root is None or root.dependency # (dependencies after the project's own files)
73 rows.append(((outside, package, folder, path.name), path, matches, folder, package))
74 for _key, path, matches, folder, package in sorted(rows, key=lambda r: r[0]):
75 self.tree.set_file(path, matches, folder, package)