Extend completer for editor to also support comments

This commit is contained in:
Veronica Berglyd Olsen
2025-05-18 21:56:58 +02:00
parent 779026408e
commit f2f2e6cb46
+60 -21
View File
@@ -3,15 +3,16 @@ novelWriter GUI Document Editor
================================= =================================
File History: File History:
Created: 2018-09-29 [0.0.1] GuiDocEditor Created: 2018-09-29 [0.0.1] GuiDocEditor
Created: 2019-04-22 [0.0.1] BackgroundWordCounter Created: 2019-04-22 [0.0.1] BackgroundWordCounter
Created: 2019-09-29 [0.2.1] GuiDocEditSearch Created: 2019-09-29 [0.2.1] GuiDocEditSearch
Created: 2020-04-25 [0.4.5] GuiDocEditHeader Created: 2020-04-25 [0.4.5] GuiDocEditHeader
Rewritten: 2020-06-15 [0.9] GuiDocEditSearch Rewritten: 2020-06-15 [0.9] GuiDocEditSearch
Created: 2020-06-27 [0.10] GuiDocEditFooter Created: 2020-06-27 [0.10] GuiDocEditFooter
Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter
Created: 2023-11-06 [2.2b1] MetaCompleter Created: 2023-11-06 [2.2b1] MetaCompleter
Created: 2023-11-07 [2.2b1] GuiDocToolBar Created: 2023-11-07 [2.2b1] GuiDocToolBar
Extended: 2025-05-18 [2.7rc1] CommandCompleter
This file is a part of novelWriter This file is a part of novelWriter
Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
@@ -149,7 +150,7 @@ class GuiDocEditor(QPlainTextEdit):
self._autoReplace = TextAutoReplace() self._autoReplace = TextAutoReplace()
# Completer # Completer
self._completer = MetaCompleter(self) self._completer = CommandCompleter(self)
self._completer.complete.connect(self._insertCompletion) self._completer.complete.connect(self._insertCompletion)
# Create Custom Document # Create Custom Document
@@ -1079,13 +1080,16 @@ class GuiDocEditor(QPlainTextEdit):
if (block := self._qDocument.findBlock(pos)).isValid(): if (block := self._qDocument.findBlock(pos)).isValid():
text = block.text() text = block.text()
if text.startswith("@") and added + removed == 1: if text and text[0] in "@%" and added + removed == 1:
# Only run on single character changes, or it will trigger # Only run on single character changes, or it will trigger
# at unwanted times when other changes are made to the document # at unwanted times when other changes are made to the document
cursor = self.textCursor() cursor = self.textCursor()
bPos = cursor.positionInBlock() bPos = cursor.positionInBlock()
if bPos > 0 and (viewport := self.viewport()): if bPos > 0 and (viewport := self.viewport()):
show = self._completer.updateText(text, bPos) if text[0] == "@":
show = self._completer.updateMetaText(text, bPos)
else:
show = self._completer.updateCommentText(text, bPos)
point = self.cursorRect().bottomRight() point = self.cursorRect().bottomRight()
self._completer.move(viewport.mapToGlobal(point)) self._completer.move(viewport.mapToGlobal(point))
self._completer.setVisible(show) self._completer.setVisible(show)
@@ -2073,13 +2077,13 @@ class GuiDocEditor(QPlainTextEdit):
return return
class MetaCompleter(QMenu): class CommandCompleter(QMenu):
"""GuiWidget: Meta Completer Menu """GuiWidget: Command Completer Menu
This is a context menu with options populated from the user's This is a context menu with options populated from the user's
defined tags. It also helps to type the meta data keyword on a new defined tags and keys. It also helps to type the meta data keyword
line starting with an @. The updateText function should be called on on a new line starting with @ or %. The update functions should be
every keystroke on a line starting with @. called on every keystroke on a line starting with @ or %.
""" """
complete = pyqtSignal(int, int, str) complete = pyqtSignal(int, int, str)
@@ -2088,14 +2092,14 @@ class MetaCompleter(QMenu):
super().__init__(parent=parent) super().__init__(parent=parent)
return return
def updateText(self, text: str, pos: int) -> bool: def updateMetaText(self, text: str, pos: int) -> bool:
"""Update the menu options based on the line of text.""" """Update the menu options based on the line of text."""
self.clear() self.clear()
kw, sep, _ = text.partition(":") kw, sep, _ = text.partition(":")
if pos <= len(kw): if pos <= len(kw):
offset = 0 offset = 0
length = len(kw.rstrip()) length = len(kw.rstrip())
suffix = "" if sep else ":" suffix = "" if sep else ": "
options = list(filter( options = list(filter(
lambda x: x.startswith(kw.rstrip()), nwKeyWords.VALID_KEYS lambda x: x.startswith(kw.rstrip()), nwKeyWords.VALID_KEYS
)) ))
@@ -2108,7 +2112,7 @@ class MetaCompleter(QMenu):
offset = tPos[index] if lookup else pos offset = tPos[index] if lookup else pos
length = len(lookup) length = len(lookup)
suffix = "" suffix = ""
options = list(filter( options = sorted(filter(
lambda x: lookup in x.lower(), SHARED.project.index.getClassTags( lambda x: lookup in x.lower(), SHARED.project.index.getClassTags(
nwKeyWords.KEY_CLASS.get(kw.strip()) nwKeyWords.KEY_CLASS.get(kw.strip())
) )
@@ -2117,13 +2121,48 @@ class MetaCompleter(QMenu):
if not options: if not options:
return False return False
for value in sorted(options): for value in options:
rep = value + suffix rep = value + suffix
action = qtAddAction(self, value) action = qtAddAction(self, value)
action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep)) action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep))
return True return True
def updateCommentText(self, text: str, pos: int) -> bool:
"""Update the menu options based on the line of text."""
self.clear()
cmd, sep, _ = text.partition(":")
if pos <= len(cmd):
clean = text[1:].lstrip()[:6].lower()
if clean[:6] == "story.":
pre, _, key = cmd.partition(".")
offset = len(pre) + 1
length = len(key)
suffix = "" if sep else ": "
options = sorted(filter(
lambda x: x.startswith(key.rstrip()),
SHARED.project.index.getStoryKeys(),
))
elif pos < 12:
offset = 0
length = len(cmd.rstrip())
suffix = ""
options = list(filter(
lambda x: x.startswith(cmd.rstrip()),
["%Synopsis: ", "%Short: ", "%Story", "%Note"],
))
else:
return False
if options:
for value in options:
rep = value + suffix
action = qtAddAction(self, rep.rstrip(":. "))
action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep))
return True
return False
## ##
# Events # Events
## ##