Allow deleting code blocks
This commit is contained in:
parent
57f11abb99
commit
dc1046632c
4 changed files with 90 additions and 3 deletions
|
|
@ -13,7 +13,9 @@ from . import strings
|
|||
|
||||
|
||||
class CodeBlockEditorDialog(QDialog):
|
||||
def __init__(self, code: str, language: str | None, parent=None):
|
||||
def __init__(
|
||||
self, code: str, language: str | None, parent=None, allow_delete: bool = False
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(strings._("edit_code_block"))
|
||||
|
||||
|
|
@ -21,6 +23,9 @@ class CodeBlockEditorDialog(QDialog):
|
|||
self._code_edit = QPlainTextEdit(self)
|
||||
self._code_edit.setPlainText(code)
|
||||
|
||||
# Track whether the user clicked "Delete"
|
||||
self._delete_requested = False
|
||||
|
||||
# Language selector (optional)
|
||||
self._lang_combo = QComboBox(self)
|
||||
languages = [
|
||||
|
|
@ -44,12 +49,28 @@ class CodeBlockEditorDialog(QDialog):
|
|||
buttons.accepted.connect(self.accept)
|
||||
buttons.rejected.connect(self.reject)
|
||||
|
||||
if allow_delete:
|
||||
delete_btn = buttons.addButton(
|
||||
strings._("delete_code_block"),
|
||||
QDialogButtonBox.ButtonRole.DestructiveRole,
|
||||
)
|
||||
delete_btn.clicked.connect(self._on_delete_clicked)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(QLabel(strings._("locale") + ":", self))
|
||||
layout.addWidget(self._lang_combo)
|
||||
layout.addWidget(self._code_edit)
|
||||
layout.addWidget(buttons)
|
||||
|
||||
def _on_delete_clicked(self) -> None:
|
||||
"""Mark this dialog as 'delete requested' and close as Accepted."""
|
||||
self._delete_requested = True
|
||||
self.accept()
|
||||
|
||||
def was_deleted(self) -> bool:
|
||||
"""Return True if the user chose to delete the code block."""
|
||||
return self._delete_requested
|
||||
|
||||
def code(self) -> str:
|
||||
return self._code_edit.toPlainText()
|
||||
|
||||
|
|
|
|||
|
|
@ -365,3 +365,7 @@ class CodeBlockMetadata:
|
|||
self._block_languages[int(block_num)] = lang
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def clear_language(self, block_number: int):
|
||||
"""Remove any stored language for a given block, if present."""
|
||||
self._block_languages.pop(block_number, None)
|
||||
|
|
|
|||
|
|
@ -290,5 +290,6 @@
|
|||
"saturday": "Saturday",
|
||||
"sunday": "Sunday",
|
||||
"day": "Day",
|
||||
"edit_code_block": "Edit code block"
|
||||
"edit_code_block": "Edit code block",
|
||||
"delete_code_block": "Delete code block"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,12 +372,17 @@ class MarkdownEditor(QTextEdit):
|
|||
|
||||
code_text = self._get_code_block_text(open_block, close_block)
|
||||
|
||||
dlg = CodeBlockEditorDialog(code_text, lang, parent=self)
|
||||
dlg = CodeBlockEditorDialog(code_text, lang, parent=self, allow_delete=True)
|
||||
result = dlg.exec()
|
||||
if result != QDialog.DialogCode.Accepted:
|
||||
# Dialog was shown but user cancelled; event is "handled".
|
||||
return True
|
||||
|
||||
# If the user requested deletion, remove the whole block
|
||||
if hasattr(dlg, "was_deleted") and dlg.was_deleted():
|
||||
self._delete_code_block(open_block)
|
||||
return True
|
||||
|
||||
new_code = dlg.code()
|
||||
new_lang = dlg.language()
|
||||
|
||||
|
|
@ -396,6 +401,58 @@ class MarkdownEditor(QTextEdit):
|
|||
|
||||
return True
|
||||
|
||||
def _delete_code_block(self, block: QTextBlock) -> bool:
|
||||
"""Delete the fenced code block containing `block`.
|
||||
|
||||
Returns True if a block was deleted, False otherwise.
|
||||
"""
|
||||
bounds = self._find_code_block_bounds(block)
|
||||
if not bounds:
|
||||
return False
|
||||
|
||||
open_block, close_block = bounds
|
||||
doc = self.document()
|
||||
if doc is None:
|
||||
return False
|
||||
|
||||
# Remove from the opening fence down to just before the block after
|
||||
# the closing fence (so we also remove the trailing blank line).
|
||||
start_pos = open_block.position()
|
||||
after_block = close_block.next()
|
||||
if after_block.isValid():
|
||||
end_pos = after_block.position()
|
||||
else:
|
||||
end_pos = close_block.position() + len(close_block.text())
|
||||
|
||||
cursor = QTextCursor(doc)
|
||||
cursor.beginEditBlock()
|
||||
cursor.setPosition(start_pos)
|
||||
cursor.setPosition(end_pos, QTextCursor.MoveMode.KeepAnchor)
|
||||
cursor.removeSelectedText()
|
||||
cursor.endEditBlock()
|
||||
|
||||
# Clear language metadata for this block, if supported
|
||||
if hasattr(self, "_code_metadata"):
|
||||
clear = getattr(self._code_metadata, "clear_language", None)
|
||||
if clear is not None:
|
||||
clear(open_block.blockNumber())
|
||||
|
||||
# Refresh visuals (spacing + backgrounds + syntax)
|
||||
if hasattr(self, "_apply_code_block_spacing"):
|
||||
self._apply_code_block_spacing()
|
||||
if hasattr(self, "_update_code_block_row_backgrounds"):
|
||||
self._update_code_block_row_backgrounds()
|
||||
if hasattr(self, "highlighter"):
|
||||
self.highlighter.rehighlight()
|
||||
|
||||
# Move caret to where the block used to be
|
||||
cursor = self.textCursor()
|
||||
cursor.setPosition(start_pos)
|
||||
self.setTextCursor(cursor)
|
||||
self.setFocus()
|
||||
|
||||
return True
|
||||
|
||||
def _apply_line_spacing(self, height: float = 125.0):
|
||||
"""Apply proportional line spacing to the whole document."""
|
||||
doc = self.document()
|
||||
|
|
@ -1619,6 +1676,10 @@ class MarkdownEditor(QTextEdit):
|
|||
edit_action.triggered.connect(lambda: self._edit_code_block(block))
|
||||
menu.addAction(edit_action)
|
||||
|
||||
delete_action = QAction(strings._("delete_code_block"), self)
|
||||
delete_action.triggered.connect(lambda: self._delete_code_block(block))
|
||||
menu.addAction(delete_action)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
# Add standard context menu actions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue