Fix code backticks to not show but still be able to type code easily

This commit is contained in:
Miguel Jacq 2025-11-10 08:40:09 +11:00
parent bc9fa86281
commit 4f4735cfb6
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
3 changed files with 77 additions and 5 deletions

View file

@ -1,3 +1,8 @@
# 0.2.1.3
* Ensure checkbox only can get checked on/off if it is clicked right on its block position, not any click on the whole line
* Fix code backticks to not show but still be able to type code easily
# 0.2.1.2 # 0.2.1.2
* Ensure tabs are ordered by calendar date * Ensure tabs are ordered by calendar date

View file

@ -34,6 +34,7 @@ class MarkdownHighlighter(QSyntaxHighlighter):
# Recompute formats whenever the app theme changes # Recompute formats whenever the app theme changes
try: try:
self.theme_manager.themeChanged.connect(self._on_theme_changed) self.theme_manager.themeChanged.connect(self._on_theme_changed)
self.textChanged.connect(self._refresh_codeblock_margins)
except Exception: except Exception:
pass pass
@ -65,6 +66,7 @@ class MarkdownHighlighter(QSyntaxHighlighter):
self.code_block_format = QTextCharFormat() self.code_block_format = QTextCharFormat()
self.code_block_format.setFont(mono) self.code_block_format.setFont(mono)
self.code_block_format.setFontFixedPitch(True) self.code_block_format.setFontFixedPitch(True)
pal = QGuiApplication.palette() pal = QGuiApplication.palette()
if self.theme_manager.current() == Theme.DARK: if self.theme_manager.current() == Theme.DARK:
# In dark mode, use a darker panel-like background # In dark mode, use a darker panel-like background
@ -97,6 +99,36 @@ class MarkdownHighlighter(QSyntaxHighlighter):
# Also make them very faint in case they still show # Also make them very faint in case they still show
self.syntax_format.setForeground(QColor(250, 250, 250)) self.syntax_format.setForeground(QColor(250, 250, 250))
def _refresh_codeblock_margins(self):
"""Give code blocks a small left/right margin to separate them visually."""
doc = self.document()
block = doc.begin()
in_code = False
while block.isValid():
txt = block.text().strip()
cursor = QTextCursor(block)
fmt = block.blockFormat()
if txt.startswith("```"):
# fence lines: small vertical spacing, same left indent
need = (12, 6, 6) # left, top, bottom (px-like)
if (fmt.leftMargin(), fmt.topMargin(), fmt.bottomMargin()) != need:
fmt.setLeftMargin(12)
fmt.setRightMargin(6)
fmt.setTopMargin(6)
fmt.setBottomMargin(6)
cursor.setBlockFormat(fmt)
in_code = not in_code
elif in_code:
# inside the code block
if fmt.leftMargin() != 12 or fmt.rightMargin() != 6:
fmt.setLeftMargin(12)
fmt.setRightMargin(6)
cursor.setBlockFormat(fmt)
block = block.next()
def highlightBlock(self, text: str): def highlightBlock(self, text: str):
"""Apply formatting to a block of text based on markdown syntax.""" """Apply formatting to a block of text based on markdown syntax."""
if not text: if not text:
@ -108,12 +140,17 @@ class MarkdownHighlighter(QSyntaxHighlighter):
# Check for code block fences # Check for code block fences
if text.strip().startswith("```"): if text.strip().startswith("```"):
# Toggle code block state # background for the whole fence line (so block looks continuous)
self.setFormat(0, len(text), self.code_block_format)
# hide the three backticks themselves
idx = text.find("```")
if idx != -1:
self.setFormat(idx, 3, self.syntax_format)
# toggle code-block state and stop; next line picks up state
in_code_block = not in_code_block in_code_block = not in_code_block
self.setCurrentBlockState(1 if in_code_block else 0) self.setCurrentBlockState(1 if in_code_block else 0)
# Format the fence markers - but keep them somewhat visible for editing
# Use code format instead of syntax format so cursor is visible
self.setFormat(0, len(text), self.code_format)
return return
if in_code_block: if in_code_block:
@ -448,6 +485,36 @@ class MarkdownEditor(QTextEdit):
def keyPressEvent(self, event): def keyPressEvent(self, event):
"""Handle special key events for markdown editing.""" """Handle special key events for markdown editing."""
# --- Auto-close code fences when typing the 3rd backtick at line start ---
if event.text() == "`":
c = self.textCursor()
block = c.block()
line = block.text()
pos_in_block = c.position() - block.position()
# text before caret on this line
before = line[:pos_in_block]
# If we've typed exactly two backticks at line start (or after whitespace),
# treat this backtick as the "third" and expand to a full fenced block.
if before.endswith("``") and before.strip() == "``":
start = (
block.position() + pos_in_block - 2
) # start of the two backticks
edit = QTextCursor(self.document())
edit.beginEditBlock()
edit.setPosition(start)
edit.setPosition(start + 2, QTextCursor.KeepAnchor)
edit.insertText("```\n\n```")
edit.endEditBlock()
# place caret on the blank line between the fences
new_pos = start + 4 # after "```\n"
c.setPosition(new_pos)
self.setTextCursor(c)
return
# Handle Enter key for smart list continuation AND code blocks # Handle Enter key for smart list continuation AND code blocks
if event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter: if event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
cursor = self.textCursor() cursor = self.textCursor()

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "bouquin" name = "bouquin"
version = "0.2.1.2" version = "0.2.1.3"
description = "Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher." description = "Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher."
authors = ["Miguel Jacq <mig@mig5.net>"] authors = ["Miguel Jacq <mig@mig5.net>"]
readme = "README.md" readme = "README.md"