vibefpmgui.breakpoints_list

The Debug panel's list of breakpoints: every one in the project, each with its state drawn as in the editor's gutter (a red dot; hollow: disabled; grey: the debugger couldn't place it), its place and its line of code.

A click opens the line; the right-click menu enables, disables or removes one (and, from the Debug menu, all of them); Delete removes the selected one. Where the program stopped at a breakpoint, it's selected, and the gutter's yellow arrow is drawn over its dot. The code is in the editor's font and syntax colors.

  1"""The Debug panel's list of breakpoints: every one in the project, each
  2with its state drawn as in the editor's gutter (a red dot; hollow: disabled;
  3grey: the debugger couldn't place it), its place and its line of code.
  4
  5A click opens the line; the right-click menu enables, disables or removes
  6one (and, from the Debug menu, all of them); Delete removes the selected
  7one. Where the program stopped at a breakpoint, it's selected, and the
  8gutter's yellow arrow is drawn over its dot. The code is in the editor's
  9font and syntax colors.
 10"""
 11
 12from __future__ import annotations
 13
 14from collections.abc import Callable, Iterable
 15from dataclasses import dataclass
 16from pathlib import Path
 17
 18from pygments.styles import get_style_by_name
 19from qtpy.QtCore import QPointF, QRectF, Qt, Signal
 20from qtpy.QtGui import (
 21    QAction,
 22    QColor,
 23    QFont,
 24    QIcon,
 25    QPainter,
 26    QPalette,
 27    QPen,
 28    QPixmap,
 29    QPolygonF,
 30)
 31from qtpy.QtWidgets import (
 32    QAbstractItemView,
 33    QApplication,
 34    QHeaderView,
 35    QMenu,
 36    QStyle,
 37    QStyledItemDelegate,
 38    QStyleOptionViewItem,
 39    QTreeWidget,
 40    QTreeWidgetItem,
 41)
 42
 43from . import highlighter as hl
 44from .editor_shared import BREAKPOINT_COLOR, BREAKPOINT_UNPLACED, EXECUTION_COLOR
 45from .result_tree import paint_code, syntax_formats
 46
 47ENABLED, DISABLED, UNPLACED = "enabled", "disabled", "unplaced"
 48KEY_ROLE = Qt.UserRole  # an item's (path, line)
 49
 50
 51@dataclass(frozen=True)
 52class BreakpointEntry:
 53    path: str      # (absolute: as the breakpoints are kept)
 54    line: int
 55    state: str     # ENABLED, DISABLED or UNPLACED (enabled, but the debugger couldn't place it)
 56    shown: str     # its place as shown: the path in the project, and the line ("src/x.f90:12")
 57    code: str = ""  # the line's code (as in the file: its indentation not shown)
 58    why: str = ""   # why it wasn't placed
 59
 60
 61_icons: dict[tuple[str, bool], QIcon] = {}
 62
 63
 64def breakpoint_icon(state: str, here: bool = False) -> QIcon:
 65    """The gutter's dot for a state; ``here``: the program is stopped at it
 66    (the gutter's yellow arrow over the dot)."""
 67    if (state, here) not in _icons:
 68        icon = QIcon()
 69        for ratio in (1, 2, 3):
 70            pixmap = QPixmap(12 * ratio, 12 * ratio)
 71            pixmap.setDevicePixelRatio(ratio)
 72            pixmap.fill(Qt.transparent)
 73            painter = QPainter(pixmap)
 74            painter.setRenderHint(QPainter.Antialiasing)
 75            if state == DISABLED:
 76                painter.setPen(QPen(QColor(BREAKPOINT_COLOR), 1.5))
 77                painter.setBrush(Qt.NoBrush)
 78            else:
 79                painter.setPen(Qt.NoPen)
 80                painter.setBrush(QColor(BREAKPOINT_UNPLACED if state == UNPLACED else BREAKPOINT_COLOR))
 81            painter.drawEllipse(QRectF(1.5, 1.5, 9, 9))
 82            if here:  # (as editor_gutter draws it, in 12 pixels)
 83                painter.setPen(QPen(QColor(EXECUTION_COLOR).darker(140), 1))
 84                painter.setBrush(QColor(EXECUTION_COLOR))
 85                painter.drawPolygon(QPolygonF([QPointF(0.5, 1.5), QPointF(5.5, 1.5), QPointF(11.5, 6),
 86                                               QPointF(5.5, 10.5), QPointF(0.5, 10.5)]))
 87            painter.end()
 88            icon.addPixmap(pixmap)
 89        _icons[(state, here)] = icon
 90    return _icons[(state, here)]
 91
 92
 93class _CodeDelegate(QStyledItemDelegate):
 94    """The code column: the line in the editor's font and syntax colors
 95    (plain on the selected row: its text color reads best there)."""
 96
 97    def __init__(self, tree: BreakpointsList):
 98        super().__init__(tree)
 99        self.tree = tree
100
101    def paint(self, painter, option, index):
102        opt = QStyleOptionViewItem(option)
103        self.initStyleOption(opt, index)
104        entry = self.tree.entry_at(index.row())
105        if entry is None:
106            super().paint(painter, option, index)
107            return
108        opt.text = ""
109        style = opt.widget.style() if opt.widget else QApplication.style()
110        style.drawControl(QStyle.CE_ItemViewItem, opt, painter, opt.widget)
111        selected = bool(opt.state & QStyle.State_Selected)
112        group = QPalette.Active if opt.state & QStyle.State_Active else QPalette.Inactive
113        plain = opt.palette.color(group, QPalette.HighlightedText if selected else QPalette.Text)
114        if entry.state == DISABLED and not selected:
115            plain = opt.palette.color(group, QPalette.PlaceholderText)
116        code = entry.code.lstrip()
117        column = len(entry.code) - len(code)  # (where the code shown starts in the line)
118        formats = None if selected or entry.state == DISABLED else self.tree.line_formats(entry)
119        painter.save()
120        painter.setClipRect(opt.rect)
121        paint_code(painter, opt.rect.adjusted(4, 0, 0, 0), code, formats, column, self.tree.mono, plain)
122        painter.restore()
123
124
125def state_text(entry: BreakpointEntry) -> str:
126    if entry.state == DISABLED:
127        return "Disabled: the debugger doesn't stop here"
128    if entry.state == UNPLACED:
129        return f"Not placed: {entry.why}" if entry.why else "Not placed by the debugger"
130    return "Enabled"
131
132
133class BreakpointsList(QTreeWidget):
134    open_requested = Signal(str, int)       # path, line: a breakpoint clicked
135    enabled_toggled = Signal(str, int)      # path, line: enable it if disabled, else disable it
136    remove_requested = Signal(str, int)     # path, line
137
138    def __init__(self, more_actions: Iterable[QAction] = (), parent=None):
139        """``more_actions``: added to every right-click menu (the Debug menu's
140        Disable All and Remove All)."""
141        super().__init__(parent)
142        self.more_actions = list(more_actions)
143        self.entries: list[BreakpointEntry] = []
144        self.setHeaderLabels(["Breakpoint", "Code"])
145        self.setRootIsDecorated(False)
146        self.setUniformRowHeights(True)
147        self.setSelectionMode(QAbstractItemView.SingleSelection)
148        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)  # (the code: the rest)
149        self.setContextMenuPolicy(Qt.CustomContextMenu)
150        self.customContextMenuRequested.connect(self._menu_at)
151        self.itemClicked.connect(self._open)
152        self.itemActivated.connect(self._open)  # (Enter, a double-click)
153        self._hit: tuple[str, int] | None = None  # where the program stopped (selected, the arrow on it)
154        self.mono = QFont()  # the code's font: the editor's (set by the window)
155        self.lines_for: Callable[[str], list[str]] | None = None  # a file's lines (set by the window)
156        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
157        self._formats: dict = {}  # token type -> format
158        self._line_formats: dict[tuple[str, int, str], list | None] = {}
159        self.setItemDelegateForColumn(1, _CodeDelegate(self))
160
161    # ------------------------------------------------------------ contents
162    def set_entries(self, entries: list[BreakpointEntry]):
163        """The breakpoints now (the selection kept, if its breakpoint still is)."""
164        if entries == self.entries:
165            return
166        current = self.current_key()
167        self.entries = list(entries)
168        self._line_formats.clear()
169        self.clear()
170        for entry in entries:
171            item = QTreeWidgetItem([entry.shown, entry.code.strip()])
172            item.setIcon(0, breakpoint_icon(entry.state, (entry.path, entry.line) == self._hit))
173            item.setData(0, KEY_ROLE, (entry.path, entry.line))
174            tip = f"{entry.path}:{entry.line}\n{state_text(entry)}"
175            item.setToolTip(0, tip)
176            item.setToolTip(1, tip)
177            if entry.state == DISABLED:
178                item.setForeground(0, self.palette().placeholderText())
179                item.setForeground(1, self.palette().placeholderText())
180            self.addTopLevelItem(item)
181        self._select(self._hit or current)
182
183    def entry_at(self, row: int) -> BreakpointEntry | None:
184        item = self.topLevelItem(row)
185        if item is None:
186            return None
187        key = tuple(item.data(0, KEY_ROLE))
188        return next((e for e in self.entries if (e.path, e.line) == key), None)
189
190    def line_formats(self, entry: BreakpointEntry) -> list | None:
191        """The syntax format of each column of a breakpoint's line (None: plain)."""
192        key = (entry.path, entry.line, entry.code)
193        if key not in self._line_formats:
194            lines = self.lines_for(entry.path) if self.lines_for is not None else [entry.code]
195            line = entry.line if self.lines_for is not None else 1
196            self._line_formats[key] = syntax_formats(lines, line, Path(entry.path), self._style, self._formats)
197        return self._line_formats[key]
198
199    def refresh_style(self):
200        """The editor's syntax colors (View → Syntax Colors; light or dark)."""
201        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
202        self._formats.clear()
203        self._line_formats.clear()
204        self.viewport().update()
205
206    def item_for(self, path: str, line: int) -> QTreeWidgetItem | None:
207        for i in range(self.topLevelItemCount()):
208            item = self.topLevelItem(i)
209            if tuple(item.data(0, KEY_ROLE)) == (path, line):
210                return item
211        return None
212
213    def current_key(self) -> tuple[str, int] | None:
214        item = self.currentItem()
215        return tuple(item.data(0, KEY_ROLE)) if item is not None and item.isSelected() else None
216
217    def show_hit(self, where: tuple[str, int] | None):
218        """The breakpoint the program stopped at, selected (None: it's not
219        stopped at one: nothing selected)."""
220        for entry in self.entries:  # (the arrow: off the one it was on, onto this one)
221            if (entry.path, entry.line) in (self._hit, where):
222                self.item_for(entry.path, entry.line).setIcon(
223                    0, breakpoint_icon(entry.state, (entry.path, entry.line) == where))
224        self._hit = where
225        self._select(where)
226
227    def _select(self, key: tuple[str, int] | None):
228        item = self.item_for(*key) if key else None
229        if item is None:
230            self.clearSelection()
231            self.setCurrentItem(None)
232            return
233        self.setCurrentItem(item)
234        item.setSelected(True)
235        self.scrollToItem(item)
236
237    # ------------------------------------------------------------ using it
238    def _open(self, item: QTreeWidgetItem, _column: int = 0):
239        path, line = item.data(0, KEY_ROLE)
240        self.open_requested.emit(path, line)
241
242    def menu_for(self, item: QTreeWidgetItem | None) -> QMenu:
243        menu = QMenu(self)
244        if item is not None:
245            path, line = item.data(0, KEY_ROLE)
246            entry = next(e for e in self.entries if (e.path, e.line) == (path, line))
247            menu.addAction("Go to Line", lambda: self.open_requested.emit(path, line))
248            menu.addAction("Enable Breakpoint" if entry.state == DISABLED else "Disable Breakpoint",
249                           lambda: self.enabled_toggled.emit(path, line))
250            menu.addAction("Remove Breakpoint", lambda: self.remove_requested.emit(path, line))
251            if self.more_actions:
252                menu.addSeparator()
253        menu.addActions(self.more_actions)
254        return menu
255
256    def _menu_at(self, pos):
257        item = self.itemAt(pos)
258        if item is None and not self.entries:
259            return
260        menu = self.menu_for(item)
261        menu.exec(self.viewport().mapToGlobal(pos))
262        menu.deleteLater()
263
264    def keyPressEvent(self, event):
265        key = self.current_key()
266        if event.key() in (Qt.Key_Delete, Qt.Key_Backspace) and key is not None:
267            self.remove_requested.emit(*key)
268            return
269        super().keyPressEvent(event)
KEY_ROLE = <ItemDataRole.UserRole: 256>
@dataclass(frozen=True)
class BreakpointEntry:
52@dataclass(frozen=True)
53class BreakpointEntry:
54    path: str      # (absolute: as the breakpoints are kept)
55    line: int
56    state: str     # ENABLED, DISABLED or UNPLACED (enabled, but the debugger couldn't place it)
57    shown: str     # its place as shown: the path in the project, and the line ("src/x.f90:12")
58    code: str = ""  # the line's code (as in the file: its indentation not shown)
59    why: str = ""   # why it wasn't placed
BreakpointEntry( path: str, line: int, state: str, shown: str, code: str = '', why: str = '')
path: str
line: int
state: str
shown: str
code: str = ''
why: str = ''
def breakpoint_icon(state: str, here: bool = False) -> PySide6.QtGui.QIcon:
65def breakpoint_icon(state: str, here: bool = False) -> QIcon:
66    """The gutter's dot for a state; ``here``: the program is stopped at it
67    (the gutter's yellow arrow over the dot)."""
68    if (state, here) not in _icons:
69        icon = QIcon()
70        for ratio in (1, 2, 3):
71            pixmap = QPixmap(12 * ratio, 12 * ratio)
72            pixmap.setDevicePixelRatio(ratio)
73            pixmap.fill(Qt.transparent)
74            painter = QPainter(pixmap)
75            painter.setRenderHint(QPainter.Antialiasing)
76            if state == DISABLED:
77                painter.setPen(QPen(QColor(BREAKPOINT_COLOR), 1.5))
78                painter.setBrush(Qt.NoBrush)
79            else:
80                painter.setPen(Qt.NoPen)
81                painter.setBrush(QColor(BREAKPOINT_UNPLACED if state == UNPLACED else BREAKPOINT_COLOR))
82            painter.drawEllipse(QRectF(1.5, 1.5, 9, 9))
83            if here:  # (as editor_gutter draws it, in 12 pixels)
84                painter.setPen(QPen(QColor(EXECUTION_COLOR).darker(140), 1))
85                painter.setBrush(QColor(EXECUTION_COLOR))
86                painter.drawPolygon(QPolygonF([QPointF(0.5, 1.5), QPointF(5.5, 1.5), QPointF(11.5, 6),
87                                               QPointF(5.5, 10.5), QPointF(0.5, 10.5)]))
88            painter.end()
89            icon.addPixmap(pixmap)
90        _icons[(state, here)] = icon
91    return _icons[(state, here)]

