Connected the syntax highlicghter to the index. It now shows valid keywords and values

This commit is contained in:
Veronica K. B. Olsen
2019-05-30 19:57:02 +02:00
parent b526b0f5d9
commit a2d0f937ae
8 changed files with 196 additions and 92 deletions
+23 -19
View File
@@ -34,11 +34,12 @@ class GuiDocEditor(QTextEdit):
logger.debug("Initialising DocEditor ...") logger.debug("Initialising DocEditor ...")
# Class Variables # Class Variables
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theParent = theParent self.theParent = theParent
self.docChanged = False self.docChanged = False
self.pwlFile = None self.pwlFile = None
self.spellCheck = False self.spellCheck = False
self.theDocument = theParent.theDocument
# Document Variables # Document Variables
self.charCount = 0 self.charCount = 0
@@ -54,9 +55,9 @@ class GuiDocEditor(QTextEdit):
self.typApos = self.mainConf.fmtApostrophe self.typApos = self.mainConf.fmtApostrophe
# Core Elements # Core Elements
self.theDoc = self.document() self.theQDoc = self.document()
self.theDict = enchant.Dict(self.mainConf.spellLanguage) self.theDict = enchant.Dict(self.mainConf.spellLanguage)
self.hLight = GuiDocHighlighter(self.theDoc, self.theParent.theTheme) self.hLight = GuiDocHighlighter(self.theQDoc, self.theParent)
self.hLight.setDict(self.theDict) self.hLight.setDict(self.theDict)
# Context Menu # Context Menu
@@ -71,8 +72,8 @@ class GuiDocEditor(QTextEdit):
self.clearEditor() self.clearEditor()
self.initEditor() self.initEditor()
self.theDoc.setDocumentMargin(0) self.theQDoc.setDocumentMargin(0)
self.theDoc.contentsChange.connect(self._docChange) self.theQDoc.contentsChange.connect(self._docChange)
# Custom Shortcuts # Custom Shortcuts
QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext) QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext)
@@ -109,7 +110,18 @@ class GuiDocEditor(QTextEdit):
theOpt.setTabStopDistance(self.mainConf.tabWidth) theOpt.setTabStopDistance(self.mainConf.tabWidth)
if self.mainConf.doJustify: if self.mainConf.doJustify:
theOpt.setAlignment(Qt.AlignJustify) theOpt.setAlignment(Qt.AlignJustify)
self.theDoc.setDefaultTextOption(theOpt) self.theQDoc.setDefaultTextOption(theOpt)
return True
def loadText(self, tHandle):
self.hLight.setHandle(tHandle)
self.setPlainText(self.theDocument.openDocument(tHandle))
self.setCursorPosition(self.theDocument.theItem.cursorPos)
self.lastEdit = time()
self._runCounter()
self.wcTimer.start()
self.setDocumentChanged(False)
self.setReadOnly(False)
return True return True
## ##
@@ -121,14 +133,6 @@ class GuiDocEditor(QTextEdit):
self.theParent.statusBar.setDocumentStatus(self.docChanged) self.theParent.statusBar.setDocumentStatus(self.docChanged)
return self.docChanged return self.docChanged
def setText(self, theText):
self.setPlainText(theText)
self.lastEdit = time()
self._runCounter()
self.wcTimer.start()
self.setDocumentChanged(False)
return True
def getText(self): def getText(self):
theText = self.toPlainText() theText = self.toPlainText()
return theText return theText
@@ -296,7 +300,7 @@ class GuiDocEditor(QTextEdit):
if not self.wcTimer.isActive(): if not self.wcTimer.isActive():
self.wcTimer.start() self.wcTimer.start()
if self.mainConf.doReplace and not self.hasSelection: if self.mainConf.doReplace and not self.hasSelection:
self._docAutoReplace(self.theDoc.findBlock(thePos)) self._docAutoReplace(self.theQDoc.findBlock(thePos))
# logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6)) # logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6))
return return
+84 -38
View File
@@ -20,14 +20,17 @@ logger = logging.getLogger(__name__)
class GuiDocHighlighter(QSyntaxHighlighter): class GuiDocHighlighter(QSyntaxHighlighter):
def __init__(self, theDoc, theTheme): def __init__(self, theDoc, theParent):
QSyntaxHighlighter.__init__(self, theDoc) QSyntaxHighlighter.__init__(self, theDoc)
logger.debug("Initialising DocHighlighter ...") logger.debug("Initialising DocHighlighter ...")
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theDoc = theDoc self.theDoc = theDoc
self.theTheme = theTheme self.theParent = theParent
self.theTheme = theParent.theTheme
self.theIndex = theParent.theIndex
self.theDict = None self.theDict = None
self.theHandle = None
self.spellCheck = False self.spellCheck = False
self.hRules = [] self.hRules = []
@@ -90,12 +93,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
)) ))
# Keyword/Value # Keyword/Value
self.hRules.append(( # self.hRules.append((
r"^(@.+?)\s*:\s*(.+?)$", { # r"^(@.+?)\s*:\s*(.+?)$", {
1 : self.hStyles["keyword"], # 1 : self.hStyles["keyword"],
2 : self.hStyles["value"], # 2 : self.hStyles["value"],
} # }
)) # ))
# Comments # Comments
self.hRules.append(( self.hRules.append((
@@ -152,6 +155,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
return return
##
# Setters
##
def setDict(self, theDict): def setDict(self, theDict):
self.theDict = theDict self.theDict = theDict
return True return True
@@ -160,6 +167,75 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.spellCheck = theMode self.spellCheck = theMode
return True return True
def setHandle(self, theHandle):
self.theHandle = theHandle
return True
##
# Highlight Block
##
def highlightBlock(self, theText):
if self.theHandle is None:
self.setCurrentBlockState(0)
return
if theText.startswith("@"):
# Highlighting of keywords and commands
tItem = self.theParent.theProject.getItem(self.theHandle)
isValid, theBits, thePos = self.theIndex.scanThis(theText)
isGood = self.theIndex.checkThese(theBits, tItem)
if isValid:
for n in range(len(theBits)):
xPos = thePos[n]
xLen = len(theBits[n])
if isGood[n]:
if n == 0:
self.setFormat(xPos, xLen, self.hStyles["keyword"])
else:
self.setFormat(xPos, xLen, self.hStyles["value"])
else:
kwFmt = self.format(xPos)
kwFmt.setUnderlineColor(self.colSpell)
kwFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
self.setFormat(xPos, xLen, kwFmt)
else:
# Other text just uses regex
for rX, xFmt in self.rules:
rxItt = rX.globalMatch(theText, 0)
while rxItt.hasNext():
rxMatch = rxItt.next()
for xM in xFmt.keys():
xPos = rxMatch.capturedStart(xM)
xLen = rxMatch.capturedLength(xM)
self.setFormat(xPos, xLen, xFmt[xM])
self.setCurrentBlockState(0)
if self.theDict is None or not self.spellCheck or theText.startswith("@"):
return
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
while rxSpell.hasNext():
rxMatch = rxSpell.next()
if not self.theDict.check(rxMatch.captured(0)):
if rxMatch.captured(0) == rxMatch.captured(0).upper():
continue
xPos = rxMatch.capturedStart(0)
xLen = rxMatch.capturedLength(0)
spFmt = self.format(xPos)
spFmt.setUnderlineColor(self.colSpell)
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
self.setFormat(xPos, xLen, spFmt)
return
##
# Internal Functions
##
def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None): def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
theFormat = QTextCharFormat() theFormat = QTextCharFormat()
@@ -181,34 +257,4 @@ class GuiDocHighlighter(QSyntaxHighlighter):
return theFormat return theFormat
def highlightBlock(self, theText):
for rX, xFmt in self.rules:
rxItt = rX.globalMatch(theText, 0)
while rxItt.hasNext():
rxMatch = rxItt.next()
for xM in xFmt.keys():
xPos = rxMatch.capturedStart(xM)
xLen = rxMatch.capturedLength(xM)
self.setFormat(xPos, xLen, xFmt[xM])
self.setCurrentBlockState(0)
if self.theDict is None or not self.spellCheck or theText.startswith("@"):
return
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
while rxSpell.hasNext():
rxMatch = rxSpell.next()
if not self.theDict.check(rxMatch.captured(0)):
if rxMatch.captured(0) == rxMatch.captured(0).upper(): continue
xPos = rxMatch.capturedStart(0)
xLen = rxMatch.capturedLength(0)
spFmt = self.format(xPos)
spFmt.setUnderlineColor(self.colSpell)
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
self.setFormat(xPos, xLen, spFmt)
return
# END Class DocHighlighter # END Class DocHighlighter
+8 -10
View File
@@ -53,7 +53,7 @@ class GuiMain(QMainWindow):
self.theTheme = Theme() self.theTheme = Theme()
self.theProject = NWProject(self) self.theProject = NWProject(self)
self.theDocument = NWDoc(self.theProject, self) self.theDocument = NWDoc(self.theProject, self)
self.tagIndex = NWIndex(self.theProject, self) self.theIndex = NWIndex(self.theProject, self)
self.hasProject = False self.hasProject = False
self.resize(*self.mainConf.winGeometry) self.resize(*self.mainConf.winGeometry)
@@ -212,7 +212,7 @@ class GuiMain(QMainWindow):
if saveOK: if saveOK:
self.theProject.closeProject() self.theProject.closeProject()
self.tagIndex.clearIndex() self.theIndex.clearIndex()
self.clearGUI() self.clearGUI()
self.hasProject = False self.hasProject = False
@@ -236,7 +236,7 @@ class GuiMain(QMainWindow):
return False return False
# Load the tag index # Load the tag index
self.tagIndex.loadIndex() self.theIndex.loadIndex()
# Update GUI # Update GUI
self._setWindowTitle(self.theProject.projName) self._setWindowTitle(self.theProject.projName)
@@ -268,7 +268,7 @@ class GuiMain(QMainWindow):
self.treeView.saveTreeOrder() self.treeView.saveTreeOrder()
self.theProject.saveProject() self.theProject.saveProject()
self.tagIndex.saveIndex() self.theIndex.saveIndex()
self.mainMenu.updateRecentProjects() self.mainMenu.updateRecentProjects()
return True return True
@@ -286,9 +286,7 @@ class GuiMain(QMainWindow):
def openDocument(self, tHandle): def openDocument(self, tHandle):
self.closeDocument() self.closeDocument()
self.docEditor.setText(self.theDocument.openDocument(tHandle)) self.docEditor.loadText(tHandle)
self.docEditor.setReadOnly(False)
self.docEditor.setCursorPosition(self.theDocument.theItem.cursorPos)
self.docEditor.changeWidth() self.docEditor.changeWidth()
self.docEditor.setFocus() self.docEditor.setFocus()
self.theProject.setLastEdited(tHandle) self.theProject.setLastEdited(tHandle)
@@ -305,7 +303,7 @@ class GuiMain(QMainWindow):
theItem.setCursorPos(cursPos) theItem.setCursorPos(cursPos)
self.theDocument.saveDocument(docText) self.theDocument.saveDocument(docText)
self.docEditor.setDocumentChanged(False) self.docEditor.setDocumentChanged(False)
self.tagIndex.scanText(theItem.itemHandle, docText) self.theIndex.scanText(theItem.itemHandle, docText)
return True return True
def viewDocument(self, tHandle=None): def viewDocument(self, tHandle=None):
@@ -385,7 +383,7 @@ class GuiMain(QMainWindow):
logger.debug("Rebuilding indices ...") logger.debug("Rebuilding indices ...")
self.treeView.saveTreeOrder() self.treeView.saveTreeOrder()
self.tagIndex.clearIndex() self.theIndex.clearIndex()
nItems = len(self.theProject.treeOrder) nItems = len(self.theProject.treeOrder)
dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self) dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self)
@@ -419,7 +417,7 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount() self.treeView.projectWordCount()
# Build tag index # Build tag index
self.tagIndex.scanText(tHandle, theText) self.theIndex.scanText(tHandle, theText)
time.sleep(0.05) time.sleep(0.05)
nDone += 1 nDone += 1
+52 -22
View File
@@ -36,24 +36,25 @@ logger = logging.getLogger(__name__)
class NWIndex(): class NWIndex():
TAG_KEY = "@tag" TAG_KEY = "@tag"
POV_KEY = "@pov" POV_KEY = "@pov"
CHAR_KEY = "@char" CHAR_KEY = "@char"
PLOT_KEY = "@plot" PLOT_KEY = "@plot"
TIME_KEY = "@time" TIME_KEY = "@time"
WORLD_KEY = "@location" WORLD_KEY = "@location"
OBJECT_KEY = "@object" OBJECT_KEY = "@object"
CUSTOM_KEY = "@custom" CUSTOM_KEY = "@custom"
NOTE_KEYS = [PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY] NOTE_KEYS = [TAG_KEY]
VALID_CLASS = { NOVEL_KEYS = [PLOT_KEY, POV_KEY, CHAR_KEY, WORLD_KEY, TIME_KEY, OBJECT_KEY, CUSTOM_KEY]
nwItemClass.NOVEL : [], TAG_CLASS = {
nwItemClass.PLOT : [PLOT_KEY], POV_KEY : nwItemClass.CHARACTER,
nwItemClass.CHARACTER : [POV_KEY, CHAR_KEY], CHAR_KEY : nwItemClass.CHARACTER,
nwItemClass.WORLD : [WORLD_KEY], PLOT_KEY : nwItemClass.PLOT,
nwItemClass.TIMELINE : [TIME_KEY], TIME_KEY : nwItemClass.TIMELINE,
nwItemClass.OBJECT : [OBJECT_KEY], WORLD_KEY : nwItemClass.WORLD,
nwItemClass.CUSTOM : [CUSTOM_KEY], OBJECT_KEY : nwItemClass.OBJECT,
CUSTOM_KEY : nwItemClass.CUSTOM,
} }
def __init__(self, theProject, theParent): def __init__(self, theProject, theParent):
@@ -137,7 +138,8 @@ class NWIndex():
theItem = self.theProject.getItem(tHandle) theItem = self.theProject.getItem(tHandle)
if theItem is None: return False if theItem is None: return False
if theItem.itemType != nwItemType.FILE: return False if theItem.itemType != nwItemType.FILE: return False
itemClass = theItem.itemClass itemClass = theItem.itemClass
itemLayout = theItem.itemLayout
logger.debug("Indexing item with handle %s" % tHandle) logger.debug("Indexing item with handle %s" % tHandle)
@@ -162,7 +164,7 @@ class NWIndex():
if nChar == 0: continue if nChar == 0: continue
if aLine[0] == "#": if aLine[0] == "#":
if isNovel: if isNovel:
self.indexTitle(tHandle, aLine, nLine) self.indexTitle(tHandle, aLine, nLine, itemLayout)
elif aLine[0] == "@": elif aLine[0] == "@":
if isNovel: if isNovel:
self.indexNoteRef(tHandle, aLine, nLine) self.indexNoteRef(tHandle, aLine, nLine)
@@ -171,7 +173,7 @@ class NWIndex():
return True return True
def indexTitle(self, tHandle, aLine, nLine): def indexTitle(self, tHandle, aLine, nLine, itemLayout):
if aLine.startswith("# "): if aLine.startswith("# "):
hDepth = 1 hDepth = 1
@@ -189,7 +191,7 @@ class NWIndex():
return False return False
if hText != "": if hText != "":
self.novelIndex[tHandle].append([nLine, hDepth, hText]) self.novelIndex[tHandle].append([nLine, hDepth, hText, itemLayout.name])
return True return True
@@ -200,7 +202,7 @@ class NWIndex():
return False return False
theKey = theBits[0] theKey = theBits[0]
if theKey in self.NOTE_KEYS: if theKey in self.NOVEL_KEYS:
for aVal in theBits[1:]: for aVal in theBits[1:]:
self.noteIndex[tHandle].append([nLine, theKey, aVal]) self.noteIndex[tHandle].append([nLine, theKey, aVal])
@@ -254,4 +256,32 @@ class NWIndex():
return True, theBits, thePos return True, theBits, thePos
def checkThese(self, theBits, tItem):
theBits = [aBit.lower() for aBit in theBits]
nBits = len(theBits)
isGood = [False]*nBits
if nBits == 0:
return []
# If we have a tag, the first value is always OK, rest is ignored
if theBits[0] == self.TAG_KEY and nBits > 1:
isGood[0] = True
isGood[1] = True
return isGood
# If we're still here, we better check the references
if tItem.itemClass == nwItemClass.NOVEL:
isGood[0] = theBits[0] in self.NOVEL_KEYS
else:
isGood[0] = theBits[0] in self.NOTE_KEYS
if not isGood[0] or nBits == 1:
return isGood
for n in range(1,nBits):
if theBits[n] in self.tagIndex:
isGood[n] = self.TAG_CLASS[theBits[0]].name == self.tagIndex[theBits[n]][2]
return isGood
# END Class NWIndex # END Class NWIndex
@@ -5,6 +5,7 @@
% Begin Meta % Begin Meta
@POV: Jane @POV: Jane
@Char: Jane, John @Char: Jane, John
@Location: Earth
% End Meta % End Meta
Some text here would look good as well, and maybe some "dialogue"? Some text here would look good as well, and maybe some "dialogue"?
@@ -1,3 +1,5 @@
# Earth # Earth
@tag: Earth
Third planet from the sun, fairly dense, and with lots of people on it. Third planet from the sun, fairly dense, and with lots of people on it.
+23
View File
@@ -101,3 +101,26 @@ Start: 2019-05-30 18:26:01 End: 2019-05-30 18:27:56 Words: 0
Start: 2019-05-30 18:28:00 End: 2019-05-30 18:30:22 Words: 0 Start: 2019-05-30 18:28:00 End: 2019-05-30 18:30:22 Words: 0
Start: 2019-05-30 18:42:16 End: 2019-05-30 18:43:10 Words: 0 Start: 2019-05-30 18:42:16 End: 2019-05-30 18:43:10 Words: 0
Start: 2019-05-30 18:43:24 End: 2019-05-30 18:43:31 Words: 0 Start: 2019-05-30 18:43:24 End: 2019-05-30 18:43:31 Words: 0
Start: 2019-05-30 18:48:54 End: 2019-05-30 18:49:19 Words: 0
Start: 2019-05-30 18:49:23 End: 2019-05-30 18:49:53 Words: 0
Start: 2019-05-30 18:51:06 End: 2019-05-30 19:02:40 Words: 0
Start: 2019-05-30 19:07:17 End: 2019-05-30 19:07:26 Words: 0
Start: 2019-05-30 19:17:16 End: 2019-05-30 19:17:39 Words: 0
Start: 2019-05-30 19:17:44 End: 2019-05-30 19:17:55 Words: 0
Start: 2019-05-30 19:18:44 End: 2019-05-30 19:19:15 Words: 0
Start: 2019-05-30 19:19:20 End: 2019-05-30 19:19:47 Words: 0
Start: 2019-05-30 19:21:41 End: 2019-05-30 19:21:44 Words: 0
Start: 2019-05-30 19:24:00 End: 2019-05-30 19:24:03 Words: 0
Start: 2019-05-30 19:24:45 End: 2019-05-30 19:26:48 Words: 0
Start: 2019-05-30 19:28:23 End: 2019-05-30 19:32:41 Words: 0
Start: 2019-05-30 19:32:57 End: 2019-05-30 19:33:18 Words: 0
Start: 2019-05-30 19:33:22 End: 2019-05-30 19:33:39 Words: 0
Start: 2019-05-30 19:34:08 End: 2019-05-30 19:34:31 Words: 0
Start: 2019-05-30 19:34:43 End: 2019-05-30 19:34:45 Words: 0
Start: 2019-05-30 19:34:58 End: 2019-05-30 19:36:33 Words: 0
Start: 2019-05-30 19:36:38 End: 2019-05-30 19:36:47 Words: 0
Start: 2019-05-30 19:43:30 End: 2019-05-30 19:43:35 Words: 0
Start: 2019-05-30 19:43:42 End: 2019-05-30 19:43:49 Words: 0
Start: 2019-05-30 19:45:34 End: 2019-05-30 19:45:59 Words: 0
Start: 2019-05-30 19:53:25 End: 2019-05-30 19:55:00 Words: 0
Start: 2019-05-30 19:55:21 End: 2019-05-30 19:56:16 Words: 0
+3 -3
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.1.4" fileVersion="1.0" timeStamp="2019-05-30 18:43:31"> <novelWriterXML appVersion="0.1.4" fileVersion="1.0" timeStamp="2019-05-30 19:56:16">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
@@ -76,7 +76,7 @@
<charCount>656</charCount> <charCount>656</charCount>
<wordCount>121</wordCount> <wordCount>121</wordCount>
<paraCount>5</paraCount> <paraCount>5</paraCount>
<cursorPos>729</cursorPos> <cursorPos>105</cursorPos>
</item> </item>
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a"> <item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
<name>New File</name> <name>New File</name>
@@ -145,7 +145,7 @@
<charCount>76</charCount> <charCount>76</charCount>
<wordCount>15</wordCount> <wordCount>15</wordCount>
<paraCount>1</paraCount> <paraCount>1</paraCount>
<cursorPos>0</cursorPos> <cursorPos>20</cursorPos>
</item> </item>
<item handle="98acd8c76c93a" order="3" parent="None"> <item handle="98acd8c76c93a" order="3" parent="None">
<name>Trash</name> <name>Trash</name>