Add Pyright to syntax workflow and fix errors (#2279)
This commit is contained in:
@@ -19,6 +19,6 @@ Please check the following before you make a pull request:
|
|||||||
* [ ] The header of all files contain a reference to the repository license
|
* [ ] The header of all files contain a reference to the repository license
|
||||||
* [ ] The overall test coverage is increased or remains the same as before
|
* [ ] The overall test coverage is increased or remains the same as before
|
||||||
* [ ] All tests are passing
|
* [ ] All tests are passing
|
||||||
* [ ] All flake8 checks are passing and the style guide is followed
|
* [ ] All linting checks are passing and the style guide is followed
|
||||||
* [ ] Documentation (as docstrings) is complete and understandable
|
* [ ] Documentation (as docstrings) is complete and understandable
|
||||||
* [ ] Only files that have been actively changed are committed
|
* [ ] Only files that have been actively changed are committed
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Flake8
|
name: Linting
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -21,10 +21,14 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
- name: Checkout Source
|
- name: Checkout Source
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install flake8
|
- name: Install Dependencies
|
||||||
run: pip install -r requirements-dev.txt
|
run: pip install -r requirements.txt -r requirements-dev.txt
|
||||||
- name: Syntax Check
|
- name: Run Flake8
|
||||||
run: |
|
run: |
|
||||||
flake8 --version
|
flake8 --version
|
||||||
flake8 novelwriter --count --show-source --statistics
|
flake8 novelwriter --count --show-source --statistics
|
||||||
flake8 tests --count --show-source --statistics --extend-ignore ANN
|
flake8 tests --count --show-source --statistics --extend-ignore ANN
|
||||||
|
- name: Run Pyright
|
||||||
|
run: |
|
||||||
|
pyright --version
|
||||||
|
pyright
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Install Packages (apt)
|
- name: Install Packages (apt)
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install libenchant-2-dev qttools5-dev-tools aspell-en
|
sudo apt install libenchant-2-dev qttools5-dev-tools
|
||||||
- name: Checkout Source
|
- name: Checkout Source
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Dependencies (pip)
|
- name: Install Dependencies (pip)
|
||||||
|
|||||||
@@ -537,12 +537,11 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
|||||||
# XML Helpers
|
# XML Helpers
|
||||||
##
|
##
|
||||||
|
|
||||||
def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
|
def xmlIndent(xml: ET.Element | ET.ElementTree) -> None:
|
||||||
"""A modified version of the XML indent function in the standard
|
"""A modified version of the XML indent function in the standard
|
||||||
library. It behaves more closely to how the one from lxml does.
|
library. It behaves more closely to how the one from lxml does.
|
||||||
"""
|
"""
|
||||||
if isinstance(tree, ET.ElementTree):
|
tree = xml.getroot() if isinstance(xml, ET.ElementTree) else xml
|
||||||
tree = tree.getroot()
|
|
||||||
if not isinstance(tree, ET.Element):
|
if not isinstance(tree, ET.Element):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -43,11 +43,13 @@ from novelwriter.error import logException
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
T_BuildValue = str | int | float | bool
|
||||||
|
|
||||||
# The Settings Template
|
# The Settings Template
|
||||||
# =====================
|
# =====================
|
||||||
# Each entry contains a tuple on the form: (type, default)
|
# Each entry contains a tuple on the form: (type, default)
|
||||||
|
|
||||||
SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
|
SETTINGS_TEMPLATE: dict[str, tuple[type, T_BuildValue]] = {
|
||||||
"filter.includeNovel": (bool, True),
|
"filter.includeNovel": (bool, True),
|
||||||
"filter.includeNotes": (bool, False),
|
"filter.includeNotes": (bool, False),
|
||||||
"filter.includeInactive": (bool, False),
|
"filter.includeInactive": (bool, False),
|
||||||
@@ -378,7 +380,7 @@ class BuildSettings:
|
|||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setValue(self, key: str, value: str | int | float | bool) -> None:
|
def setValue(self, key: str, value: T_BuildValue) -> None:
|
||||||
"""Set a specific value for a build setting."""
|
"""Set a specific value for a build setting."""
|
||||||
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
|
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
|
||||||
self._changed = value != self._settings[key]
|
self._changed = value != self._settings[key]
|
||||||
@@ -502,7 +504,8 @@ class BuildSettings:
|
|||||||
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
||||||
if isinstance(settings, dict):
|
if isinstance(settings, dict):
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
self.setValue(RENAMED.get(key, key), value)
|
if isinstance(key, str) and isinstance(value, T_BuildValue):
|
||||||
|
self.setValue(RENAMED.get(key, key), value)
|
||||||
|
|
||||||
self._changed = False
|
self._changed = False
|
||||||
|
|
||||||
|
|||||||
@@ -146,9 +146,9 @@ class GuiDocMerge(NDialog):
|
|||||||
def _resetList(self) -> None:
|
def _resetList(self) -> None:
|
||||||
"""Reset the content of the list box to its original state."""
|
"""Reset the content of the list box to its original state."""
|
||||||
logger.debug("Resetting list box content")
|
logger.debug("Resetting list box content")
|
||||||
sHandle = self._data.get("sHandle", None)
|
if sHandle := self._data.get("sHandle"):
|
||||||
itemList = self._data.get("origItems", [])
|
itemList = self._data.get("origItems", [])
|
||||||
self._loadContent(sHandle, itemList)
|
self._loadContent(sHandle, itemList)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ class GuiDocSplit(NDialog):
|
|||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _reloadList(self) -> None:
|
def _reloadList(self) -> None:
|
||||||
"""Reload the content of the list box."""
|
"""Reload the content of the list box."""
|
||||||
sHandle = self._data.get("sHandle", None)
|
if sHandle := self._data.get("sHandle"):
|
||||||
self._loadContent(sHandle)
|
self._loadContent(sHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty
|
from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty # pyright: ignore
|
||||||
from PyQt6.QtGui import QEnterEvent, QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
from PyQt6.QtGui import QEnterEvent, QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
||||||
from PyQt6.QtWidgets import QAbstractButton, QWidget
|
from PyQt6.QtWidgets import QAbstractButton, QWidget
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class NSwitch(QAbstractButton):
|
|||||||
def offset(self) -> int: # type: ignore
|
def offset(self) -> int: # type: ignore
|
||||||
return self._offset
|
return self._offset
|
||||||
|
|
||||||
@offset.setter
|
@offset.setter # type: ignore
|
||||||
def offset(self, offset: int) -> None:
|
def offset(self, offset: int) -> None:
|
||||||
self._offset = offset
|
self._offset = offset
|
||||||
self.update()
|
self.update()
|
||||||
|
|||||||
@@ -1034,6 +1034,7 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
def _formatComment(self, style: ComStyle, key: str, text: str) -> tuple[str, T_Formats]:
|
def _formatComment(self, style: ComStyle, key: str, text: str) -> tuple[str, T_Formats]:
|
||||||
"""Apply formatting to comments and notes."""
|
"""Apply formatting to comments and notes."""
|
||||||
|
rFmt = []
|
||||||
tTxt, tFmt = self._extractFormats(text)
|
tTxt, tFmt = self._extractFormats(text)
|
||||||
tFmt.insert(0, (0, TextFmt.COL_B, style.textClass))
|
tFmt.insert(0, (0, TextFmt.COL_B, style.textClass))
|
||||||
tFmt.append((len(tTxt), TextFmt.COL_E, ""))
|
tFmt.append((len(tTxt), TextFmt.COL_E, ""))
|
||||||
|
|||||||
@@ -598,6 +598,7 @@ class ToOdt(Tokenizer):
|
|||||||
def _textStyle(self, hFmt: int, fClass: str = "") -> str:
|
def _textStyle(self, hFmt: int, fClass: str = "") -> str:
|
||||||
"""Return a text style for a given style code."""
|
"""Return a text style for a given style code."""
|
||||||
tKey = str(hFmt)
|
tKey = str(hFmt)
|
||||||
|
color = None
|
||||||
if fClass and (color := self._classes.get(fClass)):
|
if fClass and (color := self._classes.get(fClass)):
|
||||||
tKey = f"{tKey}:{fClass}"
|
tKey = f"{tKey}:{fClass}"
|
||||||
if tKey in self._autoText:
|
if tKey in self._autoText:
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ class TextBlockData(QTextBlockUserData):
|
|||||||
self._text = ""
|
self._text = ""
|
||||||
self._offset = 0
|
self._offset = 0
|
||||||
self._metaData: list[tuple[int, int, str, str]] = []
|
self._metaData: list[tuple[int, int, str, str]] = []
|
||||||
self._spellErrors: list[tuple[int, int,]] = []
|
self._spellErrors: list[tuple[int, int]] = []
|
||||||
return
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -681,9 +681,9 @@ class _OutlineWidget(QWidget):
|
|||||||
hFont.setBold(True)
|
hFont.setBold(True)
|
||||||
hFont.setUnderline(True)
|
hFont.setUnderline(True)
|
||||||
|
|
||||||
|
indent = False
|
||||||
if root := self.listView.invisibleRootItem():
|
if root := self.listView.invisibleRootItem():
|
||||||
parent = root
|
parent = root
|
||||||
indent = False
|
|
||||||
for anchor, entry in data.items():
|
for anchor, entry in data.items():
|
||||||
prefix, _, text = entry.partition("|")
|
prefix, _, text = entry.partition("|")
|
||||||
if prefix in ("TT", "PT", "CH", "SC", "H1", "H2"):
|
if prefix in ("TT", "PT", "CH", "SC", "H1", "H2"):
|
||||||
|
|||||||
@@ -65,6 +65,14 @@ exclude = ["docs/*"]
|
|||||||
max_line_length = 99
|
max_line_length = 99
|
||||||
ignore = ["E133", "E221", "E226", "E228", "E241", "W503"]
|
ignore = ["E133", "E221", "E226", "E228", "E241", "W503"]
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
include = ["novelwriter"]
|
||||||
|
exclude = ["**/__pycache__"]
|
||||||
|
|
||||||
|
reportIncompatibleMethodOverride = false
|
||||||
|
|
||||||
|
pythonVersion = "3.10"
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
branch = false
|
branch = false
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
flake8
|
flake8
|
||||||
|
flake8-annotations
|
||||||
flake8-pep585
|
flake8-pep585
|
||||||
flake8-pyproject
|
flake8-pyproject
|
||||||
flake8-annotations
|
|
||||||
isort
|
isort
|
||||||
|
pyright
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
%%~name: New Scene
|
%%~name: New Scene
|
||||||
%%~path: 000000000000d/000000000000f
|
%%~path: 000000000000d/000000000000f
|
||||||
%%~kind: NOVEL/DOCUMENT
|
%%~kind: NOVEL/DOCUMENT
|
||||||
%%~hash: e4148ea77e78c90c334d5dc46c38a2b7904ac117
|
%%~hash: e3cdc10e73d6250cc4eb9c24fcc4fed1e72392ff
|
||||||
%%~date: 2024-11-01 21:15:57/2024-11-01 21:16:01
|
%%~date: 2025-03-30 23:25:06/2025-03-30 23:25:11
|
||||||
# Novel
|
# Novel
|
||||||
|
|
||||||
## Chapter
|
## Chapter
|
||||||
@@ -60,5 +60,3 @@ But don’t add a double space : See?
|
|||||||
|
|
||||||
>>‘Right-aligned text’
|
>>‘Right-aligned text’
|
||||||
|
|
||||||
Some text with tesst in it.
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.6b1" hexVersion="0x020600b1" fileVersion="1.5" fileRevision="4" timeStamp="2024-11-23 23:35:44">
|
<novelWriterXML appVersion="2.7a3" hexVersion="0x020700a3" fileVersion="1.5" fileRevision="4" timeStamp="2025-03-30 23:23:22">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="4">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="5">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
</project>
|
</project>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="12" novelWords="179" notesWords="27">
|
<content items="12" novelWords="173" notesWords="27">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H1" charCount="1003" wordCount="172" paraCount="17" cursorPos="1259" />
|
<meta expanded="no" heading="H1" charCount="976" wordCount="166" paraCount="16" cursorPos="1243" />
|
||||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from novelwriter.constants import nwKeyWords, nwUnicode
|
|||||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout
|
||||||
from novelwriter.gui.doceditor import GuiDocEditor, _TagAction
|
from novelwriter.gui.doceditor import GuiDocEditor, _TagAction
|
||||||
|
from novelwriter.gui.dochighlight import TextBlockData
|
||||||
from novelwriter.text.counting import standardCounter
|
from novelwriter.text.counting import standardCounter
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
QtAlignJustify, QtAlignLeft, QtKeepAnchor, QtModCtrl, QtModNone,
|
QtAlignJustify, QtAlignLeft, QtKeepAnchor, QtModCtrl, QtModNone,
|
||||||
@@ -58,7 +59,7 @@ def getMenuForPos(editor: GuiDocEditor, pos: int, select: bool = False) -> QMenu
|
|||||||
if select:
|
if select:
|
||||||
cursor.select(QTextCursor.SelectionType.WordUnderCursor)
|
cursor.select(QTextCursor.SelectionType.WordUnderCursor)
|
||||||
editor.setTextCursor(cursor)
|
editor.setTextCursor(cursor)
|
||||||
editor._openContextMenu(editor.cursorRect().center())
|
editor._openContextFromCursor()
|
||||||
for obj in editor.children():
|
for obj in editor.children():
|
||||||
if isinstance(obj, QMenu) and obj.objectName() == "ContextMenu":
|
if isinstance(obj, QMenu) and obj.objectName() == "ContextMenu":
|
||||||
return obj
|
return obj
|
||||||
@@ -514,9 +515,20 @@ def testGuiEditor_SpellChecking(qtbot, monkeypatch, nwGUI, projPath, ipsumText,
|
|||||||
# ==============
|
# ==============
|
||||||
SHARED.project.data.setSpellCheck(True)
|
SHARED.project.data.setSpellCheck(True)
|
||||||
|
|
||||||
|
cursor = docEditor.textCursor()
|
||||||
|
cursor.setPosition(16)
|
||||||
|
data = cursor.block().userData()
|
||||||
|
assert cursor.block().text().startswith("Lorem")
|
||||||
|
assert isinstance(data, TextBlockData)
|
||||||
|
data._spellErrors = [(0, 5)]
|
||||||
|
|
||||||
|
# No known position
|
||||||
|
assert docEditor._qDocument.spellErrorAtPos(-1) == ("", -1, -1, [])
|
||||||
|
|
||||||
# With Suggestion
|
# With Suggestion
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(docEditor._qDocument, "spellErrorAtPos", lambda *a: ("Lorem", 0, 5, ["Lorax"]))
|
mp.setattr(SHARED.spelling, "suggestWords", lambda *a: ["Lorax"])
|
||||||
|
|
||||||
ctxMenu = getMenuForPos(docEditor, 16)
|
ctxMenu = getMenuForPos(docEditor, 16)
|
||||||
assert ctxMenu is not None
|
assert ctxMenu is not None
|
||||||
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
||||||
@@ -530,7 +542,8 @@ def testGuiEditor_SpellChecking(qtbot, monkeypatch, nwGUI, projPath, ipsumText,
|
|||||||
|
|
||||||
# Without Suggestion
|
# Without Suggestion
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(docEditor._qDocument, "spellErrorAtPos", lambda *a: ("Lorax", 0, 5, []))
|
mp.setattr(SHARED.spelling, "suggestWords", lambda *a: [])
|
||||||
|
|
||||||
ctxMenu = getMenuForPos(docEditor, 16)
|
ctxMenu = getMenuForPos(docEditor, 16)
|
||||||
assert ctxMenu is not None
|
assert ctxMenu is not None
|
||||||
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
||||||
@@ -541,7 +554,8 @@ def testGuiEditor_SpellChecking(qtbot, monkeypatch, nwGUI, projPath, ipsumText,
|
|||||||
|
|
||||||
# Add to Dictionary
|
# Add to Dictionary
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(docEditor._qDocument, "spellErrorAtPos", lambda *a: ("Lorax", 0, 5, []))
|
mp.setattr(SHARED.spelling, "suggestWords", lambda *a: [])
|
||||||
|
|
||||||
ctxMenu = getMenuForPos(docEditor, 16)
|
ctxMenu = getMenuForPos(docEditor, 16)
|
||||||
assert ctxMenu is not None
|
assert ctxMenu is not None
|
||||||
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
actions = [x.text() for x in ctxMenu.actions() if x.text()]
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
@@ -30,7 +29,7 @@ import pytest
|
|||||||
|
|
||||||
from PyQt6.QtCore import Qt
|
from PyQt6.QtCore import Qt
|
||||||
from PyQt6.QtGui import QPalette
|
from PyQt6.QtGui import QPalette
|
||||||
from PyQt6.QtWidgets import QInputDialog, QMenu, QMessageBox
|
from PyQt6.QtWidgets import QInputDialog, QMessageBox
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
@@ -566,32 +565,6 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
|
|
||||||
docEditor._wCounterDoc.run()
|
docEditor._wCounterDoc.run()
|
||||||
|
|
||||||
# Spell Checking
|
|
||||||
# ==============
|
|
||||||
|
|
||||||
for c in "Some text with tesst in it.":
|
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
|
||||||
|
|
||||||
currPos = docEditor.getCursorPosition()
|
|
||||||
assert docEditor._qDocument.spellErrorAtPos(currPos) == ("", -1, -1, [])
|
|
||||||
|
|
||||||
errPos = currPos - 13
|
|
||||||
if not sys.platform.startswith("win32"):
|
|
||||||
# Skip on Windows as spell checking is off there
|
|
||||||
# This check will fail without an 'en' dictionary, like aspell-en
|
|
||||||
word, cPos, cLen, suggest = docEditor._qDocument.spellErrorAtPos(errPos)
|
|
||||||
assert word == "tesst"
|
|
||||||
assert cPos == 15
|
|
||||||
assert cLen == 5
|
|
||||||
assert "test" in suggest
|
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
|
||||||
mp.setattr(QMenu, "exec", lambda *a: None)
|
|
||||||
docEditor.setCursorPosition(errPos)
|
|
||||||
docEditor._openContextFromCursor()
|
|
||||||
|
|
||||||
# Check Files
|
# Check Files
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user