The gutter's dot for a state; here: the program is stopped at it (the gutter's yellow arrow over the dot).

def state_text(entry: BreakpointEntry) -> str:
126def state_text(entry: BreakpointEntry) -> str:
127    if entry.state == DISABLED:
128        return "Disabled: the debugger doesn't stop here"
129    if entry.state == UNPLACED:
130        return f"Not placed: {entry.why}" if entry.why else "Not placed by the debugger"
131    return "Enabled"
class BreakpointsList(PySide6.QtWidgets.QTreeWidget):
134class BreakpointsList(QTreeWidget):
135    open_requested = Signal(str, int)       # path, line: a breakpoint clicked
136    enabled_toggled = Signal(str, int)      # path, line: enable it if disabled, else disable it
137    remove_requested = Signal(str, int)     # path, line
138
139    def __init__(self, more_actions: Iterable[QAction] = (), parent=None):
140        """``more_actions``: added to every right-click menu (the Debug menu's
141        Disable All and Remove All)."""
142        super().__init__(parent)
143        self.more_actions = list(more_actions)
144        self.entries: list[BreakpointEntry] = []
145        self.setHeaderLabels(["Breakpoint", "Code"])
146        self.setRootIsDecorated(False)
147        self.setUniformRowHeights(True)
148        self.setSelectionMode(QAbstractItemView.SingleSelection)
149        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)  # (the code: the rest)
150        self.setContextMenuPolicy(Qt.CustomContextMenu)
151        self.customContextMenuRequested.connect(self._menu_at)
152        self.itemClicked.connect(self._open)
153        self.itemActivated.connect(self._open)  # (Enter, a double-click)
154        self._hit: tuple[str, int] | None = None  # where the program stopped (selected, the arrow on it)
155        self.mono = QFont()  # the code's font: the editor's (set by the window)
156        self.lines_for: Callable[[str], list[str]] | None = None  # a file's lines (set by the window)
157        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
158        self._formats: dict = {}  # token type -> format
159        self._line_formats: dict[tuple[str, int, str], list | None] = {}
160        self.setItemDelegateForColumn(1, _CodeDelegate(self))
161
162    # ------------------------------------------------------------ contents
163    def set_entries(self, entries: list[BreakpointEntry]):
164        """The breakpoints now (the selection kept, if its breakpoint still is)."""
165        if entries == self.entries:
166            return
167        current = self.current_key()
168        self.entries = list(entries)
169        self._line_formats.clear()
170        self.clear()
171        for entry in entries:
172            item = QTreeWidgetItem([entry.shown, entry.code.strip()])
173            item.setIcon(0, breakpoint_icon(entry.state, (entry.path, entry.line) == self._hit))
174            item.setData(0, KEY_ROLE, (entry.path, entry.line))
175            tip = f"{entry.path}:{entry.line}\n{state_text(entry)}"
176            item.setToolTip(0, tip)
177            item.setToolTip(1, tip)
178            if entry.state == DISABLED:
179                item.setForeground(0, self.palette().placeholderText())
180                item.setForeground(1, self.palette().placeholderText())
181            self.addTopLevelItem(item)
182        self._select(self._hit or current)
183
184    def entry_at(self, row: int) -> BreakpointEntry | None:
185        item = self.topLevelItem(row)
186        if item is None:
187            return None
188        key = tuple(item.data(0, KEY_ROLE))
189        return next((e for e in self.entries if (e.path, e.line) == key), None)
190
191    def line_formats(self, entry: BreakpointEntry) -> list | None:
192        """The syntax format of each column of a breakpoint's line (None: plain)."""
193        key = (entry.path, entry.line, entry.code)
194        if key not in self._line_formats:
195            lines = self.lines_for(entry.path) if self.lines_for is not None else [entry.code]
196            line = entry.line if self.lines_for is not None else 1
197            self._line_formats[key] = syntax_formats(lines, line, Path(entry.path), self._style, self._formats)
198        return self._line_formats[key]
199
200    def refresh_style(self):
201        """The editor's syntax colors (View → Syntax Colors; light or dark)."""
202        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
203        self._formats.clear()
204        self._line_formats.clear()
205        self.viewport().update()
206
207    def item_for(self, path: str, line: int) -> QTreeWidgetItem | None:
208        for i in range(self.topLevelItemCount()):
209            item = self.topLevelItem(i)
210            if tuple(item.data(0, KEY_ROLE)) == (path, line):
211                return item
212        return None
213
214    def current_key(self) -> tuple[str, int] | None:
215        item = self.currentItem()
216        return tuple(item.data(0, KEY_ROLE)) if item is not None and item.isSelected() else None
217
218    def show_hit(self, where: tuple[str, int] | None):
219        """The breakpoint the program stopped at, selected (None: it's not
220        stopped at one: nothing selected)."""
221        for entry in self.entries:  # (the arrow: off the one it was on, onto this one)
222            if (entry.path, entry.line) in (self._hit, where):
223                self.item_for(entry.path, entry.line).setIcon(
224                    0, breakpoint_icon(entry.state, (entry.path, entry.line) == where))
225        self._hit = where
226        self._select(where)
227
228    def _select(self, key: tuple[str, int] | None):
229        item = self.item_for(*key) if key else None
230        if item is None:
231            self.clearSelection()
232            self.setCurrentItem(None)
233            return
234        self.setCurrentItem(item)
235        item.setSelected(True)
236        self.scrollToItem(item)
237
238    # ------------------------------------------------------------ using it
239    def _open(self, item: QTreeWidgetItem, _column: int = 0):
240        path, line = item.data(0, KEY_ROLE)
241        self.open_requested.emit(path, line)
242
243    def menu_for(self, item: QTreeWidgetItem | None) -> QMenu:
244        menu = QMenu(self)
245        if item is not None:
246            path, line = item.data(0, KEY_ROLE)
247            entry = next(e for e in self.entries if (e.path, e.line) == (path, line))
248            menu.addAction("Go to Line", lambda: self.open_requested.emit(path, line))
249            menu.addAction("Enable Breakpoint" if entry.state == DISABLED else "Disable Breakpoint",
250                           lambda: self.enabled_toggled.emit(path, line))
251            menu.addAction("Remove Breakpoint", lambda: self.remove_requested.emit(path, line))
252            if self.more_actions:
253                menu.addSeparator()
254        menu.addActions(self.more_actions)
255        return menu
256
257    def _menu_at(self, pos):
258        item = self.itemAt(pos)
259        if item is None and not self.entries:
260            return
261        menu = self.menu_for(item)
262        menu.exec(self.viewport().mapToGlobal(pos))
263        menu.deleteLater()
264
265    def keyPressEvent(self, event):
266        key = self.current_key()
267        if event.key() in (Qt.Key_Delete, Qt.Key_Backspace) and key is not None:
268            self.remove_requested.emit(*key)
269            return
270        super().keyPressEvent(event)

