Update linting for main code

This commit is contained in:
Veronica Berglyd Olsen
2025-08-27 21:00:09 +02:00
parent 84ff1f4640
commit c174f8f931
80 changed files with 461 additions and 1653 deletions
+3 -23
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -211,7 +211,7 @@ class FilterMode(Enum):
class BuildSettings:
"""Core: Build Settings Class
"""Core: Build Settings Class.
This class manages the build settings for a Manuscript build job.
The settings can be packed/unpacked to/from a dictionary for JSON.
@@ -229,7 +229,6 @@ class BuildSettings:
self._included = set()
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
self._changed = False
return
@classmethod
def fromDict(cls, data: dict) -> BuildSettings:
@@ -315,7 +314,6 @@ class BuildSettings:
def setName(self, name: str) -> None:
"""Set the build setting display name."""
self._name = str(name)
return
def setBuildID(self, value: str | uuid.UUID) -> None:
"""Set a UUID build ID."""
@@ -324,13 +322,11 @@ class BuildSettings:
self._uuid = str(uuid.uuid4())
elif value != self._uuid:
self._uuid = value
return
def setOrder(self, value: int) -> None:
"""Set the build order."""
if isinstance(value, int):
self._order = value
return
def setLastBuildPath(self, path: Path | str | None) -> None:
"""Set the last used build path."""
@@ -341,41 +337,35 @@ class BuildSettings:
else:
self._path = CONFIG.homePath()
self._changed = True
return
def setLastBuildName(self, name: str) -> None:
"""Set the last used build name."""
self._build = str(name).strip()
self._changed = True
return
def setLastFormat(self, value: nwBuildFmt) -> None:
"""Set the last used build format."""
if isinstance(value, nwBuildFmt):
self._format = value
self._changed = True
return
def setFiltered(self, tHandle: str) -> None:
"""Set an item as filtered."""
self._excluded.discard(tHandle)
self._included.discard(tHandle)
self._changed = True
return
def setIncluded(self, tHandle: str) -> None:
"""Set an item as explicitly included."""
self._excluded.discard(tHandle)
self._included.add(tHandle)
self._changed = True
return
def setExcluded(self, tHandle: str) -> None:
"""Set an item as explicitly excluded."""
self._excluded.add(tHandle)
self._included.discard(tHandle)
self._changed = True
return
def setAllowRoot(self, tHandle: str, state: bool) -> None:
"""Set a specific root folder as allowed or not."""
@@ -385,14 +375,12 @@ class BuildSettings:
elif state is False:
self._skipRoot.add(tHandle)
self._changed = True
return
def setValue(self, key: str, value: T_BuildValue) -> None:
"""Set a specific value for a build setting."""
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
self._changed |= (value != self._settings[key])
self._settings[key] = value
return
##
# Methods
@@ -463,7 +451,6 @@ class BuildSettings:
called when the changes have been safely saved or passed on.
"""
self._changed = False
return
def pack(self) -> dict:
"""Pack all content into a JSON compatible dictionary."""
@@ -516,8 +503,6 @@ class BuildSettings:
self._changed = False
return
@classmethod
def duplicate(cls, source: BuildSettings) -> BuildSettings:
"""Make a copy of another build."""
@@ -529,7 +514,7 @@ class BuildSettings:
class BuildCollection:
"""Core: Build Collection Class
"""Core: Build Collection Class.
This object holds all the build setting objects defined by the given
project. The build settings are saved as a single JSON file in the
@@ -542,7 +527,6 @@ class BuildCollection:
self._defaultBuild = ""
self._builds: dict[str, BuildSettings] = {}
self._loadCollection()
return
def __len__(self) -> int:
"""Return the number of builds."""
@@ -581,21 +565,18 @@ class BuildCollection:
build.setOrder(i)
self._lastBuild = lastBuild
self._saveCollection()
return
def setDefaultBuild(self, buildID: str) -> None:
"""Set the default build id."""
if buildID != self._defaultBuild:
self._defaultBuild = buildID
self._saveCollection()
return
def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection."""
if isinstance(build, BuildSettings):
self._builds[build.buildID] = build
self._saveCollection()
return
##
# Methods
@@ -605,7 +586,6 @@ class BuildCollection:
"""Remove a build from the collection."""
self._builds.pop(buildID, None)
self._saveCollection()
return
def builds(self) -> Iterable[tuple[str, str]]:
"""Iterate over all available builds."""
+10 -23
View File
@@ -23,7 +23,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -52,7 +52,9 @@ logger = logging.getLogger(__name__)
class DocMerger:
"""Document tool for merging a set of documents into a single new
"""Tool: Merge Documents.
Document tool for merging a set of documents into a single new
document. The parameters are defined by the user using the
GuiDocMerge dialog.
"""
@@ -62,7 +64,6 @@ class DocMerger:
self._error = ""
self._target = None
self._text = []
return
@property
def targetHandle(self) -> str | None:
@@ -85,7 +86,6 @@ class DocMerger:
"""
self._target = self._project.tree[tHandle]
self._text = []
return
def newTargetDoc(self, sHandle: str, label: str) -> None:
"""Create a brand new target document based on a source handle
@@ -101,7 +101,6 @@ class DocMerger:
nwItem.notifyToRefresh()
self._target = nwItem
self._text = []
return
def appendText(self, sHandle: str, addComment: bool, cmtPrefix: str) -> None:
"""Append text from an existing document to the text buffer."""
@@ -112,7 +111,6 @@ class DocMerger:
status, _ = item.getImportStatus()
text = f"% {cmtPrefix} {info}: {item.itemName} [{status}]\n\n{text}"
self._text.append(text)
return
def writeTargetDoc(self) -> bool:
"""Write the accumulated text into the designated target
@@ -158,10 +156,7 @@ class DocSplitter:
self._srcHandle = sHandle
self._srcItem = srcItem
return
def __len__(self) -> int:
"""The length of the split job."""
return len(self._rawData)
##
@@ -178,7 +173,6 @@ class DocSplitter:
"""
self._parHandle = pHandle
self._inFolder = False
return
def newParentFolder(self, pHandle: str, folderLabel: str) -> None:
"""Create a new folder that will be the top level parent item
@@ -192,7 +186,6 @@ class DocSplitter:
nwItem.notifyToRefresh()
self._parHandle = nHandle
self._inFolder = True
return
def splitDocument(self, splitData: list, splitText: list[str]) -> None:
"""Loop through the split data record and perform the split job
@@ -204,12 +197,9 @@ class DocSplitter:
chunk = buffer[lineNo:]
buffer = buffer[:lineNo]
self._rawData.insert(0, (chunk, hLevel, hLabel))
return
def writeDocuments(self, docHierarchy: bool) -> Iterable[bool]:
"""An iterator that will write each document in the buffer, and
return its new handle, parent handle, and sibling handle.
"""
"""Write each document in the buffer and yield if successful."""
if self._srcHandle and self._srcItem and self._parHandle:
pHandle = self._parHandle
hHandle = [self._parHandle, None, None, None, None]
@@ -260,7 +250,6 @@ class DocDuplicator:
def __init__(self, project: NWProject) -> None:
self._project = project
return
##
# Methods
@@ -293,13 +282,16 @@ class DocDuplicator:
class DocSearch:
"""Tool> Search Documents.
A global document search class.
"""
def __init__(self) -> None:
self._regEx = re.compile(r"")
self._opts = re.IGNORECASE
self._words = False
self._escape = True
return
##
# Methods
@@ -308,22 +300,19 @@ class DocSearch:
def setCaseSensitive(self, state: bool) -> None:
"""Set the case sensitive search flag."""
self._opts = 0 if state else re.IGNORECASE
return
def setWholeWords(self, state: bool) -> None:
"""Set the whole words search flag."""
self._words = state
return
def setUserRegEx(self, state: bool) -> None:
"""Set the escape flag to the opposite state."""
self._escape = not state
return
def iterSearch(
self, project: NWProject, search: str
) -> Iterable[tuple[NWItem, list[tuple[int, int, str]], bool]]:
"""Iteratively search through documents in a project."""
"""Iterate through documents in a project and apply search."""
self._regEx = re.compile(self._buildPattern(search), self._opts)
logger.debug("Searching with pattern '%s'", self._regEx.pattern)
storage = project.storage
@@ -376,7 +365,6 @@ class ProjectBuilder:
def __init__(self) -> None:
self._path = None
self.tr = partial(QCoreApplication.translate, "ProjectBuilder")
return
@property
def projPath(self) -> Path | None:
@@ -620,4 +608,3 @@ class ProjectBuilder:
project.index.rebuild()
project.saveProject()
project.closeProject()
return
+4 -9
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
class NWBuildDocument:
"""Core: Manuscript Document Build Class
"""Core: Manuscript Document Build Class.
This is the core tool that assembles a project and outputs a
manuscript, based on a build definition object (BuildSettings).
@@ -72,7 +72,6 @@ class NWBuildDocument:
self._cache = None
self._count = False
self._outline = False
return
##
# Properties
@@ -106,7 +105,6 @@ class NWBuildDocument:
def addDocument(self, tHandle: str) -> None:
"""Add a document to the build queue manually."""
self._queue.append(tHandle)
return
def queueAll(self) -> None:
"""Queue all document as defined by the build settings."""
@@ -115,7 +113,6 @@ class NWBuildDocument:
for item in self._project.tree:
if filtered.get(item.itemHandle, False):
self._queue.append(item.itemHandle)
return
def iterBuildPreview(self, newPage: bool) -> Iterable[tuple[int, bool]]:
"""Build a preview QTextDocument."""
@@ -131,7 +128,7 @@ class NWBuildDocument:
return
def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
"""Wrapper for builders based on format."""
"""Select a builder based on format."""
self._error = None
self._cache = None
@@ -341,12 +338,10 @@ class NWBuildDocument:
scale*self._build.getFloat("format.rightMargin"),
)
filtered = self._build.buildItemFilter(
return self._build.buildItemFilter(
self._project, withRoots=self._build.getBool("text.addNoteHeadings")
)
return filtered
def _doBuild(self, bldObj: Tokenizer, tHandle: str, convert: bool = True) -> bool:
"""Build a single document and add it to the build object."""
tItem = self._project.tree[tHandle]
+2 -6
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import hashlib
@@ -42,7 +42,7 @@ logger = logging.getLogger(__name__)
class NWDocument:
"""Core: Document Class
"""Core: Document Class.
A Class wrapping a single novelWriter document file. It represents
a project item of nwItemType FILE. The file is not guaranteed to
@@ -68,8 +68,6 @@ class NWDocument:
if self._handle is not None:
self._item = self._project.tree[tHandle]
return
def __repr__(self) -> str:
return f"<NWDocument handle={self._handle}>"
@@ -357,5 +355,3 @@ class NWDocument:
else:
logger.debug("Unknown meta data: '%s'", metaLine.strip())
return
+6 -42
View File
@@ -22,7 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -56,7 +56,7 @@ KEY_SOURCE = "0123456789bcdfghjklmnpqrstvwxz"
class Index:
"""Core: Project Index
"""Core: Project Index.
This class holds the entire index for a given project. The index
contains the data that isn't stored in the project items themselves.
@@ -99,8 +99,6 @@ class Index:
self._indexChange = 0.0
self._rootChange = {}
return
def __repr__(self) -> str:
return f"<Index project='{self._project.data.name}'>"
@@ -129,7 +127,6 @@ class Index:
def setNovelModelExtraColumn(self, extra: nwNovelExtra) -> None:
"""Set the data content type of the novel model extra column."""
self._novelExtra = extra
return
##
# Public Methods
@@ -142,7 +139,6 @@ class Index:
self._indexChange = 0.0
self._rootChange = {}
SHARED.emitIndexCleared(self._project)
return
def rebuild(self) -> None:
"""Rebuild the entire index from scratch."""
@@ -158,7 +154,6 @@ class Index:
for tHandle in self._novelModels:
self.refreshNovelModel(tHandle)
SHARED.clearMainProgress()
return
def deleteHandle(self, tHandle: str) -> None:
"""Delete all entries of a given document handle."""
@@ -168,7 +163,6 @@ class Index:
del self._tagsIndex[tTag]
del self._itemIndex[tHandle]
SHARED.emitIndexChangedTags(self._project, [], delTags)
return
def reIndexHandle(self, tHandle: str | None) -> None:
"""Put a file back into the index. This is used when files are
@@ -178,7 +172,6 @@ class Index:
if tHandle and self._project.tree.checkType(tHandle, nwItemType.FILE):
logger.debug("Re-indexing item '%s'", tHandle)
self.scanText(tHandle, self._project.storage.getDocumentText(tHandle))
return
def refreshHandle(self, tHandle: str) -> None:
"""Update the class for all tags of a handle."""
@@ -188,7 +181,6 @@ class Index:
self.deleteHandle(tHandle)
else:
self._tagsIndex.updateClass(tHandle, item.itemClass.name)
return
def indexChangedSince(self, checkTime: int | float) -> bool:
"""Check if the index has changed since a given time."""
@@ -211,7 +203,6 @@ class Index:
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model)
model.endResetModel()
return
def updateNovelModelData(self, nwItem: NWItem) -> bool:
"""Refresh a novel model."""
@@ -428,8 +419,6 @@ class Index:
if updated or deleted:
SHARED.emitIndexChangedTags(self._project, updated, deleted)
return
def _scanInactive(self, nwItem: NWItem, text: str) -> None:
"""Scan an inactive document for meta data."""
for line in text.splitlines():
@@ -438,7 +427,6 @@ class Index:
if hDepth != "H0":
nwItem.setMainHeading(hDepth)
break
return
def _splitHeading(self, line: str) -> tuple[str, str]:
"""Split a heading into its heading level and text value."""
@@ -462,7 +450,6 @@ class Index:
"""Count text stats and save the counts to the index."""
cC, wC, pC = standardCounter(text)
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
return
def _indexKeyword(
self, tHandle: str, line: str, sTitle: str, itemClass: nwItemClass, tags: dict[str, bool]
@@ -498,7 +485,6 @@ class Index:
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model)
self._novelModels[tHandle] = model
return
def _appendSubTreeToModel(self, tHandle: str, model: NovelModel) -> None:
"""Append all active novel documents to a novel model."""
@@ -509,7 +495,6 @@ class Index:
and node.item.isActive
):
model.append(node)
return
##
# Check @ Lines
@@ -683,15 +668,13 @@ class Index:
"words": hItem.wordCount,
}
result = [(
return [(
tKey,
tData[tKey]["level"],
tData[tKey]["title"],
tData[tKey]["words"]
) for tKey in tOrder]
return result
def getCounts(self, tHandle: str, sTitle: str | None = None) -> tuple[int, int, int]:
"""Return the counts for a file, or a section of a file,
starting at title sTitle if it is provided.
@@ -796,7 +779,7 @@ class Index:
# =====================
class TagsIndex:
"""Core: Tags Index Wrapper Class
"""Core: Tags Index Wrapper Class.
A wrapper class that holds the reverse lookup tags index. This is
just a simple wrapper around a single dictionary to keep tighter
@@ -807,14 +790,12 @@ class TagsIndex:
def __init__(self) -> None:
self._tags: dict[str, dict[str, str]] = {}
return
def __contains__(self, tagKey: str) -> bool:
return tagKey.lower() in self._tags
def __delitem__(self, tagKey: str) -> None:
self._tags.pop(tagKey.lower(), None)
return
def __getitem__(self, tagKey: str) -> dict | None:
return self._tags.get(tagKey.lower(), None)
@@ -826,7 +807,6 @@ class TagsIndex:
def clear(self) -> None:
"""Clear the index."""
self._tags = {}
return
def items(self) -> ItemsView:
"""Return a dictionary view of all tags."""
@@ -842,7 +822,6 @@ class TagsIndex:
"heading": sTitle,
"class": className,
}
return
def tagName(self, tagKey: str, default: str = "") -> str:
"""Get the name of a given tag."""
@@ -882,7 +861,6 @@ class TagsIndex:
for entry in self._tags.values():
if entry.get("handle") == tHandle:
entry["class"] = className
return
##
# Pack/Unpack
@@ -925,11 +903,9 @@ class TagsIndex:
self.add(name, display, handle, heading, className)
return
class IndexCache:
"""Core: Item Index Lookup Data Class
"""Core: Item Index Lookup Data Class.
A small data class passed between all objects of the Item Index
which provides lookup capabilities and caching for shared data.
@@ -941,14 +917,13 @@ class IndexCache:
self.tags: TagsIndex = tagsIndex
self.story: set[str] = set()
self.note: set[str] = set()
return
# The Item Index Objects
# ======================
class ItemIndex:
"""Core: Item Index Wrapper Class
"""Core: Item Index Wrapper Class.
A wrapper object holding the indexed items. This is a wrapper
class around a single storage dictionary with a set of utility
@@ -963,14 +938,12 @@ class ItemIndex:
self._project = project
self._cache = IndexCache(tagsIndex)
self._items: dict[str, IndexNode] = {}
return
def __contains__(self, tHandle: str) -> bool:
return tHandle in self._items
def __delitem__(self, tHandle: str) -> None:
self._items.pop(tHandle, None)
return
def __getitem__(self, tHandle: str) -> IndexNode | None:
return self._items.get(tHandle, None)
@@ -982,14 +955,12 @@ class ItemIndex:
def clear(self) -> None:
"""Clear the index."""
self._items = {}
return
def add(self, tHandle: str, nwItem: NWItem) -> None:
"""Add a new item to the index. This will overwrite the item if
it already exists.
"""
self._items[tHandle] = IndexNode(self._cache, tHandle, nwItem)
return
def allStoryKeys(self) -> set[str]:
"""Return all story structure keys."""
@@ -1064,7 +1035,6 @@ class ItemIndex:
"""
if tHandle in self._items:
self._items[tHandle].setHeadingCounts(sTitle, cC, wC, pC)
return
def setHeadingComment(
self, tHandle: str, sTitle: str,
@@ -1073,25 +1043,21 @@ class ItemIndex:
"""Set a story comment for a heading on a given item."""
if tHandle in self._items:
self._items[tHandle].setHeadingComment(sTitle, comment, key, text)
return
def setHeadingTag(self, tHandle: str, sTitle: str, tagKey: str) -> None:
"""Set the main tag for a heading on a given item."""
if tHandle in self._items:
self._items[tHandle].setHeadingTag(sTitle, tagKey)
return
def addHeadingRef(self, tHandle: str, sTitle: str, tagKeys: list[str], refType: str) -> None:
"""Set the reference tags for a heading on a given item."""
if tHandle in self._items:
self._items[tHandle].addHeadingRef(sTitle, tagKeys, refType)
return
def addNoteKey(self, tHandle: str, style: T_NoteTypes, key: str) -> None:
"""Set notes key for a given item."""
if tHandle in self._items:
self._items[tHandle].addNoteKey(style, key)
return
def genNewNoteKey(self, tHandle: str, style: T_NoteTypes) -> str:
"""Set notes key for a given item."""
@@ -1131,5 +1097,3 @@ class ItemIndex:
tItem = IndexNode(self._cache, tHandle, nwItem)
tItem.unpackData(tData)
self._items[tHandle] = tItem
return
+3 -19
View File
@@ -23,7 +23,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -50,7 +50,7 @@ NOTE_TYPES: list[T_NoteTypes] = ["footnotes", "comments"]
class IndexNode:
"""Core: Single Index Item Node Class
"""Core: Single Index Item Node Class.
This object represents the index data of a project item (NWItem).
It holds a record of all the headings in the text, and the meta data
@@ -68,7 +68,6 @@ class IndexNode:
self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(self._cache, TT_NONE)}
self._notes: dict[str, set[str]] = {}
self._count = 0
return
def __repr__(self) -> str:
return f"<IndexNode handle='{self._handle}'>"
@@ -107,39 +106,33 @@ class IndexNode:
if TT_NONE in self._headings:
self._headings.pop(TT_NONE)
self._headings[tHeading.key] = tHeading
return
def setHeadingCounts(self, sTitle: str, cCount: int, wCount: int, pCount: int) -> None:
"""Set the character, word and paragraph count of a heading."""
if sTitle in self._headings:
self._headings[sTitle].setCounts([cCount, wCount, pCount])
return
def setHeadingComment(self, sTitle: str, comment: nwComment, key: str, text: str) -> None:
"""Set the comment text of a heading."""
if sTitle in self._headings:
self._headings[sTitle].setComment(comment.name, key, text)
return
def setHeadingTag(self, sTitle: str, tag: str) -> None:
"""Set the tag of a heading."""
if sTitle in self._headings:
self._headings[sTitle].setTag(tag)
return
def addHeadingRef(self, sTitle: str, tags: list[str], keyword: str) -> None:
"""Add a reference key and all its types to a heading."""
if sTitle in self._headings:
for tag in tags:
self._headings[sTitle].addReference(tag, keyword)
return
def addNoteKey(self, style: T_NoteTypes, key: str) -> None:
"""Add a note key to the index."""
if style not in self._notes:
self._notes[style] = set()
self._notes[style].add(key)
return
##
# Data Methods
@@ -195,11 +188,10 @@ class IndexNode:
self._notes[style] = set(keys)
else:
raise KeyError("Index node contains an invalid key")
return
class IndexHeading:
"""Core: Single Index Heading Class
"""Core: Single Index Heading Class.
This object represents a section of text in a project item
associated with a single (valid) heading. It holds a separate record
@@ -224,7 +216,6 @@ class IndexHeading:
self._tag = ""
self._refs: dict[str, set[str]] = {}
self._comments: dict[str, str] = {}
return
def __repr__(self) -> str:
return f"<IndexHeading key='{self._key}'>"
@@ -289,12 +280,10 @@ class IndexHeading:
"""Set the level of the heading if it's a valid value."""
if level in nwStyles.H_VALID:
self._level = level
return
def setLine(self, line: int) -> None:
"""Set the line number of a heading."""
self._line = max(0, checkInt(line, 0))
return
def setCounts(self, counts: Sequence[int]) -> None:
"""Set the character, word and paragraph count. Make sure the
@@ -306,7 +295,6 @@ class IndexHeading:
max(0, checkInt(counts[1], 0)),
max(0, checkInt(counts[2], 0)),
)
return
def setComment(self, comment: str, key: str, text: str) -> None:
"""Set the text for a comment and make sure it is a string."""
@@ -319,12 +307,10 @@ class IndexHeading:
case "note" if key:
self._cache.note.add(key)
self._comments[f"note.{key}"] = str(text)
return
def setTag(self, tag: str) -> None:
"""Set the tag for references, and make sure it is a string."""
self._tag = str(tag).lower()
return
def addReference(self, tag: str, keyword: str) -> None:
"""Add a record of a reference tag, and what keyword types it is
@@ -335,7 +321,6 @@ class IndexHeading:
if tag not in self._refs:
self._refs[tag] = set()
self._refs[tag].add(keyword)
return
##
# Getters
@@ -409,4 +394,3 @@ class IndexHeading:
self.setComment(comment, compact(kind), str(entry))
else:
raise KeyError("Unknown key in heading entry")
return
+7 -31
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
class NWItem:
"""Core: Item Data Class
"""Core: Item Data Class.
This class holds all the project information about a project item.
Each item must be associated with a project and have a valid handle.
@@ -84,16 +84,14 @@ class NWItem:
self._wordInit = 0 # Initial character count
self._charInit = 0 # Initial word count
return
def __repr__(self) -> str:
return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>"
def __bool__(self) -> bool:
"""The truthiness of the class. The handle used to be initiated
to None, but this is no longer the case. It should always
evaluate to True since 2.1-beta1, although unpack and the NWTree
class can leave it as an empty string.
"""Check the truthiness of the class. The handle used to be
initiated to None, but this is no longer the case. It should
always evaluate to True since 2.1-beta1, although unpack and the
NWTree class can leave it as an empty string.
"""
return bool(self._handle)
@@ -206,15 +204,13 @@ class NWItem:
meta["cursorPos"] = str(self._cursorPos)
name["active"] = yesNo(self._active)
data = {
return {
"name": str(self._name),
"itemAttr": item,
"metaAttr": meta,
"nameAttr": name,
}
return data
def unpack(self, data: dict) -> bool:
"""Set the values from a data dictionary."""
item = data.get("itemAttr", {})
@@ -298,13 +294,11 @@ class NWItem:
def notifyToRefresh(self) -> None:
"""Notify GUI that item info needs to be refreshed."""
self._project.tree.refreshItems([self._handle])
return
def notifyNovelStructureChange(self) -> None:
"""Notify that the structure of a novel has changed."""
if self._root and self._class == nwItemClass.NOVEL:
self._project.tree.novelStructureChanged(self._root)
return
##
# Lookup Methods
@@ -457,8 +451,6 @@ class NWItem:
if self._import is None:
self.setImport("New") # This forces a default value lookup
return
##
# Set Item Values
##
@@ -469,7 +461,6 @@ class NWItem:
self._name = simplified(name)
else:
self._name = ""
return
def setParent(self, handle: Any) -> None:
"""Set the parent handle, and ensure it is valid."""
@@ -479,7 +470,6 @@ class NWItem:
self._parent = handle
else:
self._parent = None
return
def setRoot(self, handle: Any) -> None:
"""Set the root handle, and ensure it is valid."""
@@ -489,7 +479,6 @@ class NWItem:
self._root = handle
else:
self._root = None
return
def setOrder(self, order: Any) -> None:
"""Set the item order, and ensure that it is valid. This value
@@ -497,7 +486,6 @@ class NWItem:
the moment.
"""
self._order = checkInt(order, 0)
return
def setType(self, value: Any) -> None:
"""Set the item type from either a proper nwItemType, or set it
@@ -510,7 +498,6 @@ class NWItem:
else:
logger.error("Unrecognised item type '%s'", value)
self._type = nwItemType.NO_TYPE
return
def setClass(self, value: Any) -> None:
"""Set the item class from either a proper nwItemClass, or set
@@ -523,7 +510,6 @@ class NWItem:
else:
logger.error("Unrecognised item class '%s'", value)
self._class = nwItemClass.NO_CLASS
return
def setLayout(self, value: Any) -> None:
"""Set the item layout from either a proper nwItemLayout, or set
@@ -536,21 +522,18 @@ class NWItem:
else:
logger.error("Unrecognised item layout '%s'", value)
self._layout = nwItemLayout.NO_LAYOUT
return
def setStatus(self, value: Any) -> None:
"""Set the item status by looking it up in the valid status
items of the current project.
"""
self._status = self._project.data.itemStatus.check(value)
return
def setImport(self, value: Any) -> None:
"""Set the item importance by looking it up in the valid import
items of the current project.
"""
self._import = self._project.data.itemImport.check(value)
return
def setActive(self, state: Any) -> None:
"""Set the active flag."""
@@ -558,7 +541,6 @@ class NWItem:
self._active = state
else:
self._active = False
return
def setExpanded(self, state: Any) -> None:
"""Set the expanded status of an item in the project tree."""
@@ -566,7 +548,6 @@ class NWItem:
self._expanded = state
else:
self._expanded = False
return
##
# Set Document Meta Data
@@ -576,7 +557,6 @@ class NWItem:
"""Set the main heading level."""
if value in nwStyles.H_LEVEL:
self._heading = value
return
def setCharCount(self, count: Any) -> None:
"""Set the character count, and ensure that it is an integer."""
@@ -584,7 +564,6 @@ class NWItem:
self._charCount = max(0, count)
else:
self._charCount = 0
return
def setWordCount(self, count: Any) -> None:
"""Set the word count, and ensure that it is an integer."""
@@ -592,7 +571,6 @@ class NWItem:
self._wordCount = max(0, count)
else:
self._wordCount = 0
return
def setParaCount(self, count: Any) -> None:
"""Set the paragraph count, and ensure that it is an integer."""
@@ -600,7 +578,6 @@ class NWItem:
self._paraCount = max(0, count)
else:
self._paraCount = 0
return
def setCursorPos(self, position: Any) -> None:
"""Set the cursor position, and ensure that it is an integer."""
@@ -608,4 +585,3 @@ class NWItem:
self._cursorPos = max(0, position)
else:
self._cursorPos = 0
return
+5 -21
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -66,7 +66,7 @@ T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None
class ProjectNode:
"""Core: Project Model Node Class
"""Core: Project Model Node Class.
The project tree structure is saved as nodes in a tree, starting
from a root node. This class makes up these nodes.
@@ -103,7 +103,6 @@ class ProjectNode:
self._count = 0
self.refresh()
self.updateCount()
return
def __repr__(self) -> str:
return (
@@ -114,7 +113,7 @@ class ProjectNode:
)
def __bool__(self) -> bool:
"""A node should always evaluate to True."""
# A node should always evaluate to True.
return True
##
@@ -162,15 +161,12 @@ class ProjectNode:
self._cache[C_STATUS_TIP] = sText
self._cache[C_STATUS_ACCESS] = sText
return
def updateCount(self, propagate: bool = True) -> None:
"""Update counts, and propagate upwards in the tree."""
self._count = self._item.mainCount + sum(c._count for c in self._children) # noqa: SLF001
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
if propagate and (parent := self._parent):
parent.updateCount()
return
##
# Data Access
@@ -223,7 +219,6 @@ class ProjectNode:
self._children.append(child)
self._refreshChildrenPos()
self._item.notifyNovelStructureChange()
return
def takeChild(self, pos: int) -> ProjectNode | None:
"""Remove a child item and return it."""
@@ -243,7 +238,6 @@ class ProjectNode:
self._children.insert(target, node)
self._refreshChildrenPos()
self._item.notifyNovelStructureChange()
return
def setExpanded(self, state: bool) -> None:
"""Set the node's expanded state."""
@@ -251,7 +245,6 @@ class ProjectNode:
self._item.setExpanded(True)
else:
self._item.setExpanded(False)
return
##
# Internal Functions
@@ -262,14 +255,12 @@ class ProjectNode:
for node in self._children:
children.append(node)
node._recursiveAppendChildren(children) # noqa: SLF001
return
def _refreshChildrenPos(self) -> None:
"""Update the row value on all children."""
for n, child in enumerate(self._children):
child._row = n # noqa: SLF001
child.item.setOrder(n)
return
def _updateRelationships(self, child: ProjectNode) -> None:
"""Update a child item's relationships."""
@@ -282,11 +273,10 @@ class ProjectNode:
child.item.setParent(None)
child.item.setRoot(child.item.itemHandle)
child.item.setClassDefaults(child.item.itemClass)
return
class ProjectModel(QAbstractItemModel):
"""Core: Project Model Class
"""Core: Project Model Class.
This class provides the interface for the tree widget used on the
GUI. It implements the QModelIndex based interface required, adds
@@ -302,11 +292,9 @@ class ProjectModel(QAbstractItemModel):
self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
self._root.item.setName("Invisible Root")
logger.debug("Ready: ProjectModel")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: ProjectModel")
return
##
# Properties
@@ -363,7 +351,7 @@ class ProjectModel(QAbstractItemModel):
##
def supportedDropActions(self) -> Qt.DropAction:
"""Return supported drop actions"""
"""Return supported drop actions."""
return Qt.DropAction.MoveAction
def mimeTypes(self) -> list[str]:
@@ -445,7 +433,6 @@ class ProjectModel(QAbstractItemModel):
self.beginInsertRows(parent, row, row)
node.addChild(child, row)
self.endInsertRows()
return
def removeChild(self, parent: QModelIndex, pos: int) -> ProjectNode | None:
"""Remove a node from the model and return it."""
@@ -469,7 +456,6 @@ class ProjectModel(QAbstractItemModel):
self.beginMoveRows(index.parent(), pos, pos, index.parent(), end)
parent.moveChild(pos, new)
self.endMoveRows()
return
def multiMove(self, indices: list[QModelIndex], target: QModelIndex, pos: int = -1) -> None:
"""Move multiple items to a new location."""
@@ -497,7 +483,6 @@ class ProjectModel(QAbstractItemModel):
node._updateRelationships(child) # noqa: SLF001
child.item.notifyToRefresh()
node.item.notifyToRefresh()
return
##
# Other Methods
@@ -506,7 +491,6 @@ class ProjectModel(QAbstractItemModel):
def clear(self) -> None:
"""Clear the project model."""
self._root.children.clear()
return
def allExpanded(self) -> list[QModelIndex]:
"""Return a list of all expanded items."""
+2 -6
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -54,6 +54,7 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
class NovelModel(QAbstractTableModel):
"""Core: Novel Model CLass."""
__slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
@@ -64,11 +65,9 @@ class NovelModel(QAbstractTableModel):
self._columns = 3
self._extraKey = ""
self._extraLabel = ""
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NovelModel")
return
##
# Properties
@@ -102,7 +101,6 @@ class NovelModel(QAbstractTableModel):
self._columns = 4
self._extraKey = nwKeyWords.PLOT_KEY
self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
return
##
# Model Interface
@@ -147,7 +145,6 @@ class NovelModel(QAbstractTableModel):
def clear(self) -> None:
"""Clear the model."""
self._rows.clear()
return
def append(self, node: IndexNode) -> None:
"""Append a node to the model."""
@@ -155,7 +152,6 @@ class NovelModel(QAbstractTableModel):
for key, head in node.items():
if key != "T0000":
self._rows.append(self._generateEntry(handle, key, head))
return
def refresh(self, node: IndexNode) -> bool:
"""Refresh an index node."""
+2 -3
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -80,7 +80,7 @@ VALID_MAP: dict[str, set[str]] = {
class OptionState:
"""Core: GUI Options Storage
"""Core: GUI Options Storage.
A class for storing the state of the GUI. The data is stored per
project. Settings that should be project-independent are stored in
@@ -90,7 +90,6 @@ class OptionState:
def __init__(self, project: NWProject) -> None:
self._project = project
self._state = {}
return
##
# Load and Save Cache
+7 -13
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -57,6 +57,7 @@ logger = logging.getLogger(__name__)
class NWProjectState(Enum):
"""The state of the loaded project."""
UNKNOWN = 0
LOCKED = 1
@@ -65,6 +66,11 @@ class NWProjectState(Enum):
class NWProject:
"""Core: novelWriter Project Class.
This class is the parent class of the project, and holds instances
of project data, the project tree, and the project index.
"""
__slots__ = (
"_changed", "_data", "_index", "_langData", "_options", "_session",
@@ -92,17 +98,13 @@ class NWProject:
logger.debug("Ready: NWProject")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NWProject")
return
def clear(self) -> None:
"""Clear the project."""
self._tree.clear()
self._index.clear()
return
##
# Properties
@@ -263,7 +265,6 @@ class NWProject:
if rHandle and (tHandle := SHARED.project.newFile(tag.title(), rHandle)):
self.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n")
self._tree.refreshItems([tHandle])
return
##
# Project Methods
@@ -441,7 +442,6 @@ class NWProject:
self._tree.writeToCFile()
self._session.appendSession(idleTime)
self._storage.closeSession()
return
def backupProject(self, doNotify: bool) -> bool:
"""Create a zip file of the entire project."""
@@ -499,7 +499,6 @@ class NWProject:
self._data.itemImport.add(None, self.tr("Minor"), "purple", "BLOCK_2", 0)
self._data.itemImport.add(None, self.tr("Major"), "purple", "BLOCK_3", 0)
self._data.itemImport.add(None, self.tr("Main"), "purple", "BLOCK_4", 0)
return
def setProjectLang(self, language: str | None) -> None:
"""Set the project-specific language."""
@@ -508,7 +507,6 @@ class NWProject:
self._data.setLanguage(language)
self._loadProjectLocalisation()
self.setProjectChanged(True)
return
def setProjectChanged(self, status: bool) -> bool:
"""Toggle the project changed flag, and propagate the
@@ -527,7 +525,6 @@ class NWProject:
"""Update the total word and character count values."""
wNovel, wNotes, cNovel, cNotes = self._tree.sumCounts()
self._data.setCurrCounts(wNovel=wNovel, wNotes=wNotes, cNovel=cNovel, cNotes=cNotes)
return
def countStatus(self) -> None:
"""Count how many times the various status flags are used in the
@@ -541,7 +538,6 @@ class NWProject:
self._data.itemStatus.increment(nwItem.itemStatus)
else:
self._data.itemImport.increment(nwItem.itemImport)
return
def updateStatus(self, kind: T_StatusKind, update: T_UpdateEntry) -> None:
"""Update status or import entries."""
@@ -553,13 +549,11 @@ class NWProject:
self._data.itemImport.update(update)
SHARED.emitStatusLabelsChanged(self, kind)
self._tree.refreshAllItems()
return
def updateTheme(self) -> None:
"""Update theme elements."""
self._data.itemStatus.refreshIcons()
self._data.itemImport.refreshIcons()
return
def localLookup(self, word: str | int) -> str:
"""Look up a word or number in the translation map for the
+2 -21
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
class NWProjectData:
"""Core: Project Data Class
"""Core: Project Data Class.
The class holds all project data from the main XML file, aside from
the list of project items.
@@ -86,8 +86,6 @@ class NWProjectData:
self._status = NWStatus(NWStatus.STATUS)
self._import = NWStatus(NWStatus.IMPORT)
return
##
# Properties
##
@@ -191,13 +189,11 @@ class NWProjectData:
"""Increment the save count by one."""
self._saveCount += 1
self._project.setProjectChanged(True)
return
def incAutoCount(self) -> None:
"""Increment the auto save count by one."""
self._autoCount += 1
self._project.setProjectChanged(True)
return
##
# Getters
@@ -219,67 +215,57 @@ class NWProjectData:
elif value != self._uuid:
self._uuid = value
self._project.setProjectChanged(True)
return
def setName(self, value: str | None) -> None:
"""Set a new project name."""
if value != self._name:
self._name = simplified(str(value or ""))
self._project.setProjectChanged(True)
return
def setAuthor(self, value: str | None) -> None:
"""Set the author value."""
if value != self._author:
self._author = simplified(str(value or ""))
self._project.setProjectChanged(True)
return
def setSaveCount(self, value: Any) -> None:
"""Set the save count from last session."""
self._saveCount = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setAutoCount(self, value: Any) -> None:
"""Set the auto save count from last session."""
self._autoCount = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setEditTime(self, value: Any) -> None:
"""Set the edit time from last session."""
self._editTime = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setDoBackup(self, value: Any) -> None:
"""Set the do write backup flag."""
if value != self._doBackup:
self._doBackup = checkBool(value, False)
self._project.setProjectChanged(True)
return
def setLanguage(self, value: str | None) -> None:
"""Set the project language."""
if value != self._language:
self._language = checkStringNone(value, None)
self._project.setProjectChanged(True)
return
def setSpellCheck(self, value: Any) -> None:
"""Set the spell check flag."""
if value != self._spellCheck:
self._spellCheck = checkBool(value, False)
self._project.setProjectChanged(True)
return
def setSpellLang(self, value: str | None) -> None:
"""Set the spell check language."""
if value != self._spellLang:
self._spellLang = checkStringNone(value, None)
self._project.setProjectChanged(True)
return
def setLastHandle(self, value: str | None, component: str) -> None:
"""Set a last used handle into the handle registry for a given
@@ -288,7 +274,6 @@ class NWProjectData:
if isinstance(component, str):
self._lastHandle[component] = checkStringNone(value, None)
self._project.setProjectChanged(True)
return
def setLastHandles(self, value: dict) -> None:
"""Set the full last handles dictionary to a new set of values.
@@ -299,7 +284,6 @@ class NWProjectData:
if key in self._lastHandle:
self._lastHandle[key] = str(entry) if isHandle(entry) else None
self._project.setProjectChanged(True)
return
def setInitCounts(
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
@@ -321,7 +305,6 @@ class NWProjectData:
count = checkInt(cNotes, 0)
self._initCounts[3] = count
self._currCounts[3] = count
return
def setCurrCounts(
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
@@ -335,7 +318,6 @@ class NWProjectData:
self._currCounts[2] = checkInt(cNovel, 0)
if cNotes is not None:
self._currCounts[3] = checkInt(cNotes, 0)
return
def setAutoReplace(self, value: dict) -> None:
"""Set the auto-replace dictionary."""
@@ -345,4 +327,3 @@ class NWProjectData:
if isinstance(entry, str):
self._autoReplace[key] = simplified(entry)
self._project.setProjectChanged(True)
return
+3 -16
View File
@@ -22,7 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -72,7 +72,7 @@ class XMLReadState(Enum):
class ProjectXMLReader:
"""Core: Project XML Reader
"""Core: Project XML Reader.
All data is read into a NWProjectData instance, which must be
provided.
@@ -124,7 +124,6 @@ class ProjectXMLReader:
self._appVersion = ""
self._hexVersion = 0x0
self._timeStamp = ""
return
##
# Properties
@@ -254,8 +253,6 @@ class ProjectXMLReader:
elif xItem.tag == "editTime": # Moved to attribute in 1.5
data.setEditTime(xItem.text)
return
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None:
"""Parse the settings section of the XML file."""
logger.debug("Parsing <settings> section")
@@ -294,8 +291,6 @@ class ProjectXMLReader:
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
data.setInitCounts(wNotes=xItem.text)
return
def _parseProjectContent(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
@@ -356,8 +351,6 @@ class ProjectXMLReader:
"nameAttr": name,
})
return
def _parseProjectContentLegacy(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
@@ -434,8 +427,6 @@ class ProjectXMLReader:
"nameAttr": name,
})
return
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
"""Parse a status or importance entry."""
for xEntry in xItem:
@@ -450,7 +441,6 @@ class ProjectXMLReader:
if color is None:
color = f"{red}, {green}, {blue}"
sObject.add(key, xEntry.text or "", color, shape, count)
return
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
"""Parse a dictionary stored with key as an attribute and the
@@ -470,7 +460,7 @@ class ProjectXMLReader:
class ProjectXMLWriter:
"""Core: Project XML Writer
"""Core: Project XML Writer.
The project writer class will only write a file according to the
very latest spec.
@@ -479,7 +469,6 @@ class ProjectXMLWriter:
def __init__(self, path: str | Path) -> None:
self._path = Path(path)
self._error = None
return
##
# Properties
@@ -580,7 +569,6 @@ class ProjectXMLWriter:
"""Pack a single value into an XML element."""
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
xItem.text = str(value) or ""
return
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None:
"""Pack the entries of a dictionary into an XML element."""
@@ -589,4 +577,3 @@ class ProjectXMLWriter:
if len(key) > 0:
xEntry = ET.SubElement(xItem, "entry", attrib={"key": key})
xEntry.text = str(value) or ""
return
+2 -4
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
class NWSessionLog:
"""Core: Session JSON Lines Log File
"""Core: Session JSON Lines Log File.
The class that wraps the session log file, which is in JSON Lines
format. That is, one JSON object per line.
@@ -52,7 +52,6 @@ class NWSessionLog:
def __init__(self, project: NWProject) -> None:
self._project = project
self._start = 0.0
return
##
# Properties
@@ -70,7 +69,6 @@ class NWSessionLog:
def startSession(self) -> None:
"""Start the writing session."""
self._start = time()
return
def appendSession(self, idleTime: float) -> bool:
"""Append session statistics to the sessions log file."""
+12 -13
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
class NWSpellEnchant:
"""Core: Enchant Spell Checking Wrapper
"""Core: Enchant Spell Checking Wrapper.
This is a rapper class for Enchant to keep the API consistent
between spell check tools.
@@ -57,11 +57,9 @@ class NWSpellEnchant:
self._language = None
self._broker = None
logger.debug("Ready: NWSpellEnchant")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NWSpellEnchant")
return
##
# Properties
@@ -106,21 +104,19 @@ class NWSpellEnchant:
for word in self._userDict:
self._enchant.add_to_session(word)
return
##
# Methods
##
def checkWord(self, word: str) -> bool:
"""Wrapper function for pyenchant."""
"""Forward check to pyenchant."""
try:
return bool(self._enchant.check(word))
except Exception:
return True
def suggestWords(self, word: str) -> list[str]:
"""Wrapper function for pyenchant."""
"""Ask pyenchant for suggestions."""
try:
return self._enchant.suggest(word)
except Exception:
@@ -172,24 +168,29 @@ class FakeEnchant:
self.tag = ""
self.provider = FakeProvider()
return
def check(self, word: str) -> bool:
"""Return True for all words."""
return True
def suggest(self, word: str) -> list[str]:
"""Return an empty suggestion list."""
return []
def add_to_session(self, word: str) -> None:
"""Do nothing."""
return
class UserDictionary:
"""Core: User Word Dictionary.
This class holds all the user's own words for spell checking
purposes. The dictionary is per-project.
"""
def __init__(self, project: NWProject) -> None:
self._project = project
self._words = set()
return
def __contains__(self, word: str) -> bool:
return word in self._words
@@ -219,7 +220,6 @@ class UserDictionary:
except Exception:
logger.error("Failed to load user dictionary")
logException()
return
def save(self) -> None:
"""Save the user's dictionary."""
@@ -232,4 +232,3 @@ class UserDictionary:
except Exception:
logger.error("Failed to save user dictionary")
logException()
return
+3 -8
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import dataclasses
@@ -49,6 +49,7 @@ CUSTOM_COL = "custom"
@dataclasses.dataclass
class StatusEntry:
"""DataClass: Status Label Values."""
name: str
color: QColor
@@ -73,6 +74,7 @@ T_StatusKind = Literal["s", "i"]
class NWStatus:
"""Core: Status/Importance Label Class."""
STATUS = "s"
IMPORT = "i"
@@ -84,7 +86,6 @@ class NWStatus:
self._default = None
self._prefix = prefix[:1]
self._height = SHARED.theme.baseIconHeight
return
def __len__(self) -> int:
return len(self._store)
@@ -133,8 +134,6 @@ class NWStatus:
if self._default not in self._store:
self._default = next(iter(self._store)) if self._store else None
return
def check(self, value: str) -> str:
"""Check the key against the stored status names."""
if self._isKey(value) and value in self._store:
@@ -147,13 +146,11 @@ class NWStatus:
"""Clear the counts of references to the status entries."""
for entry in self._store.values():
entry.count = 0
return
def increment(self, key: str | None) -> None:
"""Increment the counter for a given entry."""
if key and key in self._store:
self._store[key].count += 1
return
def pack(self) -> Iterable[tuple[str, dict]]:
"""Pack the status entries into a dictionary."""
@@ -195,7 +192,6 @@ class NWStatus:
if entry.theme != CUSTOM_COL:
entry.color = SHARED.theme.parseColor(entry.theme)
entry.icon = NWStatus.createIcon(self._height, entry.color, entry.shape)
return
@staticmethod
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
@@ -252,7 +248,6 @@ class _ShapeCache:
def __init__(self) -> None:
self._cache: dict[nwStatusShape, QPainterPath] = {}
return
def getShape(self, shape: nwStatusShape) -> QPainterPath:
"""Return a painter shape for an icon."""
+5 -10
View File
@@ -20,7 +20,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import json
@@ -47,6 +47,7 @@ logger = logging.getLogger(__name__)
class NWStorageOpen(Enum):
"""The status of a storage location."""
UNKOWN = 0
NOT_FOUND = 1
@@ -56,6 +57,7 @@ class NWStorageOpen(Enum):
class NWStorageCreate(Enum):
"""The status of a new storage location."""
NOT_EMPTY = 0
OS_ERROR = 1
@@ -63,7 +65,7 @@ class NWStorageCreate(Enum):
class NWStorage:
"""Core: Project Storage Class
"""Core: Project Storage Class.
The class that handles all paths related to the project storage.
"""
@@ -81,7 +83,6 @@ class NWStorage:
self._openMode = self.MODE_INACTIVE
self._ready = False
self._exception = None
return
def clear(self) -> None:
"""Reset internal variables."""
@@ -90,7 +91,6 @@ class NWStorage:
self._lockFilePath = None
self._openMode = self.MODE_INACTIVE
self._ready = False
return
##
# Properties
@@ -252,13 +252,11 @@ class NWStorage:
"""Lock the session when the project is successfully opened."""
if self._ready:
self._writeLockFile()
return
def closeSession(self) -> None:
"""Run tasks related to closing the session."""
self._clearLockFile()
self.clear()
return
##
# Content Access Methods
@@ -394,7 +392,7 @@ class NWStorage:
class _LegacyStorage:
"""Core: Legacy Storage Converter Utils
"""Core: Legacy Storage Converter Utils.
A class with various functions to convert old file formats and
file/folder layouts to the current project format.
@@ -402,7 +400,6 @@ class _LegacyStorage:
def __init__(self, project: NWProject) -> None:
self._project = project
return
def legacyDataFolder(self, path: Path, child: Path) -> None:
"""Handle the content of a legacy data folder from a version 1.0
@@ -484,8 +481,6 @@ class _LegacyStorage:
except Exception as exc:
logger.warning("Failed to delete: %s", item, exc_info=exc)
return
##
# Internal Functions
##
+5 -14
View File
@@ -21,7 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa
from __future__ import annotations
import logging
@@ -50,7 +50,7 @@ MAX_DEPTH = 999 # Cap of tree traversing for loops (recursion limit)
class NWTree:
"""Core: Project Tree Data Class
"""Core: Project Tree Data Class.
Only one instance of this class should exist in the project class.
This class holds all the project items of the project as instances
@@ -71,18 +71,16 @@ class NWTree:
self._trash = None
self._ready = False
logger.debug("Ready: NWTree")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NWTree")
return
def __len__(self) -> int:
"""The number of items in the project."""
"""Return the number of items in the project."""
return len(self._items)
def __bool__(self) -> bool:
"""True if there are any items in the project."""
"""Return True if there are any items in the project."""
return bool(self._items)
def __getitem__(self, tHandle: str | None) -> NWItem | None:
@@ -95,7 +93,7 @@ class NWTree:
return None
def __contains__(self, tHandle: str) -> bool:
"""Checks if a handle exists in the tree."""
"""Check if a handle exists in the tree."""
return tHandle in self._items
def __iter__(self) -> Iterator[NWItem]:
@@ -142,7 +140,6 @@ class NWTree:
self._trash = None
oldModel.deleteLater()
del oldModel
return
def add(self, item: NWItem, pos: int = -1) -> bool:
"""Add a project item into the project tree."""
@@ -260,8 +257,6 @@ class NWTree:
self._model.endInsertRows()
self._model.layoutChanged.emit()
return
def pickParent(self, sNode: ProjectNode, hLevel: int, isNote: bool) -> tuple[str | None, int]:
"""Pick an appropriate parent handle for adding a new item."""
if sNode.item.isFolderType() or sNode.item.isRootType():
@@ -299,7 +294,6 @@ class NWTree:
indexE = self._model.indexFromNode(node, 3)
self._model.dataChanged.emit(indexS, indexE)
self._itemChange(node.item, nwChange.UPDATE)
return
def refreshAllItems(self) -> None:
"""Refresh all items in the tree."""
@@ -309,13 +303,11 @@ class NWTree:
self._model.root.refresh()
self._model.root.updateCount(propagate=False)
self._model.layoutChanged.emit()
return
def novelStructureChanged(self, tHandle: str) -> None:
"""Emit a novel structure change signal."""
if self._ready:
SHARED.novelStructureChanged.emit(tHandle)
return
def checkConsistency(self, prefix: str) -> tuple[int, int]:
"""Check the project tree consistency. Also check the content
@@ -496,7 +488,6 @@ class NWTree:
SHARED.emitProjectItemChanged(self._project, tHandle, change)
if item.isRootType():
SHARED.emitRootFolderChanged(self._project, tHandle, change)
return
def _getTrashNode(self) -> ProjectNode | None:
"""Get the trash node. If it doesn't exist, create it."""