diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3a2448..5b8065bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ ## Version 0.9rc1 [2020-xx-xx] +**Core Functionality** + +* Underline text formatting has been removed. It is not standard HTML5, not Markdown, and was previously implemented using the double underscore notation that in standard Markdown is renderred as bold text. Instead, novelWriter now renders a single `*` or `-` wrapping a piece of text *within* a paragraphs as italicised text, and a double `**` or `__` as bold text. The keyboard shortcuts and automatic features **only** support the `*` notation. A triple set of `***` are treated as both bold and italicised. PR #310. +* Strikethrough formatting has been added back into novelWriter using the standard Markdown `~~` wrapping. PR #310. + **User Interface** * The Open Project dialog will now ask before removing an entry from the recent projects list. PR #309. +* The text emphasis functions, either selected from the menu or via keyboard shortcuts, will now try to respond to the command in a more meaningful way. That is, the text editor will try to toggle the bold or italics features independently of eachother on the selected text. A feature to apply both at the same time has also been added. PR #310. **Other Changes** diff --git a/README.md b/README.md index bce21ca7..fd119506 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ It allows for a minimal set of formatting needed for writing text documents for These are currently limited to: * Headings level 1 to 4 using the `#` syntax only. -* Bold, italic and underline text. +* Bold, italic and strikethrough text. * Hard line breaks using two or more spaces at the end of a line. That is it. diff --git a/docs/source/interface.rst b/docs/source/interface.rst index 4b4a8662..3174a888 100644 --- a/docs/source/interface.rst +++ b/docs/source/interface.rst @@ -33,8 +33,8 @@ Markdown Format =============== The document editor uses a simplified markdown format. -That is, it supports basic formatting like bold, italics and underline, as well as four levels of headings. -The formats are listed below. +That is, it supports basic formatting like bold, italics and strikethrough, as well as four levels of headings. +The preference of novelWriter is to use `*` for wrapping emphasised text, but `_` is partially supported when typed, but not by the automatic formatting features and keyboard shortcuts. In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or, optionally, hidden in the document viewer. If the first word of a comment is "Synopsis:" (with the colon), the comment is treated specially, and will show up in the Outline View. @@ -48,9 +48,12 @@ The editor also has a minimal set of keywords used for setting tags and referenc "``## Title``", "Heading level two. The space after the # is mandatory." "``### Title``", "Heading level three. The space after the # is mandatory." "``#### Title``", "Heading level four. The space after the # is mandatory." + "``*text*``", "The text is rendered as italicised text." "``**text**``", "The text is rendered as bold text." - "``_text_``", "The text is rendered as italicized text." - "``__text__``", "The text is rendered as underlined text." + "``***text***``", "The text is rendered as bold italicised text." + "``_text_``", "Alternative format for italicised text." + "``__text__``", "Alternative format for bold text." + "``~~text~~``", "Strikethrough text." "``% text...``", "A comment. The text is not exported by default, seen in viewer, or counted towards word counts." "``% Synopsis: text...``", "A synopsis comment. Shows up in the Synopsis column of the Outline View, but is otherwise treated as a comment." "``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values." @@ -110,6 +113,7 @@ These are as following: ":kbd:`Ctrl-.`", "Correct word under cursor." ":kbd:`Ctrl-,`", "Open the Preferences dialog." ":kbd:`Ctrl-/`", "Change block format to comment." + ":kbd:`Ctrl--`", "Strikethrough selected text, or word under cursor." ":kbd:`Ctrl-0`", "Remove block formatting for block under cursor." ":kbd:`Ctrl-1`", "Change block format to header level 1." ":kbd:`Ctrl-2`", "Change block format to header level 2." @@ -122,7 +126,7 @@ These are as following: ":kbd:`Ctrl-E`", "If in tree view, edit a document or folder settings. (Same as :kbd:`F2`)" ":kbd:`Ctrl-F`", "Open the search bar and search for selected word, if any is selected." ":kbd:`Ctrl-G`", "Find next occurrence of word in current document. (Same as :kbd:`F3`)" - ":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected. (On Mac, this is :kbd:`Cmd+=`)" + ":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected. (On Mac, this is :kbd:`Cmd-=`)" ":kbd:`Ctrl-I`", "Format selected text, or word under cursor, as italic." ":kbd:`Ctrl-N`", "Create new document." ":kbd:`Ctrl-O`", "Open selected document." @@ -143,6 +147,7 @@ These are as following: ":kbd:`Ctrl-Shift-/`", "Remove block formatting for block under cursor." ":kbd:`Ctrl-Shift-1`", "Replace occurrence of word in current document, and search for next occurrence." ":kbd:`Ctrl-Shift-A`", "Select all text in current paragraph." + ":kbd:`Ctrl-Shift-B`", "Format selected text, or word under cursor, as bold and italic." ":kbd:`Ctrl-Shift-D`", "Wrap selected text, or word under cursor, in single quotes." ":kbd:`Ctrl-Shift-G`", "Find previous occurrence of word in current document. (Same as :kbd:`Shift-F3`" ":kbd:`Ctrl-Shift-I`", "Import text to the current document from a text file." diff --git a/nw/constants/__init__.py b/nw/constants/__init__.py index 1abd529c..57761e42 100644 --- a/nw/constants/__init__.py +++ b/nw/constants/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from nw.constants.iso import isoLanguage, isoCountry from nw.constants.constants import ( - nwConst, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode + nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode ) from nw.constants.enum import ( nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline @@ -11,6 +11,7 @@ __all__ = [ "isoLanguage", "isoCountry", "nwConst", + "nwRegEx", "nwFiles", "nwKeyWords", "nwLabels", diff --git a/nw/constants/constants.py b/nw/constants/constants.py index 66b026fc..9bd47110 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -34,6 +34,15 @@ class nwConst(): # END Class nwConst +class nwRegEx(): + + FMT_B = r"(? 0: - toCheck = aLine[1:].lstrip().lower() + toCheck = aLine[1:].lstrip() + synTag = toCheck[:9].lower() tLen = len(aLine) cLen = len(toCheck) cOff = tLen - cLen - if toCheck.startswith("synopsis:"): + if synTag == "synopsis:": self._indexSynopsis(tHandle, isNovel, aLine[cOff+9:].strip(), nTitle) # Count words for remaining text after last heading diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 61541f42..22dde913 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -116,14 +116,28 @@ class ToHtml(Tokenizer): """Convert the list of text tokens into a HTML document saved to theResult. """ - htmlTags = { - self.FMT_B_B : "", - self.FMT_B_E : "", - self.FMT_I_B : "", - self.FMT_I_E : "", - self.FMT_U_B : "", - self.FMT_U_E : "", - } + if self.genMode == self.M_PREVIEW: + htmlTags = { # HTML4 + CSS2 + self.FMT_B_B : "", + self.FMT_B_E : "", + self.FMT_I_B : "", + self.FMT_I_E : "", + self.FMT_S_B : "", + self.FMT_S_E : "", + self.FMT_D_B : "", + self.FMT_D_E : "", + } + else: + htmlTags = { # HTML5 + self.FMT_B_B : "", + self.FMT_B_E : "", + self.FMT_I_B : "", + self.FMT_I_E : "", + self.FMT_S_B : "", + self.FMT_S_E : "", + self.FMT_D_B : "", + self.FMT_D_E : "", + } if self.isNovel and self.genMode != self.M_PREVIEW: # For novel files for export, we bump the titles one level diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 70599d59..09345e09 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -34,7 +34,7 @@ from PyQt5.QtCore import QRegularExpression from nw.core.document import NWDoc from nw.core.tools import numberToWord -from nw.constants import nwItemLayout, nwItemType +from nw.constants import nwItemLayout, nwItemType, nwRegEx logger = logging.getLogger(__name__) @@ -44,8 +44,10 @@ class Tokenizer(): FMT_B_E = 2 # End bold FMT_I_B = 3 # Begin italics FMT_I_E = 4 # End italics - FMT_U_B = 5 # Begin underline - FMT_U_E = 6 # End underline + FMT_S_B = 5 # Begin bold italic + FMT_S_E = 6 # End bold italic + FMT_D_B = 7 # Begin strikeout + FMT_D_E = 8 # End strikeout T_EMPTY = 1 # Empty line (new paragraph) T_SYNOPSIS = 2 # Synopsis comment @@ -292,23 +294,19 @@ class Tokenizer(): The format of the token list is an entry with a four-tuple for each line in the file. The tuple is as follows: 1: The type of the block, self.T_* - 2: The text content of the block, without leading tags - 3: The internal formatting map of the text, self.FMT_* - 4: The style of the block, self.A_* + 2: The line in file where this block occurred + 3: The text content of the block, without leading tags + 4: The internal formatting map of the text, self.FMT_* + 5: The style of the block, self.A_* """ # RegExes for adding formatting tags within text lines - # Keep in sync with the DocHighlighter class - rxFormats = [( - QRegularExpression(r"(?", "") + theText = theText.replace("", "") self.setHtml(theText) return diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index bd5da37f..4c5d5ce7 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -509,16 +509,18 @@ class GuiDocEditor(QTextEdit): self.copy() elif theAction == nwDocAction.PASTE: self.paste() - elif theAction == nwDocAction.BOLD: - self._wrapSelection("**","**") elif theAction == nwDocAction.ITALIC: - self._wrapSelection("_","_") - elif theAction == nwDocAction.U_LINE: - self._wrapSelection("__","__") + self._toggleEmph(1) + elif theAction == nwDocAction.BOLD: + self._toggleEmph(2) + elif theAction == nwDocAction.BOLDITALIC: + self._toggleEmph(3) + elif theAction == nwDocAction.STRIKE: + self._toggleStrike() elif theAction == nwDocAction.S_QUOTE: - self._wrapSelection(self.typSQOpen,self.typSQClose) + self._wrapSelection(self.typSQOpen, self.typSQClose) elif theAction == nwDocAction.D_QUOTE: - self._wrapSelection(self.typDQOpen,self.typDQClose) + self._wrapSelection(self.typDQOpen, self.typDQClose) elif theAction == nwDocAction.SEL_ALL: self._makeSelection(QTextCursor.Document) elif theAction == nwDocAction.SEL_PARA: @@ -881,18 +883,20 @@ class GuiDocEditor(QTextEdit): self.bigDoc = False return - def _wrapSelection(self, tBefore, tAfter): + def _wrapSelection(self, tBefore, tAfter=None): """Wraps the selected text in whatever is in tBefore and tAfter. If there is no selection, the autoSelect setting decides the action. AutoSelect will select the word under the cursor before wrapping it. If this feature is disabled, nothing is done. """ - theCursor = self.textCursor() - if self.mainConf.autoSelect and not theCursor.hasSelection(): - theCursor.select(QTextCursor.WordUnderCursor) + if tAfter is None: + tAfter = tBefore + + theCursor = self._autoSelect() if theCursor.hasSelection(): posS = theCursor.selectionStart() posE = theCursor.selectionEnd() + theCursor.clearSelection() theCursor.beginEditBlock() theCursor.setPosition(posE) @@ -900,10 +904,134 @@ class GuiDocEditor(QTextEdit): theCursor.setPosition(posS) theCursor.insertText(tBefore) theCursor.endEditBlock() + + theCursor.setPosition(posE + len(tBefore)) + theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, posE-posS) + self.setTextCursor(theCursor) + else: logger.warning("No selection made, nothing to do") return + def _clearSurrounding(self, theCursor, nChars): + """Clears n characters before and after the cursor. + """ + if theCursor.hasSelection(): + posS = theCursor.selectionStart() + posE = theCursor.selectionEnd() + theCursor.clearSelection() + theCursor.beginEditBlock() + theCursor.setPosition(posS) + for i in range(nChars): + theCursor.deletePreviousChar() + theCursor.setPosition(posE) + for i in range(nChars): + theCursor.deletePreviousChar() + theCursor.endEditBlock() + theCursor.clearSelection() + else: + logger.warning("No selection made, nothing to do") + return + + def _autoSelect(self): + """Returns a cursor which may or may not have a selection based + on user settings and document action. + """ + theCursor = self.textCursor() + if self.mainConf.autoSelect and not theCursor.hasSelection(): + theCursor.select(QTextCursor.WordUnderCursor) + self.setTextCursor(theCursor) + return theCursor + + def _toggleEmph(self, eLevel): + """Toggle emphasis of a given level between 1 and 3, where 1 is + italic, 2 is bold, and 3 is bold italic. The current level in + the text is cLevel. The rules are as follows: + + cLevel | eLevel | Result + ============+============+============ + None | Italic | Italic + None | Bold | Bold + None | BoldItalic | BoldItalic + ------------+------------+------------ + Italic | Italic | None + Italic | Bold | BoldItalic + Italic | BoldItalic | BoldItalic + ------------+------------+------------ + Bold | Italic | BoldItalic + Bold | Bold | None + Bold | BoldItalic | BoldItalic + ------------+------------+------------ + BoldItalic | Italic | Bold + BoldItalic | Bold | Italic + BoldItalic | BoldItalic | None + + """ + theCursor = self._autoSelect() + if theCursor.hasSelection(): + posS = theCursor.selectionStart() + posE = theCursor.selectionEnd() + + numB = 0 + for n in range(3): + if self.qDocument.characterAt(posS-n-1) == "*": + numB += 1 + else: + break + + numA = 0 + for n in range(3): + if self.qDocument.characterAt(posE+n) == "*": + numA += 1 + else: + break + + cLevel = min(numB, numA) + if cLevel == 0: + # Has no emphasis, set to desired level + self._wrapSelection("*"*eLevel) + elif cLevel == 3: + # Has max, so reduce by desired level + self._clearSurrounding(theCursor, eLevel) + elif cLevel == eLevel: + # Toggle mode, so clear what we had set + self._clearSurrounding(theCursor, cLevel) + else: + # Already at 1 or 2, increase to 3 + self._wrapSelection("*"*(3 - cLevel)) + + return + + def _toggleStrike(self): + """Toggle strikethrough text. + """ + theCursor = self._autoSelect() + if theCursor.hasSelection(): + posS = theCursor.selectionStart() + posE = theCursor.selectionEnd() + + numB = 0 + for n in range(2): + if self.qDocument.characterAt(posS-n-1) == "~": + numB += 1 + else: + break + + numA = 0 + for n in range(2): + if self.qDocument.characterAt(posE+n) == "~": + numA += 1 + else: + break + + cLevel = min(numB, numA) + if cLevel == 2: + self._clearSurrounding(theCursor, 2) + else: + self._wrapSelection("~~") + + return + def _formatBlock(self, docAction): """Changes the block format of the block under the cursor. """ diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index afd5a77c..a76c9646 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -33,7 +33,7 @@ from PyQt5.QtGui import ( QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QBrush ) -from nw.constants import nwUnicode +from nw.constants import nwUnicode, nwRegEx logger = logging.getLogger(__name__) @@ -78,7 +78,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): """Initialise the syntax highlighter, setting all the colour rules and building the regexes. """ - logger.debug("Setting up highlighting rules") self.colHead = QColor(*self.theTheme.colHead) @@ -98,28 +97,28 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.colTrail.setAlpha(64) self.hStyles = { - "header1" : self._makeFormat(self.colHead, "bold",1.8), - "header2" : self._makeFormat(self.colHead, "bold",1.6), - "header3" : self._makeFormat(self.colHead, "bold",1.4), - "header4" : self._makeFormat(self.colHead, "bold",1.2), - "header1h" : self._makeFormat(self.colHeadH,"bold",1.8), - "header2h" : self._makeFormat(self.colHeadH,"bold",1.6), - "header3h" : self._makeFormat(self.colHeadH,"bold",1.4), - "header4h" : self._makeFormat(self.colHeadH,"bold",1.2), - "bold" : self._makeFormat(self.colEmph, "bold"), - "italic" : self._makeFormat(self.colEmph, "italic"), - "strike" : self._makeFormat(self.colEmph, "strike"), - "underline" : self._makeFormat(self.colEmph, "underline"), - "trailing" : self._makeFormat(self.colTrail,"background"), - "nobreak" : self._makeFormat(self.colTrail,"background"), - "dialogue1" : self._makeFormat(self.colDialN), - "dialogue2" : self._makeFormat(self.colDialD), - "dialogue3" : self._makeFormat(self.colDialS), - "replace" : self._makeFormat(self.colRepTag), - "hidden" : self._makeFormat(self.colComm), - "keyword" : self._makeFormat(self.colKey), - "modifier" : self._makeFormat(self.colMod), - "value" : self._makeFormat(self.colVal), + "header1" : self._makeFormat(self.colHead, "bold", 1.8), + "header2" : self._makeFormat(self.colHead, "bold", 1.6), + "header3" : self._makeFormat(self.colHead, "bold", 1.4), + "header4" : self._makeFormat(self.colHead, "bold", 1.2), + "header1h" : self._makeFormat(self.colHeadH, "bold", 1.8), + "header2h" : self._makeFormat(self.colHeadH, "bold", 1.6), + "header3h" : self._makeFormat(self.colHeadH, "bold", 1.4), + "header4h" : self._makeFormat(self.colHeadH, "bold", 1.2), + "bold" : self._makeFormat(self.colEmph, "bold"), + "italic" : self._makeFormat(self.colEmph, "italic"), + "bolditalic" : self._makeFormat(self.colEmph, ("bold","italic")), + "strike" : self._makeFormat(self.colEmph, "strike"), + "trailing" : self._makeFormat(self.colTrail, "background"), + "nobreak" : self._makeFormat(self.colTrail, "background"), + "dialogue1" : self._makeFormat(self.colDialN), + "dialogue2" : self._makeFormat(self.colDialD), + "dialogue3" : self._makeFormat(self.colDialS), + "replace" : self._makeFormat(self.colRepTag), + "hidden" : self._makeFormat(self.colComm), + "keyword" : self._makeFormat(self.colKey), + "modifier" : self._makeFormat(self.colMod), + "value" : self._makeFormat(self.colVal, "underline"), } self.hRules = [] @@ -131,7 +130,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): } )) - # Non-breaking Space + # Non-Breaking Spaces self.hRules.append(( "[%s]+" % nwUnicode.U_NBSP, { 0 : self.hStyles["nobreak"], @@ -140,23 +139,30 @@ class GuiDocHighlighter(QSyntaxHighlighter): # Markdown self.hRules.append(( - r"(? Bold Text - self.aFmtBold = QAction("Bold Text", self) - self.aFmtBold.setStatusTip("Make selected text bold") - self.aFmtBold.setShortcut("Ctrl+B") - self.aFmtBold.triggered.connect(lambda: self._docAction(nwDocAction.BOLD)) - self.fmtMenu.addAction(self.aFmtBold) - # Format > Italic Text self.aFmtItalic = QAction("Italic Text", self) self.aFmtItalic.setStatusTip("Make selected text italic") @@ -529,12 +522,26 @@ class GuiMainMenu(QMenuBar): self.aFmtItalic.triggered.connect(lambda: self._docAction(nwDocAction.ITALIC)) self.fmtMenu.addAction(self.aFmtItalic) + # Format > Bold Text + self.aFmtBold = QAction("Bold Text", self) + self.aFmtBold.setStatusTip("Make selected text bold") + self.aFmtBold.setShortcut("Ctrl+B") + self.aFmtBold.triggered.connect(lambda: self._docAction(nwDocAction.BOLD)) + self.fmtMenu.addAction(self.aFmtBold) + # Format > Underline Text - self.aFmtULine = QAction("Underline Text", self) - self.aFmtULine.setStatusTip("Underline selected text") - self.aFmtULine.setShortcut("Ctrl+U") - self.aFmtULine.triggered.connect(lambda: self._docAction(nwDocAction.U_LINE)) - self.fmtMenu.addAction(self.aFmtULine) + self.aFmtBoldIt = QAction("Bold Italic Text", self) + self.aFmtBoldIt.setStatusTip("Make selected text bold and italic") + self.aFmtBoldIt.setShortcut("Ctrl+Shift+B") + self.aFmtBoldIt.triggered.connect(lambda: self._docAction(nwDocAction.BOLDITALIC)) + self.fmtMenu.addAction(self.aFmtBoldIt) + + # Format > Strikethrough + self.aFmtStrike = QAction("Strikethrough Text", self) + self.aFmtStrike.setStatusTip("Strikethrough selected text") + self.aFmtStrike.setShortcut("Ctrl+-") + self.aFmtStrike.triggered.connect(lambda: self._docAction(nwDocAction.STRIKE)) + self.fmtMenu.addAction(self.aFmtStrike) # Edit > Separator self.fmtMenu.addSeparator() diff --git a/nw/guimain.py b/nw/guimain.py index 576cd990..cb2f19a2 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -961,9 +961,9 @@ class GuiMain(QMainWindow): self.addAction(self.mainMenu.aEditPaste) self.addAction(self.mainMenu.aSelectAll) self.addAction(self.mainMenu.aSelectPar) - self.addAction(self.mainMenu.aFmtBold) self.addAction(self.mainMenu.aFmtItalic) - self.addAction(self.mainMenu.aFmtULine) + self.addAction(self.mainMenu.aFmtBold) + self.addAction(self.mainMenu.aFmtBoldIt) self.addAction(self.mainMenu.aFmtDQuote) self.addAction(self.mainMenu.aFmtSQuote) self.addAction(self.mainMenu.aFmtHead1) diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index e93bbb57..4916b753 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -7,7 +7,7 @@ A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. -Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and __underscore__. +Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, *italic* and ***bold italic***. You can also ~~strike through~~ text. In addition, the editor supports automatic formatting of “quotes”, both double and ‘single’. Depending on the syntax highlighter, these can be in different colours. diff --git a/sample/content/bc0cbd2a407f3.nwd b/sample/content/bc0cbd2a407f3.nwd index 94b036c0..4a099dfc 100644 --- a/sample/content/bc0cbd2a407f3.nwd +++ b/sample/content/bc0cbd2a407f3.nwd @@ -14,6 +14,3 @@ In fact, if you wish, you can add all the scenes in the chapter file too. All no @location: Earth This is a second scene in the same file as the previous scene. You can always split the files up later. - - - diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index cee9b2fa..aeba369e 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,21 +1,21 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 280 - 41 - 4312 + 332 + 56 + 10692 False True True - bc0cbd2a407f3 - b3e74dbc1f584 - 920 + 636b6aa9b697b + 636b6aa9b697b + 927 B E @@ -114,10 +114,10 @@ 1st Draft True SCENE - 1199 - 216 + 1240 + 223 7 - 950 + 403 Another Scene @@ -129,7 +129,7 @@ 476 93 3 - 551 + 428 Interlude diff --git a/tests/lipsum/content/04468803b92e1.nwd b/tests/lipsum/content/04468803b92e1.nwd index 0f9e6a1f..af7504dc 100644 --- a/tests/lipsum/content/04468803b92e1.nwd +++ b/tests/lipsum/content/04468803b92e1.nwd @@ -1,4 +1,4 @@ -%%~ 04468803b92e1:60bdf227455cc:Ancient Europe +%%~ 04468803b92e1:60bdf227455cc:WORLD:NOTE:Ancient Europe # Ancient Europe @tag: Europe diff --git a/tests/lipsum/content/2426c6f0ca922.nwd b/tests/lipsum/content/2426c6f0ca922.nwd index bd573995..98ea99c3 100644 --- a/tests/lipsum/content/2426c6f0ca922.nwd +++ b/tests/lipsum/content/2426c6f0ca922.nwd @@ -1,4 +1,4 @@ -%%~ 2426c6f0ca922:6c6afb1247750:Main +%%~ 2426c6f0ca922:6c6afb1247750:PLOT:NOTE:Main # Main Plot @tag: Main diff --git a/tests/lipsum/content/441420a886d82.nwd b/tests/lipsum/content/441420a886d82.nwd index 02eb2fef..820862ba 100644 --- a/tests/lipsum/content/441420a886d82.nwd +++ b/tests/lipsum/content/441420a886d82.nwd @@ -1,4 +1,4 @@ -%%~ 441420a886d82:6bd935d2490cd:b3643d0f92e32:Chapter Two +%%~ 441420a886d82:6bd935d2490cd:b3643d0f92e32:NOVEL:CHAPTER:Chapter Two ## Chapter Two @pov: Bod diff --git a/tests/lipsum/content/47666c91c7ccf.nwd b/tests/lipsum/content/47666c91c7ccf.nwd index 1a173bb7..027b20e5 100644 --- a/tests/lipsum/content/47666c91c7ccf.nwd +++ b/tests/lipsum/content/47666c91c7ccf.nwd @@ -1,4 +1,4 @@ -%%~ 47666c91c7ccf:6bd935d2490cd:b3643d0f92e32:Scene Five +%%~ 47666c91c7ccf:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Five ### Scene Five @pov: Bod diff --git a/tests/lipsum/content/4c4f28287af27.nwd b/tests/lipsum/content/4c4f28287af27.nwd index 9d42bd6b..50f646b5 100644 --- a/tests/lipsum/content/4c4f28287af27.nwd +++ b/tests/lipsum/content/4c4f28287af27.nwd @@ -1,4 +1,4 @@ -%%~ 4c4f28287af27:67a8707f2f249:Mr. Nobody +%%~ 4c4f28287af27:67a8707f2f249:CHARACTER:NOTE:Mr. Nobody # Nobody Owens @tag: Bod diff --git a/tests/lipsum/content/7a992350f3eb6.nwd b/tests/lipsum/content/7a992350f3eb6.nwd index a6a7299d..ab7eb619 100644 --- a/tests/lipsum/content/7a992350f3eb6.nwd +++ b/tests/lipsum/content/7a992350f3eb6.nwd @@ -1,4 +1,4 @@ -%%~ 7a992350f3eb6:b3643d0f92e32:Lorem Ipusm +%%~ 7a992350f3eb6:b3643d0f92e32:NOVEL:TITLE:Lorem Ipusm # Lorem Ipsum **By lipsum.com** diff --git a/tests/lipsum/content/846352075de7d.nwd b/tests/lipsum/content/846352075de7d.nwd index e1d4f542..e7dd7e45 100644 --- a/tests/lipsum/content/846352075de7d.nwd +++ b/tests/lipsum/content/846352075de7d.nwd @@ -1,4 +1,4 @@ -%%~ 846352075de7d:b3643d0f92e32:Interlude +%%~ 846352075de7d:b3643d0f92e32:NOVEL:BOOK:Interlude ## Why do we use it? % Exctracted from the lipsum.com website. diff --git a/tests/lipsum/content/88243afbe5ed8.nwd b/tests/lipsum/content/88243afbe5ed8.nwd index 528b8e77..eba37bae 100644 --- a/tests/lipsum/content/88243afbe5ed8.nwd +++ b/tests/lipsum/content/88243afbe5ed8.nwd @@ -1,4 +1,4 @@ -%%~ 88243afbe5ed8:45e6b01ca35c1:b3643d0f92e32:Scene One +%%~ 88243afbe5ed8:45e6b01ca35c1:b3643d0f92e32:NOVEL:SCENE:Scene One ### Scene One @pov: Bod diff --git a/tests/lipsum/content/88d59a277361b.nwd b/tests/lipsum/content/88d59a277361b.nwd index db2169a5..a67f49f1 100644 --- a/tests/lipsum/content/88d59a277361b.nwd +++ b/tests/lipsum/content/88d59a277361b.nwd @@ -1,6 +1,6 @@ -%%~ 88d59a277361b:b3643d0f92e32:Prologue +%%~ 88d59a277361b:b3643d0f92e32:NOVEL:UNNUMBERED:Prologue ## Prologue % Synopsis:Explanation from the lipsum.com website. -_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. +*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. diff --git a/tests/lipsum/content/8c58a65414c23.nwd b/tests/lipsum/content/8c58a65414c23.nwd index ad83a6c1..408a3bec 100644 --- a/tests/lipsum/content/8c58a65414c23.nwd +++ b/tests/lipsum/content/8c58a65414c23.nwd @@ -1,4 +1,4 @@ -%%~ 8c58a65414c23:b3643d0f92e32:Front Matter +%%~ 8c58a65414c23:b3643d0f92e32:NOVEL:PAGE:Front Matter % Exctracted from the lipsum.com website. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32. diff --git a/tests/lipsum/content/db7e733775d4d.nwd b/tests/lipsum/content/db7e733775d4d.nwd index 7be7896e..7f4de78f 100644 --- a/tests/lipsum/content/db7e733775d4d.nwd +++ b/tests/lipsum/content/db7e733775d4d.nwd @@ -1,4 +1,4 @@ -%%~ db7e733775d4d:b3643d0f92e32:Act One +%%~ db7e733775d4d:b3643d0f92e32:NOVEL:PARTITION:Act One # Act One “Fusce maximus felis libero” \ No newline at end of file diff --git a/tests/lipsum/content/eb103bc70c90c.nwd b/tests/lipsum/content/eb103bc70c90c.nwd index 08bb88e9..db11bbf0 100644 --- a/tests/lipsum/content/eb103bc70c90c.nwd +++ b/tests/lipsum/content/eb103bc70c90c.nwd @@ -1,4 +1,4 @@ -%%~ eb103bc70c90c:6bd935d2490cd:b3643d0f92e32:Scene Three +%%~ eb103bc70c90c:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Three ### Scene Three @pov: Bod diff --git a/tests/lipsum/content/f8c0562e50f1b.nwd b/tests/lipsum/content/f8c0562e50f1b.nwd index 752bd117..15a33bc8 100644 --- a/tests/lipsum/content/f8c0562e50f1b.nwd +++ b/tests/lipsum/content/f8c0562e50f1b.nwd @@ -1,4 +1,4 @@ -%%~ f8c0562e50f1b:6bd935d2490cd:b3643d0f92e32:Scene Four +%%~ f8c0562e50f1b:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Four ### Scene Four @pov: Bod diff --git a/tests/lipsum/content/f96ec11c6a3da.nwd b/tests/lipsum/content/f96ec11c6a3da.nwd index 8390c7bd..b95163d5 100644 --- a/tests/lipsum/content/f96ec11c6a3da.nwd +++ b/tests/lipsum/content/f96ec11c6a3da.nwd @@ -1,4 +1,4 @@ -%%~ f96ec11c6a3da:45e6b01ca35c1:b3643d0f92e32:Scene Two +%%~ f96ec11c6a3da:45e6b01ca35c1:b3643d0f92e32:NOVEL:SCENE:Scene Two ### Scene Two @pov: Bod diff --git a/tests/lipsum/content/fb609cd8319dc.nwd b/tests/lipsum/content/fb609cd8319dc.nwd index d28df127..fa9626f2 100644 --- a/tests/lipsum/content/fb609cd8319dc.nwd +++ b/tests/lipsum/content/fb609cd8319dc.nwd @@ -1,4 +1,4 @@ -%%~ fb609cd8319dc:45e6b01ca35c1:b3643d0f92e32:Chapter One +%%~ fb609cd8319dc:45e6b01ca35c1:b3643d0f92e32:NOVEL:CHAPTER:Chapter One ## Chapter One @pov: Bod diff --git a/tests/lipsum/nwProject.nwx b/tests/lipsum/nwProject.nwx index ef3a9bbe..ebfbde69 100644 --- a/tests/lipsum/nwProject.nwx +++ b/tests/lipsum/nwProject.nwx @@ -1,17 +1,20 @@ - + Lorem Ipsum Lorem Ipsum lipsum.com - False + 7 + 21 + 1459 + False False True - 88d59a277361b + 04468803b92e1 None - 3397 + 3847 Replace Text 1 Replace Text 2 @@ -22,9 +25,6 @@ %title% * * *
- False - False - False New @@ -81,7 +81,7 @@ 584 92 1 - 35 + 79
Act One @@ -238,9 +238,9 @@ Main True NOTE - 9 - 2 - 0 + 1369 + 195 + 2 1387 @@ -257,9 +257,9 @@ Minor True NOTE - 14 - 2 - 0 + 1770 + 259 + 3 1792 diff --git a/tests/reference/build/1_LoremIpsum.nwd b/tests/reference/build/1_LoremIpsum.nwd index 010f3a80..fbe4f132 100644 --- a/tests/reference/build/1_LoremIpsum.nwd +++ b/tests/reference/build/1_LoremIpsum.nwd @@ -14,7 +14,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t ## Prologue -_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. +*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. # Act One diff --git a/tests/reference/build/2_LoremIpsum.nwd b/tests/reference/build/2_LoremIpsum.nwd index d58f1021..f2a26740 100644 --- a/tests/reference/build/2_LoremIpsum.nwd +++ b/tests/reference/build/2_LoremIpsum.nwd @@ -16,7 +16,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t % Synopsis:Explanation from the lipsum.com website. -_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. +*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. # Act One