QTreeWidget(self, /, parent: PySide6.QtWidgets.QWidget | None = None, *, columnCount: int | None = None, topLevelItemCount: int | None = None, supportedDragActions: PySide6.QtCore.Qt.DropAction | None = None) -> None

BreakpointsList(more_actions: Iterable[PySide6.QtGui.QAction] = (), parent=None)
139    def __init__(self, more_actions: Iterable[QAction] = (), parent=None):
140        """``more_actions``: added to every right-click menu (the Debug menu's
141        Disable All and Remove All)."""
142        super().__init__(parent)
143        self.more_actions = list(more_actions)
144        self.entries: list[BreakpointEntry] = []
145        self.setHeaderLabels(["Breakpoint", "Code"])
146        self.setRootIsDecorated(False)
147        self.setUniformRowHeights(True)
148        self.setSelectionMode(QAbstractItemView.SingleSelection)
149        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)  # (the code: the rest)
150        self.setContextMenuPolicy(Qt.CustomContextMenu)
151        self.customContextMenuRequested.connect(self._menu_at)
152        self.itemClicked.connect(self._open)
153        self.itemActivated.connect(self._open)  # (Enter, a double-click)
154        self._hit: tuple[str, int] | None = None  # where the program stopped (selected, the arrow on it)
155        self.mono = QFont()  # the code's font: the editor's (set by the window)
156        self.lines_for: Callable[[str], list[str]] | None = None  # a file's lines (set by the window)
157        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
158        self._formats: dict = {}  # token type -> format
159        self._line_formats: dict[tuple[str, int, str], list | None] = {}
160        self.setItemDelegateForColumn(1, _CodeDelegate(self))

