Merge branch 'main' into dev
This commit is contained in:
@@ -9,7 +9,7 @@ jobs:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
|
||||
- name: Install Packages (apt)
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
|
||||
- name: Install Packages (pip)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
version: 2
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
formats:
|
||||
- htmlzip
|
||||
- pdf
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/source/requirements.txt
|
||||
@@ -1,5 +1,62 @@
|
||||
# novelWriter Changelog
|
||||
|
||||
## Version 2.1.1 [2023-11-05]
|
||||
|
||||
### Release Notes
|
||||
|
||||
This is a patch release that fixes a layout issue and internationalisation issues with the new
|
||||
Manuscript Build tool. It also fixes a number of issues related to bugs in the underlying Qt
|
||||
framework that affects drag and drop functionality in the project tree. These issues were mostly
|
||||
only affecting Debian Linux package releases.
|
||||
|
||||
Other, minor issues related to updating the editor on colour theme change and project word list
|
||||
changes have been fixed as well. See the full changelog for more details.
|
||||
|
||||
### Detailed Changelog
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
* Fix an issue with width of the last two columns on Selection page of the Build Settings dialog on
|
||||
Windows. They were far too wide by default. Issue #1551. PR #1553.
|
||||
* Fix an issue where a lot of string were not translated to the UI language in the new Manuscript
|
||||
Build tool. Issue #1563. PR #1565.
|
||||
* Fix an issue in the Document Viewer where it wouldn't scroll to a heading further down the page
|
||||
when following a reference pointing to it. Issue #1566. PR #1568.
|
||||
* Add back in checks for illegal drag and drop moves in the project tree. In 2.0, the logic here
|
||||
was changed to set certain restrictions on the elements of the project tree itself, but there are
|
||||
numerous bugs in the Qt framework related to drag and drop, so the checks are ignored on at least
|
||||
Qt 5.15.8. In particular, it is possible to drop items on the root level, and it's possible to
|
||||
move root items to other locations. Neither should be possible and will severely mess up the
|
||||
project if done. Issue #1569. PR #1570.
|
||||
* Add a custom autoscroll feature when dragging an item in the project tree to near the top or
|
||||
bottom. This is actually a default feature of the tree widget in the Qt library, but this too is
|
||||
broken in some versions of Qt 5.15.x. The default feature has been permanently disabled and
|
||||
replaced by a custom written feature that behaves similarly. Issue #1561. PR #1571.
|
||||
* Fix an issue where the editor document wasn't re-highlighted when the Syntax Theme for it was
|
||||
changed. Issue #1535. PR #1573.
|
||||
* Fix an issue where editing the Project Word List would not refresh the spell checking of the
|
||||
editor. Issue #1559. PR #1573.
|
||||
|
||||
**Usability**
|
||||
|
||||
* Changed how the default UI language is selected. It used to default to the system locale, but
|
||||
that is now changed to British English if the system local is not available in novelWriter. The
|
||||
only real effects of this is that the dropdown box in Preferences now selects British English if
|
||||
the system locale is not available rather than the first in the list (currently Deutch). The
|
||||
second effect is that the language on buttons and other Qt components will match the rest of the
|
||||
UI. Issue #1564. PR #1565.
|
||||
* There is a bug in Qt on Wayland desktops where menus don't open in the correct location.
|
||||
According to one Qt ticket, QTBUG-68636, this can be mitigated by ensuring all QMenu instances
|
||||
have a parent set. This does not fix all issues, but it should help. The menus without a parent
|
||||
set have now been updated. Issue #1536. PR #1572.
|
||||
|
||||
**Documentation**
|
||||
|
||||
* Fixed a number of spelling errors and typing mistakes in the documentation for 2.1. Contributed
|
||||
by @nisemono-neko. PR #1567.
|
||||
|
||||
----
|
||||
|
||||
## Version 2.1 [2023-10-17]
|
||||
|
||||
### Release Notes
|
||||
|
||||
+2
-12
@@ -27,7 +27,7 @@ with open(initFile) as inFile:
|
||||
else:
|
||||
release = "unknown"
|
||||
|
||||
version = release.split("-")[0]
|
||||
version = release.partition("-")[0]
|
||||
|
||||
# -- General Configuration ---------------------------------------------------
|
||||
|
||||
@@ -67,11 +67,6 @@ html_sidebars = {
|
||||
"**": ["navbar-logo", "sidebar-title", "sbt-sidebar-nav"],
|
||||
}
|
||||
|
||||
# -- Options for HTMLHelp Output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "novelWriterDoc"
|
||||
|
||||
# -- Options for LaTeX Output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
@@ -84,11 +79,6 @@ latex_elements = {
|
||||
"figure_align": "htbp",
|
||||
}
|
||||
latex_logo = "_static/novelwriter-pdf.png"
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [(
|
||||
master_doc, "manual.tex", "User Guide",
|
||||
author, "manual"
|
||||
master_doc, "manual.tex", "User Guide", author, "manual"
|
||||
)]
|
||||
|
||||
@@ -87,7 +87,7 @@ set as ``name`` inside the file.
|
||||
Gustom GUI and Icons Theme
|
||||
--------------------------
|
||||
|
||||
A GUI theme ``.conf`` file consists of the follwing settings:
|
||||
A GUI theme ``.conf`` file consists of the following settings:
|
||||
|
||||
.. code-block:: cfg
|
||||
|
||||
@@ -127,7 +127,7 @@ In the Main section you must at least define the ``name`` and ``icontheme`` sett
|
||||
``typicons_light`` or ``typicons_dark``, or to an icon theme in your custom icons directory. The
|
||||
setting must match the icon theme's folder name.
|
||||
|
||||
The Palette values correspond the the Qt enum values for QPalette::ColorRole, see the
|
||||
The Palette values correspond to the Qt enum values for QPalette::ColorRole, see the
|
||||
`Qt documentation <https://doc.qt.io/qt-5.15/qpalette.html#ColorRole-enum>`_ for more details. The
|
||||
colour values are RGB numbers on the format ``r, g, b`` where each is an integer from to 255.
|
||||
Omitted values are not loaded and will use default values.
|
||||
@@ -167,5 +167,5 @@ A syntax theme ``.conf`` file consists of the follwing settings:
|
||||
modifier = 0, 0, 0
|
||||
|
||||
In the Main section, you must define at least the ``name`` setting. The Syntax colour values are
|
||||
RGB numbers on the format ``r, g, b`` where each is an integer from to 255. Omitted values default
|
||||
RGB numbers of the format ``r, g, b`` where each is an integer from to 255. Omitted values default
|
||||
to black, except ``background`` which defaults to white,
|
||||
|
||||
@@ -32,7 +32,7 @@ Glossary
|
||||
for more details.
|
||||
|
||||
Reference
|
||||
A references is one of a set of :term:`keywords<keyword>` that can be used to link to a
|
||||
A reference is one of a set of :term:`keywords<keyword>` that can be used to link to a
|
||||
:term:`tag` in another document. The reference keywords are specific to the different
|
||||
:term:`root folder` types. A full overview is available in the :ref:`a_references` chapter.
|
||||
|
||||
@@ -55,7 +55,7 @@ Glossary
|
||||
|
||||
Keyword
|
||||
A keyword in novelWriter is a special command you put in the text of your documents. They are
|
||||
not standard Markdown, but is used in novelWriter to add information that is interpreted by
|
||||
not standard Markdown, but are used in novelWriter to add information that is interpreted by
|
||||
the application. For instance, keywords are used for :term:`tags<tag>` and
|
||||
:term:`references<reference>`.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Key Features
|
||||
At its core, novelWriter is a multi-document plain text editor. It uses a markup syntax inspired by
|
||||
Markdown_ to apply simple formatting to the text. It is designed for writing fiction, so the
|
||||
formatting features available are limited to those relevant for this purpose. It is *not* suitable
|
||||
for technical writing, and it is *not* a full-feature Markdown editor.
|
||||
for technical writing, and it is *not* a full-featured Markdown editor.
|
||||
|
||||
Your novel project is organised as a collection of separate plain text documents instead of a
|
||||
single, large document. The idea here is to make it easier to reorganise your project structure
|
||||
@@ -32,11 +32,11 @@ Below are some key features of novelWriter.
|
||||
formatting text. Formatting is therefore limited to a small set of formatting tags for simple
|
||||
things like text emphasis and paragraph alignment. When you really want to focus on just
|
||||
writing, you can switch the editor into :guilabel:`Focus Mode` where only the text editor panel
|
||||
itself is vissible, and the project structure view is hidden away.
|
||||
itself is visible, and the project structure view is hidden away.
|
||||
|
||||
**Keep an eye on your notes**
|
||||
The main window can optionally show a document viewer to the right of the editor. This view
|
||||
panel is intended for displaying another scene document, you character notes, plot notes, or any
|
||||
panel is intended for displaying another scene document, your character notes, plot notes, or any
|
||||
other document you may need to reference while writing. It is not intended as a preview panel
|
||||
for the document you're editing, but if you wish, you can also use it for this purpose.
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ novelWriter is built as a cross-platform application using `Python 3 <https://ww
|
||||
the programming language, and `Qt 5 <https://www.qt.io/>`_ for the user interface.
|
||||
|
||||
novelWriter is built for Linux first, and this is where it works best. However, it also runs fine
|
||||
on Windows and MacOS due to the cross-platform framework it's build on. The author of the
|
||||
application doesn't own a Mac, so on-going Mac support is dependant on user feedback and user
|
||||
on Windows and MacOS due to the cross-platform framework it's built on. The author of the
|
||||
application doesn't own a Mac, so on-going Mac support is dependent on user feedback and user
|
||||
contributions.
|
||||
|
||||
Spell checking in novelWriter is provided by a third party library called
|
||||
@@ -36,7 +36,7 @@ read on.
|
||||
This chapter explains the basics of how the application works and what it can and cannot do.
|
||||
|
||||
:ref:`a_ui_project` – Recommended Reading
|
||||
This chapter will give you a more detailed explanation of how you the user interface components
|
||||
This chapter will give you a more detailed explanation of how you can use the user interface components
|
||||
to organise and view your project work.
|
||||
|
||||
:ref:`a_ui_writing` – Recommended Reading
|
||||
@@ -68,7 +68,7 @@ read on.
|
||||
Organising Your Projects
|
||||
========================
|
||||
|
||||
In addition to manage a collection of plain text files, novelWriter can interpret and map the
|
||||
In addition to managing a collection of plain text files, novelWriter can interpret and map the
|
||||
structure of your novel and show you additional information about its flow and content. In order
|
||||
to take advantage of these features, you must structure your text in a specific way and add some
|
||||
meta data for it to extract.
|
||||
|
||||
@@ -105,7 +105,7 @@ much all collected meta data is available here in different columns.
|
||||
|
||||
You can select which novel root folder to display from the dropdown box, and you can select which
|
||||
columns to show or hide from the menu button. You can also rearrange the columns by drag and drop.
|
||||
The app will remember you column order and size between sessions, and for each individual project.
|
||||
The app will remember your column order and size between sessions, and for each individual project.
|
||||
|
||||
|
||||
Colour Themes
|
||||
@@ -220,7 +220,7 @@ makes it easier to track them with version control software.
|
||||
Drive is mapped on Linux Gnome desktops using gvfs/gio.
|
||||
|
||||
.. caution::
|
||||
You should not add additional files to the project folder yourself. Nor should you,a s a rule,
|
||||
You should not add additional files to the project folder yourself. Nor should you, as a rule,
|
||||
manually edit files within it. If you really must manually edit the text files, e.g. with some
|
||||
automated task you want to perform, you need to rebuild the :term:`Project Index` when you open
|
||||
the project again.
|
||||
|
||||
@@ -34,7 +34,7 @@ have a distinct colour, and the references themselves will get a colour if they
|
||||
references will get a squiggly error line underneath. The same applies to duplicate tags.
|
||||
|
||||
There are a number of syntax highlighter colour themes available, both for light and dark GUIs. You
|
||||
can select them for :guilabel:`Preferences`.
|
||||
can select them from :guilabel:`Preferences`.
|
||||
|
||||
|
||||
.. _a_fmt_head:
|
||||
@@ -72,7 +72,7 @@ For headers level one and two, adding a ``!`` modifies the behaviour of the head
|
||||
``#! Title Text``
|
||||
This tells the build tool that the level one heading is intended to be used for the novel's
|
||||
main title, like for instance on the front page. When building the manuscript, this will use a
|
||||
different styling and will exclude the title from for instance a Table of Contents in Libre
|
||||
different styling and will exclude the title from, for instance, a Table of Contents in Libre
|
||||
Office.
|
||||
|
||||
``##! Title Text``
|
||||
@@ -170,7 +170,7 @@ in a special manner and will show up in the :ref:`a_ui_outline` in a dedicated c
|
||||
indicate this by altering the colour of the word.
|
||||
|
||||
``% text...``
|
||||
This is a comment. The text is not renderred by default (this can be overridden), seen in the
|
||||
This is a comment. The text is not rendered by default (this can be overridden), seen in the
|
||||
document viewer, or counted towards word counts.
|
||||
|
||||
``% Synopsis: text...``
|
||||
@@ -196,10 +196,10 @@ Tags use the command ``@tag:`` to define a tag. The tag can be set once per sect
|
||||
heading. Setting it multiple times under the same heading will just override the previous setting.
|
||||
|
||||
``@tag: value``
|
||||
A tag commad followed by the tag value, like for instance the name of a character.
|
||||
A tag command followed by the tag value, like for instance the name of a character.
|
||||
|
||||
References can be set anywhere within a section, and are collected according to their category.
|
||||
References are on the form:
|
||||
References are in the form:
|
||||
|
||||
``@keyword: value``
|
||||
A reference keyword followed by a value, or a comma separated list of values.
|
||||
@@ -236,7 +236,7 @@ Examples:
|
||||
"``>> Centred text <<``", "The text paragraph is centred."
|
||||
"``> Left indented text``", "The text has an increased left margin."
|
||||
"``Right indented text <``", "The text has an increased right margin."
|
||||
"``> Left/right indented text <``", "The text has an both margins increased."
|
||||
"``> Left/right indented text <``", "The text has both margins increased."
|
||||
|
||||
.. note::
|
||||
The text editor will not show the alignment and indentation live. But the viewer will show them
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Keyboard Shortcuts
|
||||
******************
|
||||
|
||||
Most features in novelWriter are available as keyboard shortcuts. This is a reference list if these
|
||||
Most features in novelWriter are available as keyboard shortcuts. This is a reference list of those
|
||||
shortcuts. Most of them are also listed in the application's user interface.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -48,6 +48,7 @@ instance `en_GB` for British English.
|
||||
You will need the translation tool Qt 5 Linguist on your system.
|
||||
|
||||
For Ubuntu/Debian, run:
|
||||
|
||||
```bash
|
||||
sudo apt install qttools5-dev-tools
|
||||
```
|
||||
@@ -75,6 +76,7 @@ The `i18n` folder at the root of the repository contains the `nw_XX.ts` translat
|
||||
|
||||
Whether to add a new language to the translation framework, or to update the file against the
|
||||
current source code, you must first run the `qtlupdate` command:
|
||||
|
||||
```bash
|
||||
python3 pkgutils.py qtlupdate i18n/nw_XX.ts
|
||||
```
|
||||
@@ -86,6 +88,10 @@ be a valid ISO language code, otherwise novelWriter will not accept the file.
|
||||
For instance, the French translation uses the language code `fr_FR`, so its translation file will
|
||||
be `nw_fr_FR.ts`
|
||||
|
||||
Note: The `qtlupdate` command needs the `lupdate` tool provided by PyQt6, which uses the latest
|
||||
TS file format. The tool in PyQt5 generates an older file format. On Debian/Ubuntu it is provided
|
||||
by the package `pyqt6-dev-tools`.
|
||||
|
||||
|
||||
### Edit the Translation File in Qt Linguist
|
||||
|
||||
@@ -100,6 +106,7 @@ Please select "English" and "United Kingdom" as the _source_ language if prompte
|
||||
The application does not use `.ts` files directly. The `novelwriter/assets/i18n/nw_XX.qm` files are
|
||||
the actual files used to translate the GUI into another language other than the default British
|
||||
English. These files are not generated by default, but they can be built with:
|
||||
|
||||
```bash
|
||||
python3 pkgutils.py qtlrelease
|
||||
```
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<h2>Release Notes for 2.1</h2>
|
||||
<p><i>Released on 17 October 2023</i></p>
|
||||
|
||||
<p>Scroll down for <a href="#patch">Patch Notes</a></p>
|
||||
|
||||
<p>The primary focus of this release has been a complete redesign of the Build Tool, that is, the
|
||||
tool that assembles your project into a manuscript document. The new tool, called the "Manuscript
|
||||
Build Tool" allows you to define multiple build definitions for your project. The build definitions
|
||||
@@ -30,5 +32,16 @@ a full list of changes, see the detailed changelogs.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<a name="patch"></a><h2>Patch Notes</h2>
|
||||
|
||||
<h3>Patch 2.2.1 – 5 November 2023</h3>
|
||||
|
||||
<p>This is a patch release that fixes a layout issue and internationalisation issues with the new
|
||||
Manuscript Build tool. It also fixes a number of issues related to bugs in the underlying Qt
|
||||
framework that affects drag and drop functionality in the project tree. These issues were mostly
|
||||
only affecting Debian Linux package releases.</p>
|
||||
<p>Other, minor issues related to updating the editor on colour theme change and project word list
|
||||
changes have been fixed as well. See the full changelog for more details.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -81,10 +81,12 @@ class Config:
|
||||
|
||||
# Localisation
|
||||
# Note that these paths must be strings
|
||||
self._qLocale = QLocale.system()
|
||||
self._qtTrans = {}
|
||||
self._nwLangPath = self._appPath / "assets" / "i18n"
|
||||
self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
|
||||
self._nwLangPath = str(self._appPath / "assets" / "i18n")
|
||||
|
||||
wantedLocale = self._nwLangPath / f"nw_{QLocale.system().name()}.qm"
|
||||
self._qLocale = QLocale.system() if wantedLocale.exists() else QLocale("en_GB")
|
||||
self._qtTrans = {}
|
||||
|
||||
# PDF Manual
|
||||
pdfDocs = self._appPath / "assets" / "manual.pdf"
|
||||
@@ -425,7 +427,7 @@ class Config:
|
||||
else:
|
||||
return []
|
||||
|
||||
for qmFile in Path(self._nwLangPath).iterdir():
|
||||
for qmFile in self._nwLangPath.iterdir():
|
||||
qmName = qmFile.name
|
||||
if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
|
||||
continue
|
||||
@@ -493,8 +495,8 @@ class Config:
|
||||
self._qtTrans = {}
|
||||
|
||||
langList = [
|
||||
(self._qtLangPath, "qtbase"), # Qt 5.x
|
||||
(self._nwLangPath, "nw"), # novelWriter
|
||||
(self._qtLangPath, "qtbase"), # Qt 5.x
|
||||
(str(self._nwLangPath), "nw"), # novelWriter
|
||||
]
|
||||
for lngPath, lngBase in langList:
|
||||
for lngCode in self._qLocale.uiLanguages():
|
||||
|
||||
@@ -32,7 +32,7 @@ from enum import Enum
|
||||
from typing import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import QT_TRANSLATE_NOOP
|
||||
from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.enum import nwBuildFmt
|
||||
@@ -209,7 +209,7 @@ class BuildSettings:
|
||||
@staticmethod
|
||||
def getLabel(key: str) -> str:
|
||||
"""Extract the GUI label for a specific setting."""
|
||||
return SETTINGS_LABELS.get(key, "ERROR")
|
||||
return QCoreApplication.translate("Builds", SETTINGS_LABELS.get(key, "ERROR"))
|
||||
|
||||
def getStr(self, key: str) -> str:
|
||||
"""Type safe value access for strings."""
|
||||
|
||||
@@ -162,7 +162,7 @@ class ToHtml(Tokenizer):
|
||||
pStyle = None
|
||||
lines = []
|
||||
|
||||
for tType, tLine, tText, tFormat, tStyle in self._tokens:
|
||||
for tType, nHead, tText, tFormat, tStyle in self._tokens:
|
||||
|
||||
# Replace < and > with HTML entities
|
||||
if tFormat:
|
||||
@@ -223,7 +223,7 @@ class ToHtml(Tokenizer):
|
||||
hStyle = ""
|
||||
|
||||
if self._linkHeaders:
|
||||
aNm = f"<a name='T{tLine:06d}'></a>"
|
||||
aNm = f"<a name='T{nHead:04d}'></a>"
|
||||
else:
|
||||
aNm = ""
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
|
||||
The format of the token list is an entry with a five-tuple for
|
||||
each line in the file. The tuple is as follows:
|
||||
1: The type of the block, self.T_*
|
||||
2: The line in the file where this block occurred
|
||||
2: The header number under which the text is placed
|
||||
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_*
|
||||
@@ -396,16 +396,15 @@ class Tokenizer(ABC):
|
||||
|
||||
self._tokens = []
|
||||
tmpMarkdown = []
|
||||
nLine = 0
|
||||
nHead = 0
|
||||
breakNext = False
|
||||
for aLine in self._text.splitlines():
|
||||
nLine += 1
|
||||
sLine = aLine.strip()
|
||||
|
||||
# Check for blank lines
|
||||
if len(sLine) == 0:
|
||||
self._tokens.append((
|
||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
||||
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
@@ -430,7 +429,7 @@ class Tokenizer(ABC):
|
||||
|
||||
elif sLine == "[VSPACE]":
|
||||
self._tokens.append(
|
||||
(self.T_SKIP, nLine, "", None, sAlign)
|
||||
(self.T_SKIP, nHead, "", None, sAlign)
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -438,11 +437,11 @@ class Tokenizer(ABC):
|
||||
nSkip = checkInt(sLine[8:-1], 0)
|
||||
if nSkip >= 1:
|
||||
self._tokens.append(
|
||||
(self.T_SKIP, nLine, "", None, sAlign)
|
||||
(self.T_SKIP, nHead, "", None, sAlign)
|
||||
)
|
||||
if nSkip > 1:
|
||||
self._tokens += (nSkip - 1) * [
|
||||
(self.T_SKIP, nLine, "", None, self.A_NONE)
|
||||
(self.T_SKIP, nHead, "", None, self.A_NONE)
|
||||
]
|
||||
continue
|
||||
|
||||
@@ -451,20 +450,20 @@ class Tokenizer(ABC):
|
||||
synTag = cLine[:9].lower()
|
||||
if synTag == "synopsis:":
|
||||
self._tokens.append((
|
||||
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign
|
||||
self.T_SYNOPSIS, nHead, cLine[9:].strip(), None, sAlign
|
||||
))
|
||||
if self._doSynopsis and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
else:
|
||||
self._tokens.append((
|
||||
self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign
|
||||
self.T_COMMENT, nHead, aLine[1:].strip(), None, sAlign
|
||||
))
|
||||
if self._doComments and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[0] == "@":
|
||||
self._tokens.append((
|
||||
self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign
|
||||
self.T_KEYWORD, nHead, aLine[1:].strip(), None, sAlign
|
||||
))
|
||||
if self._doKeywords and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -474,8 +473,9 @@ class Tokenizer(ABC):
|
||||
sAlign |= self.A_CENTRE
|
||||
sAlign |= self.A_PBB
|
||||
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign
|
||||
self.T_HEAD1, nHead, aLine[2:].strip(), None, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -484,39 +484,44 @@ class Tokenizer(ABC):
|
||||
if self._isNovel:
|
||||
sAlign |= self.A_PBB
|
||||
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign
|
||||
self.T_HEAD2, nHead, aLine[3:].strip(), None, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:4] == "### ":
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign
|
||||
self.T_HEAD3, nHead, aLine[4:].strip(), None, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:5] == "#### ":
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign
|
||||
self.T_HEAD4, nHead, aLine[5:].strip(), None, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:3] == "#! ":
|
||||
nHead += 1
|
||||
if self._isNovel:
|
||||
tStyle = self.T_TITLE
|
||||
else:
|
||||
tStyle = self.T_HEAD1
|
||||
|
||||
self._tokens.append((
|
||||
tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
||||
tStyle, nHead, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:4] == "##! ":
|
||||
nHead += 1
|
||||
if self._isNovel:
|
||||
tStyle = self.T_UNNUM
|
||||
sAlign |= self.A_PBB
|
||||
@@ -524,7 +529,7 @@ class Tokenizer(ABC):
|
||||
tStyle = self.T_HEAD2
|
||||
|
||||
self._tokens.append((
|
||||
tStyle, nLine, aLine[4:].strip(), None, sAlign
|
||||
tStyle, nHead, aLine[4:].strip(), None, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -581,7 +586,7 @@ class Tokenizer(ABC):
|
||||
# sorted by position
|
||||
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
||||
self._tokens.append((
|
||||
self.T_TEXT, nLine, aLine, fmtPos, sAlign
|
||||
self.T_TEXT, nHead, aLine, fmtPos, sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -600,7 +605,7 @@ class Tokenizer(ABC):
|
||||
|
||||
# Always add an empty line at the end of the file
|
||||
self._tokens.append((
|
||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
||||
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
|
||||
@@ -176,6 +176,8 @@ class GuiPreferencesGeneral(QWidget):
|
||||
for lang, langName in theLangs:
|
||||
self.guiLocale.addItem(langName, lang)
|
||||
langIdx = self.guiLocale.findData(CONFIG.guiLocale)
|
||||
if langIdx < 0:
|
||||
langIdx = self.guiLocale.findData("en_GB")
|
||||
if langIdx != -1:
|
||||
self.guiLocale.setCurrentIndex(langIdx)
|
||||
|
||||
|
||||
@@ -339,6 +339,8 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
self.clearEditor()
|
||||
else:
|
||||
self.redrawText()
|
||||
if not self._bigDoc:
|
||||
self.highLight.rehighlight()
|
||||
|
||||
return
|
||||
|
||||
@@ -980,7 +982,7 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
uCursor = self.textCursor()
|
||||
pCursor = self.cursorForPosition(pos)
|
||||
|
||||
ctxMenu = QMenu()
|
||||
ctxMenu = QMenu(self)
|
||||
|
||||
# Follow
|
||||
if self._followTag(cursor=pCursor, loadTag=False):
|
||||
|
||||
@@ -381,7 +381,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
userCursor = self.textCursor()
|
||||
userSelection = userCursor.hasSelection()
|
||||
|
||||
mnuContext = QMenu()
|
||||
mnuContext = QMenu(self)
|
||||
|
||||
# Cut, Copy and Paste
|
||||
# ===================
|
||||
|
||||
@@ -228,7 +228,7 @@ class GuiNovelToolBar(QWidget):
|
||||
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
||||
|
||||
# More Options Menu
|
||||
self.mMore = QMenu()
|
||||
self.mMore = QMenu(self)
|
||||
|
||||
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
|
||||
self.gLastCol = QActionGroup(self.mMore)
|
||||
|
||||
@@ -31,8 +31,8 @@ from enum import Enum
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QDropEvent, QMouseEvent, QPalette
|
||||
from PyQt5.QtCore import QPoint, Qt, QSize, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtGui import QDragMoveEvent, QDropEvent, QMouseEvent, QPalette
|
||||
from PyQt5.QtCore import QPoint, QTimer, Qt, QSize, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QDialog, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
||||
QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem,
|
||||
@@ -248,7 +248,7 @@ class GuiProjectToolBar(QWidget):
|
||||
self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
|
||||
# Quick Links
|
||||
self.mQuick = QMenu()
|
||||
self.mQuick = QMenu(self)
|
||||
|
||||
self.tbQuick = QToolButton(self)
|
||||
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
||||
@@ -269,7 +269,7 @@ class GuiProjectToolBar(QWidget):
|
||||
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
||||
|
||||
# Add Item Menu
|
||||
self.mAdd = QMenu()
|
||||
self.mAdd = QMenu(self)
|
||||
|
||||
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
||||
self.aAddEmpty.triggered.connect(
|
||||
@@ -307,7 +307,7 @@ class GuiProjectToolBar(QWidget):
|
||||
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
|
||||
|
||||
# More Options Menu
|
||||
self.mMore = QMenu()
|
||||
self.mMore = QMenu(self)
|
||||
|
||||
self.aExpand = self.mMore.addAction(self.tr("Expand All"))
|
||||
self.aExpand.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, True))
|
||||
@@ -507,8 +507,15 @@ class GuiProjectTree(QTreeWidget):
|
||||
# Allow Move by Drag & Drop
|
||||
self.setDragEnabled(True)
|
||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
self.setDropIndicatorShown(True)
|
||||
|
||||
# Disable built-in autoscroll as it isn't working in some Qt
|
||||
# releases (see #1561) and instead use our own implementation
|
||||
self.setAutoScroll(False)
|
||||
|
||||
# But don't allow drop on root level
|
||||
# Due to a bug, this stops working somewhere between Qt 5.15.3
|
||||
# and 5.15.8, so this is also blocked in dropEvent (see #1569)
|
||||
trRoot = self.invisibleRootItem()
|
||||
trRoot.setFlags(trRoot.flags() ^ Qt.ItemIsDropEnabled)
|
||||
|
||||
@@ -524,6 +531,13 @@ class GuiProjectTree(QTreeWidget):
|
||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.itemSelectionChanged.connect(self._treeSelectionChange)
|
||||
|
||||
# Autoscroll
|
||||
self._scrollMargin = SHARED.theme.baseIconSize
|
||||
self._scrollDirection = 0
|
||||
self._scrollTimer = QTimer()
|
||||
self._scrollTimer.timeout.connect(self._doAutoScroll)
|
||||
self._scrollTimer.setInterval(250)
|
||||
|
||||
# Set custom settings
|
||||
self.initSettings()
|
||||
|
||||
@@ -1196,7 +1210,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
logger.debug("No item found")
|
||||
return False
|
||||
|
||||
ctxMenu = QMenu()
|
||||
ctxMenu = QMenu(self)
|
||||
|
||||
# Trash Folder
|
||||
# ============
|
||||
@@ -1343,6 +1357,17 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
return True
|
||||
|
||||
@pyqtSlot()
|
||||
def _doAutoScroll(self) -> None:
|
||||
"""Scroll one item up or down based on direction value."""
|
||||
if self._scrollDirection == -1:
|
||||
self.scrollToItem(self.itemAbove(self.itemAt(1, 1)))
|
||||
elif self._scrollDirection == 1:
|
||||
self.scrollToItem(self.itemBelow(self.itemAt(1, self.height() - 1)))
|
||||
self._scrollDirection = 0
|
||||
self._scrollTimer.stop()
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
@@ -1374,14 +1399,36 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
def dragMoveEvent(self, event: QDragMoveEvent) -> None:
|
||||
"""Capture the drag move event to enable edge autoscroll."""
|
||||
y = event.pos().y()
|
||||
if y < self._scrollMargin:
|
||||
if not self._scrollTimer.isActive():
|
||||
self._scrollDirection = -1
|
||||
self._scrollTimer.start()
|
||||
elif y > self.height() - self._scrollMargin:
|
||||
if not self._scrollTimer.isActive():
|
||||
self._scrollDirection = 1
|
||||
self._scrollTimer.start()
|
||||
super().dragMoveEvent(event)
|
||||
return
|
||||
|
||||
def dropEvent(self, event: QDropEvent) -> None:
|
||||
"""Overload the drop item event to ensure relevant data has been
|
||||
updated.
|
||||
"""
|
||||
sHandle = self.getSelectedHandle()
|
||||
sItem = self._getTreeItem(sHandle) if sHandle else None
|
||||
if sHandle is None or sItem is None:
|
||||
if sHandle is None or sItem is None or sItem.parent() is None:
|
||||
logger.error("Invalid drag and drop event")
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if not self.indexAt(event.pos()).isValid():
|
||||
# Needed due to a bug somewhere around Qt 5.15.8 that
|
||||
# ignores the invisible root item flags
|
||||
logger.error("Invalid drop location")
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
logger.debug("Drag'n'drop of item '%s' accepted", sHandle)
|
||||
|
||||
@@ -991,6 +991,7 @@ class GuiMain(QMainWindow):
|
||||
if dlgWords.result() == QDialog.Accepted:
|
||||
logger.debug("Reloading word list")
|
||||
SHARED.updateSpellCheckLanguage(reload=True)
|
||||
self.docEditor.spellCheckDocument()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -332,14 +332,16 @@ class _FilterTab(QWidget):
|
||||
|
||||
treeHeader = self.optTree.header()
|
||||
treeHeader.setStretchLastSection(False)
|
||||
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1551
|
||||
treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.Stretch)
|
||||
treeHeader.setSectionResizeMode(self.C_ACTIVE, QHeaderView.Fixed)
|
||||
treeHeader.setSectionResizeMode(self.C_STATUS, QHeaderView.Fixed)
|
||||
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
|
||||
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
|
||||
|
||||
self.optTree.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.optTree.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||
self.optTree.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.optTree.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
|
||||
# Filters
|
||||
# =======
|
||||
@@ -390,11 +392,11 @@ class _FilterTab(QWidget):
|
||||
self.mainSplit.addWidget(self.filterOpt)
|
||||
self.mainSplit.setCollapsible(0, False)
|
||||
self.mainSplit.setCollapsible(1, False)
|
||||
self.mainSplit.setStretchFactor(0, 0)
|
||||
self.mainSplit.setStretchFactor(1, 1)
|
||||
self.mainSplit.setStretchFactor(0, 1)
|
||||
self.mainSplit.setStretchFactor(1, 0)
|
||||
self.mainSplit.setSizes([
|
||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "treeWidth", 1)),
|
||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "filterWidth", 1))
|
||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "treeWidth", 300)),
|
||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "filterWidth", 300))
|
||||
])
|
||||
|
||||
self.outerBox = QHBoxLayout()
|
||||
@@ -718,7 +720,7 @@ class _HeadingsTab(QWidget):
|
||||
|
||||
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
|
||||
|
||||
self.menuInsert = QMenu()
|
||||
self.menuInsert = QMenu(self)
|
||||
self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
|
||||
self.aInsChNum = self.menuInsert.addAction(self.tr("Chapter Number"))
|
||||
self.aInsChWord = self.menuInsert.addAction(self.tr("Chapter Number (Word)"))
|
||||
|
||||
@@ -325,6 +325,7 @@ def buildQtI18nTS(sysArgs):
|
||||
from PyQt6.lupdate import lupdate
|
||||
except ImportError:
|
||||
print("ERROR: This command requires lupdate from PyQt6")
|
||||
print("On Debian/Ubuntu, install: pyqt6-dev-tools")
|
||||
sys.exit(1)
|
||||
|
||||
print("")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-09-07 19:14:50">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1531" autoCount="238" editTime="75619">
|
||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-11-05 18:54:42">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1532" autoCount="238" editTime="75633">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
|
||||
@@ -166,21 +166,21 @@ def testBaseConfig_Localisation(fncPath, tstPaths):
|
||||
|
||||
i18nDir = fncPath / "i18n"
|
||||
i18nDir.mkdir()
|
||||
tstConf._nwLangPath = str(i18nDir)
|
||||
tstConf._nwLangPath = i18nDir
|
||||
|
||||
copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_en_GB.qm")
|
||||
writeFile(i18nDir / "nw_en_GB.ts", "")
|
||||
writeFile(i18nDir / "nw_abcd.qm", "")
|
||||
|
||||
tstApp = MockApp()
|
||||
tstConf.initLocalisation(tstApp)
|
||||
tstConf.initLocalisation(tstApp) # type: ignore
|
||||
|
||||
# Check Lists
|
||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||
assert theList == [("en_GB", "British English")]
|
||||
theList = tstConf.listLanguages(tstConf.LANG_PROJ)
|
||||
assert theList == [("en_GB", "British English")]
|
||||
theList = tstConf.listLanguages(None)
|
||||
theList = tstConf.listLanguages(None) # type: ignore
|
||||
assert theList == []
|
||||
|
||||
# Add Language
|
||||
|
||||
+261
-267
@@ -29,177 +29,176 @@ from novelwriter.core.project import NWProject
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_ConvertFormat(mockGUI):
|
||||
"""Test the tokenizer and converter chain using the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
"""Test the tokenizer and converter chain using the ToHtml class."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
|
||||
# Novel Files Headers
|
||||
# ===================
|
||||
|
||||
theHtml._isNovel = True
|
||||
theHtml._isNote = False
|
||||
theHtml._isFirst = True
|
||||
html._isNovel = True
|
||||
html._isNote = False
|
||||
html._isFirst = True
|
||||
|
||||
# Header 1
|
||||
theHtml._text = "# Partition\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "# Partition\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: center;'>Partition</h1>\n"
|
||||
)
|
||||
|
||||
# Header 2
|
||||
theHtml._text = "## Chapter Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "## Chapter Title\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 style='page-break-before: always;'>Chapter Title</h1>\n"
|
||||
)
|
||||
|
||||
# Header 3
|
||||
theHtml._text = "### Scene Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h2>Scene Title</h2>\n"
|
||||
html._text = "### Scene Title\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h2>Scene Title</h2>\n"
|
||||
|
||||
# Header 4
|
||||
theHtml._text = "#### Section Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h3>Section Title</h3>\n"
|
||||
html._text = "#### Section Title\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h3>Section Title</h3>\n"
|
||||
|
||||
# Title
|
||||
theHtml._text = "#! Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "#! Title\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: center;'>Title</h1>\n"
|
||||
)
|
||||
|
||||
# Unnumbered
|
||||
theHtml._text = "##! Prologue\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
|
||||
html._text = "##! Prologue\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
|
||||
|
||||
# Note Files Headers
|
||||
# ==================
|
||||
|
||||
theHtml._isNovel = False
|
||||
theHtml._isNote = True
|
||||
theHtml._isFirst = True
|
||||
theHtml.setLinkHeaders(True)
|
||||
html._isNovel = False
|
||||
html._isNote = True
|
||||
html._isFirst = True
|
||||
html.setLinkHeaders(True)
|
||||
|
||||
# Header 1
|
||||
theHtml._text = "# Heading One\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h1><a name='T000001'></a>Heading One</h1>\n"
|
||||
html._text = "# Heading One\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h1><a name='T0001'></a>Heading One</h1>\n"
|
||||
|
||||
# Header 2
|
||||
theHtml._text = "## Heading Two\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
|
||||
html._text = "## Heading Two\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h2><a name='T0001'></a>Heading Two</h2>\n"
|
||||
|
||||
# Header 3
|
||||
theHtml._text = "### Heading Three\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h3><a name='T000001'></a>Heading Three</h3>\n"
|
||||
html._text = "### Heading Three\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h3><a name='T0001'></a>Heading Three</h3>\n"
|
||||
|
||||
# Header 4
|
||||
theHtml._text = "#### Heading Four\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n"
|
||||
html._text = "#### Heading Four\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h4><a name='T0001'></a>Heading Four</h4>\n"
|
||||
|
||||
# Title
|
||||
theHtml._text = "#! Heading One\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 style='text-align: center;'><a name='T000001'></a>Heading One</h1>\n"
|
||||
html._text = "#! Heading One\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 style='text-align: center;'><a name='T0001'></a>Heading One</h1>\n"
|
||||
)
|
||||
|
||||
# Unnumbered
|
||||
theHtml._text = "##! Heading Two\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
|
||||
html._text = "##! Heading Two\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == "<h2><a name='T0001'></a>Heading Two</h2>\n"
|
||||
|
||||
# Paragraphs
|
||||
# ==========
|
||||
|
||||
# Text
|
||||
theHtml._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Some <strong>nested bold and <em>italic</em> and "
|
||||
"<del>strikethrough</del> text</strong> here</p>\n"
|
||||
)
|
||||
|
||||
# Text w/Hard Break
|
||||
theHtml._text = "Line one \nLine two \nLine three\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Line one \nLine two \nLine three\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p class='break'>Line one<br/>Line two<br/>Line three</p>\n"
|
||||
)
|
||||
|
||||
# Synopsis
|
||||
theHtml._text = "%synopsis: The synopsis ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
html._text = "%synopsis: The synopsis ...\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == ""
|
||||
|
||||
theHtml.setSynopsis(True)
|
||||
theHtml._text = "%synopsis: The synopsis ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.setSynopsis(True)
|
||||
html._text = "%synopsis: The synopsis ...\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p class='synopsis'><strong>Synopsis:</strong> The synopsis ...</p>\n"
|
||||
)
|
||||
|
||||
# Comment
|
||||
theHtml._text = "% A comment ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
html._text = "% A comment ...\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == ""
|
||||
|
||||
theHtml.setComments(True)
|
||||
theHtml._text = "% A comment ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.setComments(True)
|
||||
html._text = "% A comment ...\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p class='comment'><strong>Comment:</strong> A comment ...</p>\n"
|
||||
)
|
||||
|
||||
# Keywords
|
||||
theHtml._text = "@char: Bod, Jane\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
html._text = "@char: Bod, Jane\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == ""
|
||||
|
||||
theHtml.setKeywords(True)
|
||||
theHtml._text = "@char: Bod, Jane\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.setKeywords(True)
|
||||
html._text = "@char: Bod, Jane\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p><span class='tags'>Characters:</span> "
|
||||
"<a href='#tag_Bod'>Bod</a>, <a href='#tag_Jane'>Jane</a></p>\n"
|
||||
)
|
||||
|
||||
# Multiple Keywords
|
||||
theHtml.setKeywords(True)
|
||||
theHtml._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.setKeywords(True)
|
||||
html._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h2>"
|
||||
"<a name='T000001'></a>Chapter</h2>\n"
|
||||
"<a name='T0001'></a>Chapter</h2>\n"
|
||||
"<p style='margin-bottom: 0;'>"
|
||||
"<span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a>"
|
||||
"</p>\n"
|
||||
@@ -214,13 +213,13 @@ def testCoreToHtml_ConvertFormat(mockGUI):
|
||||
# Preview Mode
|
||||
# ============
|
||||
|
||||
theHtml.setPreview(True, True)
|
||||
html.setPreview(True, True)
|
||||
|
||||
# Text (HTML4)
|
||||
theHtml._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Some <b>nested bold and <i>italic</i> and "
|
||||
"<span style='text-decoration: line-through;'>strikethrough</span> "
|
||||
"text</b> here</p>\n"
|
||||
@@ -231,109 +230,108 @@ def testCoreToHtml_ConvertFormat(mockGUI):
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_ConvertDirect(mockGUI):
|
||||
"""Test the converter directly using the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
"""Test the converter directly using the ToHtml class."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
|
||||
theHtml._isNovel = True
|
||||
theHtml._isNote = False
|
||||
theHtml.setLinkHeaders(True)
|
||||
html._isNovel = True
|
||||
html._isNote = False
|
||||
html.setLinkHeaders(True)
|
||||
|
||||
# Special Titles
|
||||
# ==============
|
||||
|
||||
# Title
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_TITLE, 1, "A Title", None, html.A_PBB | html.A_CENTRE),
|
||||
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
|
||||
"<a name='T000001'></a>A Title</h1>\n"
|
||||
"<a name='T0001'></a>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Unnumbered
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_UNNUM, 1, "Prologue", None, html.A_PBB),
|
||||
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 style='page-break-before: always;'>"
|
||||
"<a name='T000001'></a>Prologue</h1>\n"
|
||||
"<a name='T0001'></a>Prologue</h1>\n"
|
||||
)
|
||||
|
||||
# Separators
|
||||
# ==========
|
||||
|
||||
# Separator
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_SEP, 1, "* * *", None, html.A_CENTRE),
|
||||
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
||||
html.doConvert()
|
||||
assert html.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
||||
|
||||
# Skip
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_SKIP, 1, "", None, theHtml.A_NONE),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_SKIP, 1, "", None, html.A_NONE),
|
||||
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<p class='skip'> </p>\n"
|
||||
html.doConvert()
|
||||
assert html.theResult == "<p class='skip'> </p>\n"
|
||||
|
||||
# Alignment
|
||||
# =========
|
||||
|
||||
theHtml.setLinkHeaders(False)
|
||||
html.setLinkHeaders(False)
|
||||
|
||||
# Align Left
|
||||
theHtml.setStyles(False)
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
||||
html.setStyles(False)
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_LEFT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
theHtml.setStyles(True)
|
||||
html.setStyles(True)
|
||||
|
||||
# Align Left
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_LEFT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: left;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Right
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT),
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_RIGHT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: right;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Centre
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE),
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_CENTRE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: center;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Justify
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY),
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_JUSTIFY),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
@@ -341,11 +339,11 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
||||
# ==========
|
||||
|
||||
# Page Break Always
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA),
|
||||
html._tokens = [
|
||||
(html.T_HEAD1, 1, "A Title", None, html.A_PBB | html.A_PBA),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 class='title' "
|
||||
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
|
||||
)
|
||||
@@ -354,22 +352,22 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
||||
# ======
|
||||
|
||||
# Indent Left
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L),
|
||||
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_L),
|
||||
(html.T_EMPTY, 2, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p style='margin-left: 40px;'>Some text ...</p>\n"
|
||||
)
|
||||
|
||||
# Indent Right
|
||||
theHtml._tokens = [
|
||||
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R),
|
||||
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
|
||||
html._tokens = [
|
||||
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_R),
|
||||
(html.T_EMPTY, 2, "", None, html.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p style='margin-right: 40px;'>Some text ...</p>\n"
|
||||
)
|
||||
|
||||
@@ -378,40 +376,39 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_SpecialCases(mockGUI):
|
||||
"""Test some special cases that have caused errors in the past.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
theHtml._isNovel = True
|
||||
"""Test some special cases that have caused errors in the past."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
html._isNovel = True
|
||||
|
||||
# Greater/Lesser than symbols
|
||||
# ===========================
|
||||
|
||||
theHtml._text = "Text with > and < with some **bold text** in it.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Text with > and < with some **bold text** in it.\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Text with > and < with some <strong>bold text</strong> in it.</p>\n"
|
||||
)
|
||||
|
||||
theHtml._text = "Text with some <**bold text**> in it.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Text with some <**bold text**> in it.\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Text with some <<strong>bold text</strong>> in it.</p>\n"
|
||||
)
|
||||
|
||||
theHtml._text = "Let's > be > _difficult **shall** > we_?\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Let's > be > _difficult **shall** > we_?\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Let's > be > <em>difficult <strong>shall</strong> > we</em>?</p>\n"
|
||||
)
|
||||
|
||||
theHtml._text = "Test > text _<**bold**>_ and more.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Test > text _<**bold**>_ and more.\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Test > text <em><<strong>bold</strong>></em> and more.</p>\n"
|
||||
)
|
||||
|
||||
@@ -419,20 +416,20 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
||||
# ===================
|
||||
# See: https://github.com/vkbo/novelWriter/issues/950
|
||||
|
||||
theHtml.setComments(True)
|
||||
theHtml._text = "% Test > text _<**bold**>_ and more.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html.setComments(True)
|
||||
html._text = "% Test > text _<**bold**>_ and more.\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p class='comment'>"
|
||||
"<strong>Comment:</strong> Test > text _<**bold**>_ and more."
|
||||
"</p>\n"
|
||||
)
|
||||
|
||||
theHtml._text = "## Heading <1>\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "## Heading <1>\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<h1 style='page-break-before: always;'>Heading <1></h1>\n"
|
||||
)
|
||||
|
||||
@@ -440,10 +437,10 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
||||
# ====================
|
||||
# See: https://github.com/vkbo/novelWriter/issues/1412
|
||||
|
||||
theHtml._text = "Test text \\**_bold_** and more.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = "Test text \\**_bold_** and more.\n"
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Test text **<em>bold</em>** and more.</p>\n"
|
||||
)
|
||||
|
||||
@@ -452,11 +449,10 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Complex(mockGUI, fncPath):
|
||||
"""Test the save method of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
theHtml._isNovel = True
|
||||
"""Test the save method of the ToHtml class."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
html._isNovel = True
|
||||
|
||||
# Build Project
|
||||
# =============
|
||||
@@ -502,21 +498,21 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
||||
]
|
||||
|
||||
for i in range(len(docText)):
|
||||
theHtml._text = docText[i]
|
||||
theHtml.doPreProcessing()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == resText[i]
|
||||
html._text = docText[i]
|
||||
html.doPreProcessing()
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == resText[i]
|
||||
|
||||
assert theHtml.fullHTML == resText
|
||||
assert html.fullHTML == resText
|
||||
|
||||
theHtml.replaceTabs(nSpaces=2, spaceChar=" ")
|
||||
html.replaceTabs(nSpaces=2, spaceChar=" ")
|
||||
resText[6] = "<h3>A Section</h3>\n<p> More text in scene two.</p>\n"
|
||||
|
||||
# Check File
|
||||
# ==========
|
||||
|
||||
theStyle = theHtml.getStyleSheet()
|
||||
theStyle = html.getStyleSheet()
|
||||
htmlDoc = (
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
@@ -539,7 +535,7 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
||||
)
|
||||
|
||||
saveFile = fncPath / "outFile.htm"
|
||||
theHtml.saveHtml5(saveFile)
|
||||
html.saveHtml5(saveFile)
|
||||
assert readFile(saveFile) == htmlDoc
|
||||
|
||||
# END Test testCoreToHtml_Complex
|
||||
@@ -547,86 +543,84 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Methods(mockGUI):
|
||||
"""Test all the other methods of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
theHtml.setKeepMarkdown(True)
|
||||
"""Test all the other methods of the ToHtml class."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
html.setKeepMarkdown(True)
|
||||
|
||||
# Auto-Replace, keep Unicode
|
||||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||||
theHtml._text = docText
|
||||
theHtml.setReplaceUnicode(False)
|
||||
theHtml.doPreProcessing()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = docText
|
||||
html.setReplaceUnicode(False)
|
||||
html.doPreProcessing()
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||||
)
|
||||
|
||||
# Auto-Replace, replace Unicode
|
||||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||||
theHtml._text = docText
|
||||
theHtml.setReplaceUnicode(True)
|
||||
theHtml.doPreProcessing()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
html._text = docText
|
||||
html.setReplaceUnicode(True)
|
||||
html.doPreProcessing()
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theResult == (
|
||||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||||
)
|
||||
|
||||
# With Preview
|
||||
theHtml.setPreview(True, True)
|
||||
theHtml._text = docText
|
||||
theHtml.doPreProcessing()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theMarkdown[-1] == (
|
||||
html.setPreview(True, True)
|
||||
html._text = docText
|
||||
html.doPreProcessing()
|
||||
html.tokenizeText()
|
||||
html.doConvert()
|
||||
assert html.theMarkdown[-1] == (
|
||||
"Text with <brackets> & short–dash, long—dash …\n\n"
|
||||
)
|
||||
|
||||
# Result Size
|
||||
assert theHtml.getFullResultSize() == 147
|
||||
assert html.getFullResultSize() == 147
|
||||
|
||||
# CSS
|
||||
# ===
|
||||
|
||||
assert len(theHtml.getStyleSheet()) > 1
|
||||
assert "p {text-align: left;" in " ".join(theHtml.getStyleSheet())
|
||||
assert "p {text-align: justify;" not in " ".join(theHtml.getStyleSheet())
|
||||
assert len(html.getStyleSheet()) > 1
|
||||
assert "p {text-align: left;" in " ".join(html.getStyleSheet())
|
||||
assert "p {text-align: justify;" not in " ".join(html.getStyleSheet())
|
||||
|
||||
theHtml.setJustify(True)
|
||||
assert "p {text-align: left;" not in " ".join(theHtml.getStyleSheet())
|
||||
assert "p {text-align: justify;" in " ".join(theHtml.getStyleSheet())
|
||||
html.setJustify(True)
|
||||
assert "p {text-align: left;" not in " ".join(html.getStyleSheet())
|
||||
assert "p {text-align: justify;" in " ".join(html.getStyleSheet())
|
||||
|
||||
theHtml.setStyles(False)
|
||||
assert theHtml.getStyleSheet() == []
|
||||
html.setStyles(False)
|
||||
assert html.getStyleSheet() == []
|
||||
|
||||
# END Test testCoreToHtml_Methods
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Format(mockGUI):
|
||||
"""Test all the formatters for the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject()
|
||||
theHtml = ToHtml(theProject)
|
||||
"""Test all the formatters for the ToHtml class."""
|
||||
project = NWProject()
|
||||
html = ToHtml(project)
|
||||
|
||||
# Export Mode
|
||||
# ===========
|
||||
|
||||
assert theHtml._formatSynopsis("synopsis text") == (
|
||||
assert html._formatSynopsis("synopsis text") == (
|
||||
"<p class='synopsis'><strong>Synopsis:</strong> synopsis text</p>\n"
|
||||
)
|
||||
assert theHtml._formatComments("comment text") == (
|
||||
assert html._formatComments("comment text") == (
|
||||
"<p class='comment'><strong>Comment:</strong> comment text</p>\n"
|
||||
)
|
||||
|
||||
assert theHtml._formatKeywords("") == ""
|
||||
assert theHtml._formatKeywords("tag: Jane") == (
|
||||
assert html._formatKeywords("") == ""
|
||||
assert html._formatKeywords("tag: Jane") == (
|
||||
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
||||
)
|
||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
||||
assert html._formatKeywords("char: Bod, Jane") == (
|
||||
"<span class='tags'>Characters:</span> "
|
||||
"<a href='#tag_Bod'>Bod</a>, "
|
||||
"<a href='#tag_Jane'>Jane</a>"
|
||||
@@ -635,20 +629,20 @@ def testCoreToHtml_Format(mockGUI):
|
||||
# Preview Mode
|
||||
# ============
|
||||
|
||||
theHtml.setPreview(True, True)
|
||||
html.setPreview(True, True)
|
||||
|
||||
assert theHtml._formatSynopsis("synopsis text") == (
|
||||
assert html._formatSynopsis("synopsis text") == (
|
||||
"<p class='comment'><span class='synopsis'>Synopsis:</span> synopsis text</p>\n"
|
||||
)
|
||||
assert theHtml._formatComments("comment text") == (
|
||||
assert html._formatComments("comment text") == (
|
||||
"<p class='comment'>comment text</p>\n"
|
||||
)
|
||||
|
||||
assert theHtml._formatKeywords("") == ""
|
||||
assert theHtml._formatKeywords("tag: Jane") == (
|
||||
assert html._formatKeywords("") == ""
|
||||
assert html._formatKeywords("tag: Jane") == (
|
||||
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
||||
)
|
||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
||||
assert html._formatKeywords("char: Bod, Jane") == (
|
||||
"<span class='tags'>Characters:</span> "
|
||||
"<a href='#char=Bod'>Bod</a>, "
|
||||
"<a href='#char=Jane'>Jane</a>"
|
||||
|
||||
@@ -434,8 +434,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
theToken._text = "% A comment\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_COMMENT, 1, "A comment", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_COMMENT, 0, "A comment", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "\n"
|
||||
|
||||
@@ -447,14 +447,14 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
theToken._text = "%synopsis: The synopsis\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
theToken._text = "% synopsis: The synopsis\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "\n"
|
||||
|
||||
@@ -466,8 +466,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
theToken._text = "@char: Bod\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_KEYWORD, 1, "char: Bod", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_KEYWORD, 0, "char: Bod", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "\n"
|
||||
|
||||
@@ -481,10 +481,10 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
styMid = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG | Tokenizer.A_Z_TOPMRG
|
||||
styBtm = Tokenizer.A_NONE | Tokenizer.A_Z_TOPMRG
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_KEYWORD, 1, "pov: Bod", None, styTop),
|
||||
(Tokenizer.T_KEYWORD, 2, "plot: Main", None, styMid),
|
||||
(Tokenizer.T_KEYWORD, 3, "location: Europe", None, styBtm),
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_KEYWORD, 0, "pov: Bod", None, styTop),
|
||||
(Tokenizer.T_KEYWORD, 0, "plot: Main", None, styMid),
|
||||
(Tokenizer.T_KEYWORD, 0, "location: Europe", None, styBtm),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||
|
||||
@@ -513,23 +513,23 @@ def testCoreToken_MarginFormat(mockGUI):
|
||||
)
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L),
|
||||
(Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R),
|
||||
(Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent),
|
||||
(Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign),
|
||||
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some regular text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some left-aligned text", [], Tokenizer.A_LEFT),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some right-aligned text", [], Tokenizer.A_RIGHT),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some centered text", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Left-indented block", [], Tokenizer.A_IND_L),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Right-indented block", [], Tokenizer.A_IND_R),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Double-indented block", [], dblIndent),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Right-indent, right-aligned", [], rIndAlign),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == (
|
||||
"Some regular text\n\n"
|
||||
@@ -556,20 +556,20 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken._text = "Some plain text\non two lines\n\n\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_TEXT, 1, "Some plain text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 2, "on two lines", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some plain text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "on two lines", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "Some plain text\non two lines\n\n\n\n"
|
||||
|
||||
theToken.setBodyText(False)
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "\n\n\n"
|
||||
theToken.setBodyText(True)
|
||||
@@ -579,7 +579,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
Tokenizer.T_TEXT, 0,
|
||||
"Some **bolded text** on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
@@ -587,7 +587,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "Some **bolded text** on this lines\n\n"
|
||||
|
||||
@@ -595,7 +595,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
Tokenizer.T_TEXT, 0,
|
||||
"Some _italic text_ on this lines",
|
||||
[
|
||||
[5, 1, Tokenizer.FMT_I_B],
|
||||
@@ -603,7 +603,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "Some _italic text_ on this lines\n\n"
|
||||
|
||||
@@ -611,7 +611,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
Tokenizer.T_TEXT, 0,
|
||||
"Some **_bold italic text_** on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
@@ -621,7 +621,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "Some **_bold italic text_** on this lines\n\n"
|
||||
|
||||
@@ -629,7 +629,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
Tokenizer.T_TEXT, 0,
|
||||
"Some ~~strikethrough text~~ on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_D_B],
|
||||
@@ -637,7 +637,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
||||
|
||||
@@ -645,7 +645,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
Tokenizer.T_TEXT, 0,
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
@@ -657,7 +657,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown[-1] == (
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||
@@ -679,11 +679,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
|
||||
correctResp = [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 2, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 5, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Command wo/Space
|
||||
@@ -727,12 +727,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Multiple Empty Paragraphs
|
||||
@@ -747,12 +747,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Three Skips
|
||||
@@ -764,14 +764,14 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 1
|
||||
@@ -783,11 +783,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 2
|
||||
@@ -799,11 +799,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 3
|
||||
@@ -815,11 +815,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Empty Paragraph and Page Break
|
||||
@@ -835,13 +835,13 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Multiple Skip
|
||||
@@ -854,15 +854,15 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
theToken.tokenizeText()
|
||||
assert theToken._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# END Test testCoreToken_SpecialFormat
|
||||
|
||||
Reference in New Issue
Block a user