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

1"""The "References" tab: every use of a name, from Find References. 

2 

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""" 

8 

9from __future__ import annotations 

10 

11from pathlib import Path 

12 

13from qtpy.QtCore import Signal 

14from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget 

15 

16from .result_tree import ResultTree, Root, file_labels, make_match, root_for 

17 

18 

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) 

26 

27 

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." 

32 

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) 

46 

47 def reset(self): 

48 """Another project was opened: no results.""" 

49 self.tree.clear_results() 

50 self.title.setText(self.HINT) 

51 

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)