more_actions: added to every right-click menu (the Debug menu's Disable All and Remove All).

def open_requested(unknown):

The type of the None singleton.

def enabled_toggled(unknown):

The type of the None singleton.

def remove_requested(unknown):

The type of the None singleton.

more_actions
entries: list[BreakpointEntry]
mono
lines_for: Callable[[str], list[str]] | None
def set_entries(self, entries: list[BreakpointEntry]):
163    def set_entries(self, entries: list[BreakpointEntry]):
164        """The breakpoints now (the selection kept, if its breakpoint still is)."""
165        if entries == self.entries:
166            return
167        current = self.current_key()
168        self.entries = list(entries)
169        self._line_formats.clear()
170        self.clear()
171        for entry in entries:
172            item = QTreeWidgetItem([entry.shown, entry.code.strip()])
173            item.setIcon(0, breakpoint_icon(entry.state, (entry.path, entry.line) == self._hit))
174            item.setData(0, KEY_ROLE, (entry.path, entry.line))
175            tip = f"{entry.path}:{entry.line}\n{state_text(entry)}"
176            item.setToolTip(0, tip)
177            item.setToolTip(1, tip)
178            if entry.state == DISABLED:
179                item.setForeground(0, self.palette().placeholderText())
180                item.setForeground(1, self.palette().placeholderText())
181            self.addTopLevelItem(item)
182        self._select(self._hit or current)

The breakpoints now (the selection kept, if its breakpoint still is).

def entry_at(self, row: int) -> BreakpointEntry | None:
184    def entry_at(self, row: int) -> BreakpointEntry | None:
185        item = self.topLevelItem(row)
186        if item is None:
187            return None
188        key = tuple(item.data(0, KEY_ROLE))
189        return next((e for e in self.entries if (e.path, e.line) == key), None)
def line_formats(self, entry: BreakpointEntry) -> list | None:
191    def line_formats(self, entry: BreakpointEntry) -> list | None:
192        """The syntax format of each column of a breakpoint's line (None: plain)."""
193        key = (entry.path, entry.line, entry.code)
194        if key not in self._line_formats:
195            lines = self.lines_for(entry.path) if self.lines_for is not None else [entry.code]
196            line = entry.line if self.lines_for is not None else 1
197            self._line_formats[key] = syntax_formats(lines, line, Path(entry.path), self._style, self._formats)
198        return self._line_formats[key]

The syntax format of each column of a breakpoint's line (None: plain).

def refresh_style(self):
200    def refresh_style(self):
201        """The editor's syntax colors (View → Syntax Colors; light or dark)."""
202        self._style = get_style_by_name(hl.style_name(QApplication.palette()))
203        self._formats.clear()
204        self._line_formats.clear()
205        self.viewport().update()

The editor's syntax colors (View → Syntax Colors; light or dark).

def item_for(self, path: str, line: int) -> PySide6.QtWidgets.QTreeWidgetItem | None:
207    def item_for(self, path: str, line: int) -> QTreeWidgetItem | None:
208        for i in range(self.topLevelItemCount()):
209            item = self.topLevelItem(i)
210            if tuple(item.data(0, KEY_ROLE)) == (path, line):
211                return item
212        return None
def current_key(self) -> tuple[str, int] | None:
214    def current_key(self) -> tuple[str, int] | None:
215        item = self.currentItem()
216        return tuple(item.data(0, KEY_ROLE)) if item is not None and item.isSelected() else None
def show_hit(self, where: tuple[str, int] | None):
218    def show_hit(self, where: tuple[str, int] | None):
219        """The breakpoint the program stopped at, selected (None: it's not
220        stopped at one: nothing selected)."""
221        for entry in self.entries:  # (the arrow: off the one it was on, onto this one)
222            if (entry.path, entry.line) in (self._hit, where):
223                self.item_for(entry.path, entry.line).setIcon(
224                    0, breakpoint_icon(entry.state, (entry.path, entry.line) == where))
225        self._hit = where
226        self._select(where)

The breakpoint the program stopped at, selected (None: it's not stopped at one: nothing selected).

def menu_for( self, item: PySide6.QtWidgets.QTreeWidgetItem | None) -> PySide6.QtWidgets.QMenu:
243    def menu_for(self, item: QTreeWidgetItem | None) -> QMenu:
244        menu = QMenu(self)
245        if item is not None:
246            path, line = item.data(0, KEY_ROLE)
247            entry = next(e for e in self.entries if (e.path, e.line) == (path, line))
248            menu.addAction("Go to Line", lambda: self.open_requested.emit(path, line))
249            menu.addAction("Enable Breakpoint" if entry.state == DISABLED else "Disable Breakpoint",
250                           lambda: self.enabled_toggled.emit(path, line))
251            menu.addAction("Remove Breakpoint", lambda: self.remove_requested.emit(path, line))
252            if self.more_actions:
253                menu.addSeparator()
254        menu.addActions(self.more_actions)
255        return menu
def keyPressEvent(self, event):
265    def keyPressEvent(self, event):
266        key = self.current_key()
267        if event.key() in (Qt.Key_Delete, Qt.Key_Backspace) and key is not None:
268            self.remove_requested.emit(*key)
269            return
270        super().keyPressEvent(event)

keyPressEvent(self, event: PySide6.QtGui.QKeyEvent, /) -> None

staticMetaObject = PySide6.QtCore.QMetaObject("BreakpointsList" inherits "QTreeWidget": Methods: #103 type=Signal, signature=open_requested(QString,int), parameters=QString, int #104 type=Signal, signature=enabled_toggled(QString,int), parameters=QString, int #105 type=Signal, signature=remove_requested(QString,int), parameters=QString, int )