Add option to create note from tag, and improve auto-completer a bit

This commit is contained in:
Veronica Berglyd Olsen
2023-11-06 22:25:28 +01:00
parent 1ab35b5b63
commit f17e1f38de
4 changed files with 65 additions and 23 deletions
+9
View File
@@ -61,6 +61,15 @@ class nwItemLayout(Enum):
# END Enum nwItemLayout
class nwTrinary(Enum):
NEGATIVE = -1
UNKNOWN = 0
POSITIVE = 1
# END Enum nwTrinary
class nwDocMode(Enum):
VIEW = 0
+40 -22
View File
@@ -51,9 +51,10 @@ from PyQt5.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass
from novelwriter.core.item import NWItem
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.constants import nwKeyWords, nwLabels, nwUnicode, trConst
from novelwriter.core.index import countWords
from novelwriter.core.document import NWDocument
from novelwriter.gui.dochighlight import GuiDocHighlighter
@@ -149,7 +150,7 @@ class GuiDocEditor(QPlainTextEdit):
self.keyContext = QShortcut(self)
self.keyContext.setKey("Ctrl+.")
self.keyContext.setContext(Qt.WidgetShortcut)
self.keyContext.activated.connect(self._openSpellContext)
self.keyContext.activated.connect(self._openContextFromCursor)
self.followTag1 = QShortcut(self)
self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
@@ -974,10 +975,9 @@ class GuiDocEditor(QPlainTextEdit):
bPos = cursor.positionInBlock()
if bPos > 0:
show = self._completer.updateText(text, bPos)
if not self._completer.isVisible() and show:
point = self.cursorRect().bottomRight()
self._completer.move(self.viewport().mapToGlobal(point))
self._completer.show()
point = self.cursorRect().bottomRight()
self._completer.move(self.viewport().mapToGlobal(point))
self._completer.setVisible(show)
elif self._doReplace and added == 1:
self._docAutoReplace(text)
@@ -994,6 +994,7 @@ class GuiDocEditor(QPlainTextEdit):
cursor.setPosition(pos, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(pos + length, QTextCursor.MoveMode.KeepAnchor)
cursor.insertText(text)
self._completer.hide()
return
@pyqtSlot("QPoint")
@@ -1007,9 +1008,14 @@ class GuiDocEditor(QPlainTextEdit):
ctxMenu = QMenu(self)
# Follow
if self._followTag(cursor=pCursor, loadTag=False):
status = self._followTag(cursor=pCursor, process=False)
if status == nwTrinary.POSITIVE:
aTag = ctxMenu.addAction(self.tr("Follow Tag"))
aTag.triggered.connect(lambda: self._followTag(cursor=pCursor))
aTag.triggered.connect(lambda: self._followTag(cursor=pCursor, process=True))
ctxMenu.addSeparator()
elif status == nwTrinary.NEGATIVE:
aTag = ctxMenu.addAction(self.tr("Create Note for Tag"))
aTag.triggered.connect(lambda: self._followTag(cursor=pCursor, process=True))
ctxMenu.addSeparator()
# Cut, Copy and Paste
@@ -1690,7 +1696,7 @@ class GuiDocEditor(QPlainTextEdit):
# Internal Functions
##
def _followTag(self, cursor: QTextCursor | None = None, loadTag: bool = True) -> bool:
def _followTag(self, cursor: QTextCursor | None = None, process: bool = True) -> nwTrinary:
"""Activated by Ctrl+Enter. Checks that we're in a block
starting with '@'. We then find the tag under the cursor and
check that it is not the tag itself. If all this is fine, we
@@ -1702,41 +1708,53 @@ class GuiDocEditor(QPlainTextEdit):
block = cursor.block()
text = block.text()
if len(text) == 0:
return False
return nwTrinary.UNKNOWN
if text.startswith("@"):
if text.startswith("@") and isinstance(self._nwItem, NWItem):
isGood, tBits, tPos = SHARED.project.index.scanThis(text)
if not isGood:
return False
return nwTrinary.UNKNOWN
tag = ""
exist = False
cPos = cursor.selectionStart() - block.position()
for sTag, sPos in zip(reversed(tBits), reversed(tPos)):
tExist = SHARED.project.index.checkThese(tBits, self._nwItem)
for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
if cPos >= sPos:
# The cursor is between the start of two tags
if cPos <= sPos + len(sTag):
# The cursor is inside or at the edge of the tag
tag = sTag
exist = sExist
break
if not tag or tag.startswith("@"):
# The keyword cannot be looked up, so we ignore that
return False
return nwTrinary.UNKNOWN
if loadTag:
if process and exist:
logger.debug("Attempting to follow tag '%s'", tag)
self.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
else:
logger.debug("Potential tag '%s'", tag)
elif process and not exist:
if SHARED.question(self.tr(
"Do you want to create a new project note for the tag '{0}'?"
).format(tag)):
itemClass = nwKeyWords.KEY_CLASS.get(tBits[0], nwItemClass.NO_CLASS)
if SHARED.mainGui.projView.createNewNote(tag, itemClass):
self._qDocument.syntaxHighlighter.rehighlightBlock(block)
else:
SHARED.error(self.tr(
"Could not create note in a root folder for '{0}'. "
"If one doesn't exist, you must create one first."
).format(trConst(nwLabels.CLASS_NAME[itemClass])))
return True
return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
return False
return nwTrinary.UNKNOWN
def _openSpellContext(self) -> None:
def _openContextFromCursor(self) -> None:
"""Open the spell check context menu at the cursor."""
self._openContextMenu(self.cursorRect().center())
return
+15
View File
@@ -147,6 +147,7 @@ class GuiProjectView(QWidget):
self.getSelectedHandle = self.projTree.getSelectedHandle
self.setSelectedHandle = self.projTree.setSelectedHandle
self.changedSince = self.projTree.changedSince
self.createNewNote = self.projTree.createNewNote
return
@@ -570,6 +571,20 @@ class GuiProjectTree(QTreeWidget):
self._timeChanged = 0.0
return
def createNewNote(self, tag: str, itemClass: nwItemClass | None) -> bool:
"""Create a new note. This function is used by the document
editor to create note files for unknown tags.
"""
rHandle = SHARED.project.tree.findRoot(itemClass)
if rHandle:
tHandle = SHARED.project.newFile(tag, rHandle)
if tHandle:
text = f"# {tag}\n\n@tag: {tag}\n\n"
SHARED.project.writeNewFile(tHandle, 1, False, text)
self.revealNewTreeItem(tHandle, wordCount=True)
return True
return False
def newTreeItem(self, itemType: nwItemType, itemClass: nwItemClass | None = None,
hLevel: int = 1, isNote: bool = False) -> bool:
"""Add new item to the tree, with a given itemType (and
+1 -1
View File
@@ -530,7 +530,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
with monkeypatch.context() as mp:
mp.setattr(QMenu, "exec_", lambda *a: None)
docEditor.setCursorPosition(errPos)
docEditor._openSpellContext()
docEditor._openContextFromCursor()
# Check Files
# ===========