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