@@ -1,3 +1,4 @@
|
|||||||
include LICENSE.md
|
include LICENSE.md
|
||||||
|
recursive-include nw/assets *
|
||||||
recursive-include nw/graphics *
|
recursive-include nw/graphics *
|
||||||
recursive-include nw/themes *
|
recursive-include nw/themes *
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ For the apt package manager on Debian systems, the following Python3 packages ar
|
|||||||
|
|
||||||
These are optional, but recommended:
|
These are optional, but recommended:
|
||||||
|
|
||||||
* `python3-enchant` for spell checking
|
* `python3-enchant` for better spell checking
|
||||||
* `python3-pycountry` for translating language codes to language names
|
|
||||||
* `python3-latexcodec` for escaping unicode characters in LaTeX export
|
* `python3-latexcodec` for escaping unicode characters in LaTeX export
|
||||||
* `python3-pandoc` for additional exports to Word, Open Office, eBooks, etc.
|
* `python3-pandoc` for additional exports to Word, Open Office, eBooks, etc.
|
||||||
|
|
||||||
@@ -64,12 +63,14 @@ python3 -m pip install pyqt5
|
|||||||
python3 -m pip install appdirs
|
python3 -m pip install appdirs
|
||||||
python3 -m pip install lxml
|
python3 -m pip install lxml
|
||||||
python3 -m pip install pyenchant
|
python3 -m pip install pyenchant
|
||||||
python3 -m pip install pycountry
|
|
||||||
python3 -m pip install latexcodec
|
python3 -m pip install latexcodec
|
||||||
python3 -m pip install pypandoc
|
python3 -m pip install pypandoc
|
||||||
```
|
```
|
||||||
On Windows,`pyenchant` may cause problems.
|
On Windows,`pyenchant` may cause problems.
|
||||||
I'm looking for a good alternative for spell checking.
|
If no external spell checking tool is installed, novelWriter will use a basic spell checker based on standard Python package `difflib`.
|
||||||
|
Currently, only English dictionaries are available, but more can be added to the `nw/assets/dict` folder.
|
||||||
|
See the RADME.md file in that folder for how to generate more dictionaries.
|
||||||
|
This option is both slow and limited.
|
||||||
|
|
||||||
Note: On Windows, make sure Python3 is in your PATH if you want to launch novelWriter from command line.
|
Note: On Windows, make sure Python3 is in your PATH if you want to launch novelWriter from command line.
|
||||||
You can also right click the `novelWriter.py` file, create a shortcut, then right click again, select "Properties" and change the target to your python executable and `novelWriter.py`.
|
You can also right click the `novelWriter.py` file, create a shortcut, then right click again, select "Properties" and change the target to your python executable and `novelWriter.py`.
|
||||||
|
|||||||
+6
-17
@@ -5,7 +5,7 @@ import sys
|
|||||||
|
|
||||||
if sys.hexversion < 0x030500F0:
|
if sys.hexversion < 0x030500F0:
|
||||||
print("ERROR: At least Python 3.5 is required")
|
print("ERROR: At least Python 3.5 is required")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt5.QtWidgets
|
import PyQt5.QtWidgets
|
||||||
@@ -13,37 +13,26 @@ try:
|
|||||||
import PyQt5.QtCore
|
import PyQt5.QtCore
|
||||||
except:
|
except:
|
||||||
print("ERROR: Failed to load dependency python3-pyqt5")
|
print("ERROR: Failed to load dependency python3-pyqt5")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt5.QtSvg
|
import PyQt5.QtSvg
|
||||||
except:
|
except:
|
||||||
print("ERROR: Failed to load dependency python3-pyqt5.qtsvg")
|
print("ERROR: Failed to load dependency python3-pyqt5.qtsvg")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import lxml
|
import lxml
|
||||||
except:
|
except:
|
||||||
print("ERROR: Failed to load dependency python3-lxml")
|
print("ERROR: Failed to load dependency python3-lxml")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import appdirs
|
import appdirs
|
||||||
except:
|
except:
|
||||||
print("ERROR: Failed to load dependency python3-appdirs")
|
print("ERROR: Failed to load dependency python3-appdirs")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
spellPack = None
|
|
||||||
try:
|
|
||||||
import enchant
|
|
||||||
spellPack = "enchant"
|
|
||||||
except:
|
|
||||||
print("WARNING: No spell check library found.")
|
|
||||||
print("Please install python3-enchant if you want to use spell checking")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import nw
|
import nw
|
||||||
inArgs = sys.argv[1:]
|
nw.main(sys.argv[1:])
|
||||||
if spellPack is not None:
|
|
||||||
inArgs.append("--spell=%s" % spellPack)
|
|
||||||
nw.main(inArgs)
|
|
||||||
|
|||||||
+1
-6
@@ -89,7 +89,6 @@ def main(sysArgs=None):
|
|||||||
"version",
|
"version",
|
||||||
"config=",
|
"config=",
|
||||||
"testmode",
|
"testmode",
|
||||||
"spell=",
|
|
||||||
"style=",
|
"style=",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ def main(sysArgs=None):
|
|||||||
" -q, --quiet Disable output to command line. Does not affect log file.\n"
|
" -q, --quiet Disable output to command line. Does not affect log file.\n"
|
||||||
" -t, --time Shows time stamp in logging output.\n"
|
" -t, --time Shows time stamp in logging output.\n"
|
||||||
" -l, --logfile= Specify log file.\n"
|
" -l, --logfile= Specify log file.\n"
|
||||||
" --style= Set Qt5 style flag. Defaults to Fusion.\n"
|
" --style= Set Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||||
" --config= Alternative config file.\n"
|
" --config= Alternative config file.\n"
|
||||||
" --headless Do not display GUI. Useful for testing scripts.\n"
|
" --headless Do not display GUI. Useful for testing scripts.\n"
|
||||||
).format(
|
).format(
|
||||||
@@ -126,7 +125,6 @@ def main(sysArgs=None):
|
|||||||
showTime = False
|
showTime = False
|
||||||
confPath = None
|
confPath = None
|
||||||
testMode = False
|
testMode = False
|
||||||
spellTool = None
|
|
||||||
qtStyle = "Fusion"
|
qtStyle = "Fusion"
|
||||||
|
|
||||||
# Parse Options
|
# Parse Options
|
||||||
@@ -163,13 +161,10 @@ def main(sysArgs=None):
|
|||||||
confPath = inArg
|
confPath = inArg
|
||||||
elif inOpt in ("--testmode"):
|
elif inOpt in ("--testmode"):
|
||||||
testMode = True
|
testMode = True
|
||||||
elif inOpt in ("--spell"):
|
|
||||||
spellTool = inArg
|
|
||||||
|
|
||||||
# Set Config Options
|
# Set Config Options
|
||||||
CONFIG.showGUI = not testMode
|
CONFIG.showGUI = not testMode
|
||||||
CONFIG.debugInfo = debugLevel < logging.INFO
|
CONFIG.debugInfo = debugLevel < logging.INFO
|
||||||
CONFIG.spellTool = spellTool
|
|
||||||
|
|
||||||
# Set Logging
|
# Set Logging
|
||||||
if showTime: debugStr = timeStr+debugStr
|
if showTime: debugStr = timeStr+debugStr
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
## Dictionary Conversion
|
||||||
|
|
||||||
|
Source: https://ftp.gnu.org/gnu/aspell/dict/0index.html
|
||||||
|
|
||||||
|
```
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
|
||||||
|
aspell --dict-dir=. -d en dump master > en.dict
|
||||||
|
aspell --dict-dir=. -d en_GB-ise-w_accents dump master > en_GB.dict
|
||||||
|
aspell --dict-dir=. -d en_US-w_accents dump master > en_US.dict
|
||||||
|
```
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
This English word list is comes directly from SCOWL 2019.10.06 (up to level 60,
|
||||||
|
using the speller/make-aspell-dict script, http://wordlist.sourceforge.net/)
|
||||||
|
and is thus under the same copyright of SCOWL. The affix file (only
|
||||||
|
included in the aspell6 package) is based on the Ispell one which is
|
||||||
|
under the same copyright of Ispell. Part of SCOWL is also based on
|
||||||
|
Ispell thus the Ispell copyright is included with the SCOWL copyright.
|
||||||
|
The collective work is Copyright 2000-2018 by Kevin Atkinson as well
|
||||||
|
as any of the copyrights mentioned below:
|
||||||
|
|
||||||
|
Copyright 2000-2018 by Kevin Atkinson
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute and sell these word
|
||||||
|
lists, the associated scripts, the output created from the scripts,
|
||||||
|
and its documentation for any purpose is hereby granted without fee,
|
||||||
|
provided that the above copyright notice appears in all copies and
|
||||||
|
that both that copyright notice and this permission notice appear in
|
||||||
|
supporting documentation. Kevin Atkinson makes no representations
|
||||||
|
about the suitability of this array for any purpose. It is provided
|
||||||
|
"as is" without express or implied warranty.
|
||||||
|
|
||||||
|
Alan Beale <biljir@pobox.com> also deserves special credit as he has,
|
||||||
|
in addition to providing the 12Dicts package and being a major
|
||||||
|
contributor to the ENABLE word list, given me an incredible amount of
|
||||||
|
feedback and created a number of special lists (those found in the
|
||||||
|
Supplement) in order to help improve the overall quality of SCOWL.
|
||||||
|
|
||||||
|
The 10 level includes the 1000 most common English words (according to
|
||||||
|
the Moby (TM) Words II [MWords] package), a subset of the 1000 most
|
||||||
|
common words on the Internet (again, according to Moby Words II), and
|
||||||
|
frequently class 16 from Brian Kelk's "UK English Wordlist
|
||||||
|
with Frequency Classification".
|
||||||
|
|
||||||
|
The MWords package was explicitly placed in the public domain:
|
||||||
|
|
||||||
|
The Moby lexicon project is complete and has
|
||||||
|
been place into the public domain. Use, sell,
|
||||||
|
rework, excerpt and use in any way on any platform.
|
||||||
|
|
||||||
|
Placing this material on internal or public servers is
|
||||||
|
also encouraged. The compiler is not aware of any
|
||||||
|
export restrictions so freely distribute world-wide.
|
||||||
|
|
||||||
|
You can verify the public domain status by contacting
|
||||||
|
|
||||||
|
Grady Ward
|
||||||
|
3449 Martha Ct.
|
||||||
|
Arcata, CA 95521-4884
|
||||||
|
|
||||||
|
grady@netcom.com
|
||||||
|
grady@northcoast.com
|
||||||
|
|
||||||
|
The "UK English Wordlist With Frequency Classification" is also in the
|
||||||
|
Public Domain:
|
||||||
|
|
||||||
|
Date: Sat, 08 Jul 2000 20:27:21 +0100
|
||||||
|
From: Brian Kelk <Brian.Kelk@cl.cam.ac.uk>
|
||||||
|
|
||||||
|
> I was wondering what the copyright status of your "UK English
|
||||||
|
> Wordlist With Frequency Classification" word list as it seems to
|
||||||
|
> be lacking any copyright notice.
|
||||||
|
|
||||||
|
There were many many sources in total, but any text marked
|
||||||
|
"copyright" was avoided. Locally-written documentation was one
|
||||||
|
source. An earlier version of the list resided in a filespace called
|
||||||
|
PUBLIC on the University mainframe, because it was considered public
|
||||||
|
domain.
|
||||||
|
|
||||||
|
Date: Tue, 11 Jul 2000 19:31:34 +0100
|
||||||
|
|
||||||
|
> So are you saying your word list is also in the public domain?
|
||||||
|
|
||||||
|
That is the intention.
|
||||||
|
|
||||||
|
The 20 level includes frequency classes 7-15 from Brian's word list.
|
||||||
|
|
||||||
|
The 35 level includes frequency classes 2-6 and words appearing in at
|
||||||
|
least 11 of 12 dictionaries as indicated in the 12Dicts package. All
|
||||||
|
words from the 12Dicts package have had likely inflections added via
|
||||||
|
my inflection database.
|
||||||
|
|
||||||
|
The 12Dicts package and Supplement is in the Public Domain.
|
||||||
|
|
||||||
|
The WordNet database, which was used in the creation of the
|
||||||
|
Inflections database, is under the following copyright:
|
||||||
|
|
||||||
|
This software and database is being provided to you, the LICENSEE,
|
||||||
|
by Princeton University under the following license. By obtaining,
|
||||||
|
using and/or copying this software and database, you agree that you
|
||||||
|
have read, understood, and will comply with these terms and
|
||||||
|
conditions.:
|
||||||
|
|
||||||
|
Permission to use, copy, modify and distribute this software and
|
||||||
|
database and its documentation for any purpose and without fee or
|
||||||
|
royalty is hereby granted, provided that you agree to comply with
|
||||||
|
the following copyright notice and statements, including the
|
||||||
|
disclaimer, and that the same appear on ALL copies of the software,
|
||||||
|
database and documentation, including modifications that you make
|
||||||
|
for internal use or for distribution.
|
||||||
|
|
||||||
|
WordNet 1.6 Copyright 1997 by Princeton University. All rights
|
||||||
|
reserved.
|
||||||
|
|
||||||
|
THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON
|
||||||
|
UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON
|
||||||
|
UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT-
|
||||||
|
ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
|
||||||
|
LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||||
|
THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
|
||||||
|
|
||||||
|
The name of Princeton University or Princeton may not be used in
|
||||||
|
advertising or publicity pertaining to distribution of the software
|
||||||
|
and/or database. Title to copyright in this software, database and
|
||||||
|
any associated documentation shall at all times remain with
|
||||||
|
Princeton University and LICENSEE agrees to preserve same.
|
||||||
|
|
||||||
|
The 40 level includes words from Alan's 3esl list found in version 4.0
|
||||||
|
of his 12dicts package. Like his other stuff the 3esl list is also in the
|
||||||
|
public domain.
|
||||||
|
|
||||||
|
The 50 level includes Brian's frequency class 1, words appearing
|
||||||
|
in at least 5 of 12 of the dictionaries as indicated in the 12Dicts
|
||||||
|
package, and uppercase words in at least 4 of the previous 12
|
||||||
|
dictionaries. A decent number of proper names is also included: The
|
||||||
|
top 1000 male, female, and Last names from the 1990 Census report; a
|
||||||
|
list of names sent to me by Alan Beale; and a few names that I added
|
||||||
|
myself. Finally a small list of abbreviations not commonly found in
|
||||||
|
other word lists is included.
|
||||||
|
|
||||||
|
The name files form the Census report is a government document which I
|
||||||
|
don't think can be copyrighted.
|
||||||
|
|
||||||
|
The file special-jargon.50 uses common.lst and word.lst from the
|
||||||
|
"Unofficial Jargon File Word Lists" which is derived from "The Jargon
|
||||||
|
File". All of which is in the Public Domain. This file also contain
|
||||||
|
a few extra UNIX terms which are found in the file "unix-terms" in the
|
||||||
|
special/ directory.
|
||||||
|
|
||||||
|
The 55 level includes words from Alan's 2of4brif list found in version
|
||||||
|
4.0 of his 12dicts package. Like his other stuff the 2of4brif is also
|
||||||
|
in the public domain.
|
||||||
|
|
||||||
|
The 60 level includes all words appearing in at least 2 of the 12
|
||||||
|
dictionaries as indicated by the 12Dicts package.
|
||||||
|
|
||||||
|
The 70 level includes Brian's frequency class 0 and the 74,550 common
|
||||||
|
dictionary words from the MWords package. The common dictionary words,
|
||||||
|
like those from the 12Dicts package, have had all likely inflections
|
||||||
|
added. The 70 level also included the 5desk list from version 4.0 of
|
||||||
|
the 12Dics package which is in the public domain.
|
||||||
|
|
||||||
|
The 80 level includes the ENABLE word list, all the lists in the
|
||||||
|
ENABLE supplement package (except for ABLE), the "UK Advanced Cryptics
|
||||||
|
Dictionary" (UKACD), the list of signature words from the YAWL package,
|
||||||
|
and the 10,196 places list from the MWords package.
|
||||||
|
|
||||||
|
The ENABLE package, mainted by M\Cooper <thegrendel@theriver.com>,
|
||||||
|
is in the Public Domain:
|
||||||
|
|
||||||
|
The ENABLE master word list, WORD.LST, is herewith formally released
|
||||||
|
into the Public Domain. Anyone is free to use it or distribute it in
|
||||||
|
any manner they see fit. No fee or registration is required for its
|
||||||
|
use nor are "contributions" solicited (if you feel you absolutely
|
||||||
|
must contribute something for your own peace of mind, the authors of
|
||||||
|
the ENABLE list ask that you make a donation on their behalf to your
|
||||||
|
favorite charity). This word list is our gift to the Scrabble
|
||||||
|
community, as an alternate to "official" word lists. Game designers
|
||||||
|
may feel free to incorporate the WORD.LST into their games. Please
|
||||||
|
mention the source and credit us as originators of the list. Note
|
||||||
|
that if you, as a game designer, use the WORD.LST in your product,
|
||||||
|
you may still copyright and protect your product, but you may *not*
|
||||||
|
legally copyright or in any way restrict redistribution of the
|
||||||
|
WORD.LST portion of your product. This *may* under law restrict your
|
||||||
|
rights to restrict your users' rights, but that is only fair.
|
||||||
|
|
||||||
|
UKACD, by J Ross Beresford <ross@bryson.demon.co.uk>, is under the
|
||||||
|
following copyright:
|
||||||
|
|
||||||
|
Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved.
|
||||||
|
|
||||||
|
The following restriction is placed on the use of this publication:
|
||||||
|
if The UK Advanced Cryptics Dictionary is used in a software package
|
||||||
|
or redistributed in any form, the copyright notice must be
|
||||||
|
prominently displayed and the text of this document must be included
|
||||||
|
verbatim.
|
||||||
|
|
||||||
|
There are no other restrictions: I would like to see the list
|
||||||
|
distributed as widely as possible.
|
||||||
|
|
||||||
|
The 95 level includes the 354,984 single words, 256,772 compound
|
||||||
|
words, 4,946 female names and the 3,897 male names, and 21,986 names
|
||||||
|
from the MWords package, ABLE.LST from the ENABLE Supplement, and some
|
||||||
|
additional words found in my part-of-speech database that were not
|
||||||
|
found anywhere else.
|
||||||
|
|
||||||
|
Accent information was taken from UKACD.
|
||||||
|
|
||||||
|
The VarCon package was used to create the American, British, Canadian,
|
||||||
|
and Australian word list. It is under the following copyright:
|
||||||
|
|
||||||
|
Copyright 2000-2016 by Kevin Atkinson
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute and sell this array, the
|
||||||
|
associated software, and its documentation for any purpose is hereby
|
||||||
|
granted without fee, provided that the above copyright notice appears
|
||||||
|
in all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation. Kevin Atkinson makes no
|
||||||
|
representations about the suitability of this array for any
|
||||||
|
purpose. It is provided "as is" without express or implied warranty.
|
||||||
|
|
||||||
|
Copyright 2016 by Benjamin Titze
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute and sell this array, the
|
||||||
|
associated software, and its documentation for any purpose is hereby
|
||||||
|
granted without fee, provided that the above copyright notice appears
|
||||||
|
in all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation. Benjamin Titze makes no
|
||||||
|
representations about the suitability of this array for any
|
||||||
|
purpose. It is provided "as is" without express or implied warranty.
|
||||||
|
|
||||||
|
Since the original words lists come from the Ispell distribution:
|
||||||
|
|
||||||
|
Copyright 1993, Geoff Kuenning, Granada Hills, CA
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. All modifications to the source code must be clearly marked as
|
||||||
|
such. Binary redistributions based on modified source code
|
||||||
|
must be clearly marked as modified versions in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
(clause 4 removed with permission from Geoff Kuenning)
|
||||||
|
5. The name of Geoff Kuenning may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
+127250
File diff suppressed because it is too large
Load Diff
+123295
File diff suppressed because it is too large
Load Diff
+123592
File diff suppressed because it is too large
Load Diff
+40
-13
@@ -41,7 +41,6 @@ class Config:
|
|||||||
self.appHandle = nw.__package__.lower()
|
self.appHandle = nw.__package__.lower()
|
||||||
self.showGUI = True
|
self.showGUI = True
|
||||||
self.debugInfo = False
|
self.debugInfo = False
|
||||||
self.spellTool = None
|
|
||||||
|
|
||||||
# Set Paths
|
# Set Paths
|
||||||
self.confPath = None
|
self.confPath = None
|
||||||
@@ -51,9 +50,10 @@ class Config:
|
|||||||
self.appPath = None
|
self.appPath = None
|
||||||
self.appRoot = None
|
self.appRoot = None
|
||||||
self.appIcon = None
|
self.appIcon = None
|
||||||
self.guiPath = None
|
self.assetPath = None
|
||||||
self.themeRoot = None
|
self.themeRoot = None
|
||||||
self.themePath = None
|
self.graphPath = None
|
||||||
|
self.dictPath = None
|
||||||
|
|
||||||
# Set default values
|
# Set default values
|
||||||
self.confChanged = False
|
self.confChanged = False
|
||||||
@@ -90,10 +90,12 @@ class Config:
|
|||||||
self.showTabsNSpaces = False
|
self.showTabsNSpaces = False
|
||||||
self.showLineEndings = False
|
self.showLineEndings = False
|
||||||
|
|
||||||
|
self.fmtApostrophe = nwUnicode.U_RSQUO
|
||||||
self.fmtSingleQuotes = [nwUnicode.U_LSQUO,nwUnicode.U_RSQUO]
|
self.fmtSingleQuotes = [nwUnicode.U_LSQUO,nwUnicode.U_RSQUO]
|
||||||
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO,nwUnicode.U_RDQUO]
|
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO,nwUnicode.U_RDQUO]
|
||||||
|
|
||||||
self.spellLanguage = "en_GB"
|
self.spellTool = None
|
||||||
|
self.spellLanguage = None
|
||||||
|
|
||||||
## Backup
|
## Backup
|
||||||
self.backupPath = ""
|
self.backupPath = ""
|
||||||
@@ -136,9 +138,9 @@ class Config:
|
|||||||
self.osDarwin = False
|
self.osDarwin = False
|
||||||
self.osUnknown = False
|
self.osUnknown = False
|
||||||
if self.osType.startswith("linux"):
|
if self.osType.startswith("linux"):
|
||||||
self.osLinux = True
|
self.osLinux = True
|
||||||
elif self.osType.startswith("darwin"):
|
elif self.osType.startswith("darwin"):
|
||||||
self.osDarwin = True
|
self.osDarwin = True
|
||||||
elif self.osType.startswith("win32"):
|
elif self.osType.startswith("win32"):
|
||||||
self.osWindows = True
|
self.osWindows = True
|
||||||
elif self.osType.startswith("cygwin"):
|
elif self.osType.startswith("cygwin"):
|
||||||
@@ -146,6 +148,10 @@ class Config:
|
|||||||
else:
|
else:
|
||||||
self.osUnknown = True
|
self.osUnknown = True
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
self.hasEnchant = False
|
||||||
|
self.hasSymSpell = False
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -165,10 +171,10 @@ class Config:
|
|||||||
self.lastPath = self.homePath
|
self.lastPath = self.homePath
|
||||||
self.appPath = getattr(sys, "_MEIPASS", path.abspath(path.dirname(__file__)))
|
self.appPath = getattr(sys, "_MEIPASS", path.abspath(path.dirname(__file__)))
|
||||||
self.appRoot = path.join(self.appPath,path.pardir)
|
self.appRoot = path.join(self.appPath,path.pardir)
|
||||||
self.helpPath = path.join(self.appRoot,"help","en_GB")
|
self.assetPath = path.join(self.appPath,"assets")
|
||||||
self.guiPath = path.join(self.appPath,"gui")
|
|
||||||
self.themeRoot = path.join(self.appPath,"themes")
|
self.themeRoot = path.join(self.appPath,"themes")
|
||||||
self.graphPath = path.join(self.appPath,"graphics")
|
self.graphPath = path.join(self.appPath,"graphics")
|
||||||
|
self.dictPath = path.join(self.assetPath,"dict")
|
||||||
self.appIcon = path.join(self.graphPath, nwFiles.APP_ICON)
|
self.appIcon = path.join(self.graphPath, nwFiles.APP_ICON)
|
||||||
|
|
||||||
# If config folder does not exist, make it.
|
# If config folder does not exist, make it.
|
||||||
@@ -185,13 +191,16 @@ class Config:
|
|||||||
# If it exists, load it
|
# If it exists, load it
|
||||||
self.loadConfig()
|
self.loadConfig()
|
||||||
else:
|
else:
|
||||||
# If it does not exist, save a copy of the defaults
|
# If it does not exist, save a copy of the default values
|
||||||
self.saveConfig()
|
self.saveConfig()
|
||||||
|
|
||||||
|
# Check the availability of optional packages
|
||||||
|
self._checkOptionalPackages()
|
||||||
|
|
||||||
if self.spellTool is None:
|
if self.spellTool is None:
|
||||||
logger.warning("No spell check tool available")
|
self.spellTool = "internal"
|
||||||
else:
|
if self.spellLanguage is None:
|
||||||
logger.debug("Using spell check tool '%s'" % self.spellTool)
|
self.spellLanguage = "en"
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -287,8 +296,11 @@ class Config:
|
|||||||
self.fmtDoubleQuotes = self._parseLine(
|
self.fmtDoubleQuotes = self._parseLine(
|
||||||
cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes
|
cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes
|
||||||
)
|
)
|
||||||
|
self.spellTool = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
|
||||||
|
)
|
||||||
self.spellLanguage = self._parseLine(
|
self.spellLanguage = self._parseLine(
|
||||||
cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage
|
cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage
|
||||||
)
|
)
|
||||||
self.showTabsNSpaces = self._parseLine(
|
self.showTabsNSpaces = self._parseLine(
|
||||||
cnfParse, cnfSec, "showtabsnspaces", self.CNF_BOOL, self.showTabsNSpaces
|
cnfParse, cnfSec, "showtabsnspaces", self.CNF_BOOL, self.showTabsNSpaces
|
||||||
@@ -379,6 +391,7 @@ class Config:
|
|||||||
cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots))
|
cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots))
|
||||||
cnfParse.set(cnfSec,"fmtsinglequote", self._packList(self.fmtSingleQuotes))
|
cnfParse.set(cnfSec,"fmtsinglequote", self._packList(self.fmtSingleQuotes))
|
||||||
cnfParse.set(cnfSec,"fmtdoublequote", self._packList(self.fmtDoubleQuotes))
|
cnfParse.set(cnfSec,"fmtdoublequote", self._packList(self.fmtDoubleQuotes))
|
||||||
|
cnfParse.set(cnfSec,"spelltool", str(self.spellTool))
|
||||||
cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage))
|
cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage))
|
||||||
cnfParse.set(cnfSec,"showtabsnspaces", str(self.showTabsNSpaces))
|
cnfParse.set(cnfSec,"showtabsnspaces", str(self.showTabsNSpaces))
|
||||||
cnfParse.set(cnfSec,"showlineendings", str(self.showLineEndings))
|
cnfParse.set(cnfSec,"showlineendings", str(self.showLineEndings))
|
||||||
@@ -519,4 +532,18 @@ class Config:
|
|||||||
return None
|
return None
|
||||||
return checkVal
|
return checkVal
|
||||||
|
|
||||||
|
def _checkOptionalPackages(self):
|
||||||
|
"""Cheks if we have the optional packages used by some features.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import enchant
|
||||||
|
self.hasEnchant = True
|
||||||
|
logger.debug("Checking package pyenchant: Ok")
|
||||||
|
except:
|
||||||
|
self.hasEnchant = False
|
||||||
|
logger.debug("Checking package pyenchant: Missing")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# End Class Config
|
# End Class Config
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import nw
|
|||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
|
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
|
||||||
|
)
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit,
|
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit,
|
||||||
QLabel, QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox,
|
QLabel, QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox,
|
||||||
@@ -25,6 +27,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QFileDialog
|
QFileDialog
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from nw.tools import NWSpellCheck, NWSpellSimple, NWSpellEnchant
|
||||||
from nw.constants import nwAlert, nwQuotes
|
from nw.constants import nwAlert, nwQuotes
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -156,14 +159,27 @@ class GuiConfigEditGeneral(QWidget):
|
|||||||
self.spellLang.setLayout(self.spellLangForm)
|
self.spellLang.setLayout(self.spellLangForm)
|
||||||
|
|
||||||
self.spellLangList = QComboBox(self)
|
self.spellLangList = QComboBox(self)
|
||||||
for spTag, spName in self.theParent.docEditor.theDict.listDictionaries():
|
self.spellToolList = QComboBox(self)
|
||||||
self.spellLangList.addItem(spName, spTag)
|
self.spellToolList.addItem("Internal (difflib)", NWSpellCheck.SP_INTERNAL)
|
||||||
spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
|
self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT)
|
||||||
if spellIdx != -1:
|
self.spellToolList.addItem("SymSpell (symspellpy)", NWSpellCheck.SP_SYMSPELL)
|
||||||
self.spellLangList.setCurrentIndex(spellIdx)
|
|
||||||
|
|
||||||
self.spellLangForm.addWidget(QLabel("Language"), 0, 0)
|
theModel = self.spellToolList.model()
|
||||||
self.spellLangForm.addWidget(self.spellLangList, 0, 1)
|
idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT)
|
||||||
|
idSymSpell = self.spellToolList.findData(NWSpellCheck.SP_SYMSPELL)
|
||||||
|
theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant)
|
||||||
|
theModel.item(idSymSpell).setEnabled(self.mainConf.hasSymSpell)
|
||||||
|
|
||||||
|
self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool)
|
||||||
|
toolIdx = self.spellToolList.findData(self.mainConf.spellTool)
|
||||||
|
if toolIdx != -1:
|
||||||
|
self.spellToolList.setCurrentIndex(toolIdx)
|
||||||
|
self._doUpdateSpellTool(0)
|
||||||
|
|
||||||
|
self.spellLangForm.addWidget(QLabel("Provider"), 0, 0)
|
||||||
|
self.spellLangForm.addWidget(self.spellToolList, 0, 1)
|
||||||
|
self.spellLangForm.addWidget(QLabel("Language"), 1, 0)
|
||||||
|
self.spellLangForm.addWidget(self.spellLangList, 1, 1)
|
||||||
self.spellLangForm.setColumnStretch(2, 1)
|
self.spellLangForm.setColumnStretch(2, 1)
|
||||||
|
|
||||||
# AutoSave
|
# AutoSave
|
||||||
@@ -227,8 +243,8 @@ class GuiConfigEditGeneral(QWidget):
|
|||||||
self.outerBox.addWidget(self.guiLook, 0, 0)
|
self.outerBox.addWidget(self.guiLook, 0, 0)
|
||||||
self.outerBox.addWidget(self.spellLang, 1, 0)
|
self.outerBox.addWidget(self.spellLang, 1, 0)
|
||||||
self.outerBox.addWidget(self.autoSave, 2, 0)
|
self.outerBox.addWidget(self.autoSave, 2, 0)
|
||||||
self.outerBox.addWidget(self.projBackup, 3, 0)
|
self.outerBox.addWidget(self.projBackup, 3, 0, 1, 2)
|
||||||
self.outerBox.setColumnStretch(2, 1)
|
self.outerBox.setColumnStretch(1, 1)
|
||||||
self.outerBox.setRowStretch(4, 1)
|
self.outerBox.setRowStretch(4, 1)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
@@ -241,6 +257,7 @@ class GuiConfigEditGeneral(QWidget):
|
|||||||
|
|
||||||
guiTheme = self.guiLookTheme.currentData()
|
guiTheme = self.guiLookTheme.currentData()
|
||||||
guiSyntax = self.guiLookSyntax.currentData()
|
guiSyntax = self.guiLookSyntax.currentData()
|
||||||
|
spellTool = self.spellToolList.currentData()
|
||||||
spellLanguage = self.spellLangList.currentData()
|
spellLanguage = self.spellLangList.currentData()
|
||||||
autoSaveDoc = self.autoSaveDoc.value()
|
autoSaveDoc = self.autoSaveDoc.value()
|
||||||
autoSaveProj = self.autoSaveProj.value()
|
autoSaveProj = self.autoSaveProj.value()
|
||||||
@@ -253,6 +270,7 @@ class GuiConfigEditGeneral(QWidget):
|
|||||||
|
|
||||||
self.mainConf.guiTheme = guiTheme
|
self.mainConf.guiTheme = guiTheme
|
||||||
self.mainConf.guiSyntax = guiSyntax
|
self.mainConf.guiSyntax = guiSyntax
|
||||||
|
self.mainConf.spellTool = spellTool
|
||||||
self.mainConf.spellLanguage = spellLanguage
|
self.mainConf.spellLanguage = spellLanguage
|
||||||
self.mainConf.autoSaveDoc = autoSaveDoc
|
self.mainConf.autoSaveDoc = autoSaveDoc
|
||||||
self.mainConf.autoSaveProj = autoSaveProj
|
self.mainConf.autoSaveProj = autoSaveProj
|
||||||
@@ -286,6 +304,39 @@ class GuiConfigEditGeneral(QWidget):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _disableComboItem(self, theList, theValue):
|
||||||
|
theIdx = theList.findData(theValue)
|
||||||
|
theModel = theList.model()
|
||||||
|
anItem = theModel.item(1)
|
||||||
|
anItem.setFlags(anItem.flags() ^ Qt.ItemIsEnabled)
|
||||||
|
return theModel
|
||||||
|
|
||||||
|
def _doUpdateSpellTool(self, currIdx):
|
||||||
|
spellTool = self.spellToolList.currentData()
|
||||||
|
self._updateLanguageList(spellTool)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _updateLanguageList(self, spellTool):
|
||||||
|
"""Updates the list of available spell checking dictionaries
|
||||||
|
available for the selected spell check tool. It will try to
|
||||||
|
preserve the language choice, if the language exists in the
|
||||||
|
updated list.
|
||||||
|
"""
|
||||||
|
if spellTool == NWSpellCheck.SP_ENCHANT:
|
||||||
|
theDict = NWSpellEnchant()
|
||||||
|
else:
|
||||||
|
theDict = NWSpellSimple()
|
||||||
|
|
||||||
|
self.spellLangList.clear()
|
||||||
|
for spTag, spName in theDict.listDictionaries():
|
||||||
|
self.spellLangList.addItem(spName, spTag)
|
||||||
|
|
||||||
|
spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
|
||||||
|
if spellIdx != -1:
|
||||||
|
self.spellLangList.setCurrentIndex(spellIdx)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# END Class GuiConfigEditGeneral
|
# END Class GuiConfigEditGeneral
|
||||||
|
|
||||||
class GuiConfigEditEditor(QWidget):
|
class GuiConfigEditEditor(QWidget):
|
||||||
@@ -536,12 +587,12 @@ class GuiConfigEditEditor(QWidget):
|
|||||||
self.mainConf.textMargin = textMargin
|
self.mainConf.textMargin = textMargin
|
||||||
self.mainConf.tabWidth = tabWidth
|
self.mainConf.tabWidth = tabWidth
|
||||||
|
|
||||||
autoSelect = self.autoSelect.isChecked()
|
autoSelect = self.autoSelect.isChecked()
|
||||||
doReplace = self.autoReplaceMain.isChecked()
|
doReplace = self.autoReplaceMain.isChecked()
|
||||||
doReplaceSQuote = self.autoReplaceSQ.isChecked()
|
doReplaceSQuote = self.autoReplaceSQ.isChecked()
|
||||||
doReplaceDQuote = self.autoReplaceDQ.isChecked()
|
doReplaceDQuote = self.autoReplaceDQ.isChecked()
|
||||||
doReplaceDash = self.autoReplaceDash.isChecked()
|
doReplaceDash = self.autoReplaceDash.isChecked()
|
||||||
doReplaceDots = self.autoReplaceDash.isChecked()
|
doReplaceDots = self.autoReplaceDash.isChecked()
|
||||||
|
|
||||||
self.mainConf.autoSelect = autoSelect
|
self.mainConf.autoSelect = autoSelect
|
||||||
self.mainConf.doReplace = doReplace
|
self.mainConf.doReplace = doReplace
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from PyQt5.QtGui import (
|
|||||||
|
|
||||||
from nw.project import NWDoc
|
from nw.project import NWDoc
|
||||||
from nw.gui.tools import GuiDocHighlighter, WordCounter
|
from nw.gui.tools import GuiDocHighlighter, WordCounter
|
||||||
from nw.tools import NWSpellCheck
|
from nw.tools import NWSpellCheck, NWSpellSimple
|
||||||
from nw.constants import nwFiles, nwUnicode, nwDocAction, nwAlert
|
from nw.constants import nwFiles, nwUnicode, nwDocAction, nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -64,14 +64,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.qDocument = self.document()
|
self.qDocument = self.document()
|
||||||
self.qDocument.setDocumentMargin(self.mainConf.textMargin)
|
self.qDocument.setDocumentMargin(self.mainConf.textMargin)
|
||||||
self.qDocument.contentsChange.connect(self._docChange)
|
self.qDocument.contentsChange.connect(self._docChange)
|
||||||
if self.mainConf.spellTool == "enchant":
|
|
||||||
from nw.tools.spellenchant import NWSpellEnchant
|
|
||||||
self.theDict = NWSpellEnchant()
|
|
||||||
else:
|
|
||||||
self.theDict = NWSpellCheck()
|
|
||||||
|
|
||||||
|
# Syntax
|
||||||
self.hLight = GuiDocHighlighter(self.qDocument, self.theParent)
|
self.hLight = GuiDocHighlighter(self.qDocument, self.theParent)
|
||||||
self.hLight.setDict(self.theDict)
|
|
||||||
|
|
||||||
# Context Menu
|
# Context Menu
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
@@ -142,7 +137,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
created, and when the user changes the main editor preferences.
|
created, and when the user changes the main editor preferences.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Reload dictionaries
|
# Reload spell check and dictionaries
|
||||||
|
self._setupSpellChecking()
|
||||||
self.setDictionaries()
|
self.setDictionaries()
|
||||||
|
|
||||||
# Set font
|
# Set font
|
||||||
@@ -727,4 +723,19 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self._findNext()
|
self._findNext()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _setupSpellChecking(self):
|
||||||
|
"""Create the spell checking object based on the spellTool
|
||||||
|
setting in config.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.mainConf.spellTool == "enchant":
|
||||||
|
from nw.tools.spellenchant import NWSpellEnchant
|
||||||
|
self.theDict = NWSpellEnchant()
|
||||||
|
else:
|
||||||
|
self.theDict = NWSpellSimple()
|
||||||
|
|
||||||
|
self.hLight.setDict(self.theDict)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# END Class GuiDocEditor
|
# END Class GuiDocEditor
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from nw.tools.analyse import TextAnalysis
|
|||||||
from nw.tools.optlaststate import OptLastState
|
from nw.tools.optlaststate import OptLastState
|
||||||
from nw.tools.spellcheck import NWSpellCheck
|
from nw.tools.spellcheck import NWSpellCheck
|
||||||
from nw.tools.spellenchant import NWSpellEnchant
|
from nw.tools.spellenchant import NWSpellEnchant
|
||||||
|
from nw.tools.spellsimple import NWSpellSimple
|
||||||
from nw.tools.translate import numberToWord
|
from nw.tools.translate import numberToWord
|
||||||
from nw.tools.wordcount import countWords
|
from nw.tools.wordcount import countWords
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ __all__ = [
|
|||||||
"OptLastState",
|
"OptLastState",
|
||||||
"NWSpellCheck",
|
"NWSpellCheck",
|
||||||
"NWSpellEnchant",
|
"NWSpellEnchant",
|
||||||
|
"NWSpellSimple",
|
||||||
"numberToWord",
|
"numberToWord",
|
||||||
"countWords",
|
"countWords",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -17,9 +17,16 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class NWSpellCheck():
|
class NWSpellCheck():
|
||||||
|
|
||||||
|
SP_INTERNAL = "internal"
|
||||||
|
SP_ENCHANT = "enchant"
|
||||||
|
SP_SYMSPELL = "symspell"
|
||||||
|
|
||||||
theDict = None
|
theDict = None
|
||||||
|
PROJW = []
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.mainConf = nw.CONFIG
|
||||||
|
self.projectDict = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, theLang, projectDict=None):
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
@@ -32,9 +39,40 @@ class NWSpellCheck():
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def addWord(self, newWord):
|
def addWord(self, newWord):
|
||||||
|
if self.projectDict is not None and newWord not in self.PROJW:
|
||||||
|
newWord = newWord.strip()
|
||||||
|
self.PROJW.append(newWord)
|
||||||
|
try:
|
||||||
|
with open(self.projectDict,mode="w+",encoding="utf-8") as outFile:
|
||||||
|
for pWord in self.PROJW:
|
||||||
|
outFile.write("%s\n" % pWord)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to write to project word list at %s" % str(self.projectDict))
|
||||||
|
logger.error(str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
def listDictionaries(self):
|
def listDictionaries(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _readProjectDictionary(self, projectDict):
|
||||||
|
self.PROJW = []
|
||||||
|
if projectDict is not None:
|
||||||
|
self.projectDict = projectDict
|
||||||
|
try:
|
||||||
|
with open(projectDict,mode="r",encoding="utf-8") as wordsFile:
|
||||||
|
for theLine in wordsFile:
|
||||||
|
theLine = theLine.strip()
|
||||||
|
if len(theLine) > 0 and theLine not in self.PROJW:
|
||||||
|
self.PROJW.append(theLine)
|
||||||
|
logger.debug("Project word list")
|
||||||
|
logger.debug("Project word list contains %d words" % len(self.PROJW))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to load project word list")
|
||||||
|
logger.error(str(e))
|
||||||
|
return
|
||||||
|
|
||||||
# END Class NWSpellCheck
|
# END Class NWSpellCheck
|
||||||
|
|||||||
@@ -32,15 +32,16 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
crash.
|
crash.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if projectDict is None:
|
self.theDict = enchant.Dict(theLang)
|
||||||
self.theDict = enchant.Dict(theLang)
|
|
||||||
else:
|
|
||||||
self.theDict = enchant.DictWithPWL(theLang, projectDict)
|
|
||||||
logger.debug("Enchant spell checking for language %s loaded" % theLang)
|
logger.debug("Enchant spell checking for language %s loaded" % theLang)
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to load enchant spell checking for language %s" % theLang)
|
logger.error("Failed to load enchant spell checking for language %s" % theLang)
|
||||||
self.theDict = NWSpellEnchantDummy()
|
self.theDict = NWSpellEnchantDummy()
|
||||||
|
|
||||||
|
self._readProjectDictionary(projectDict)
|
||||||
|
for pWord in self.PROJW:
|
||||||
|
self.theDict.add_to_session(pWord)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def checkWord(self, theWord):
|
def checkWord(self, theWord):
|
||||||
@@ -50,7 +51,8 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
return self.theDict.suggest(theWord)
|
return self.theDict.suggest(theWord)
|
||||||
|
|
||||||
def addWord(self, newWord):
|
def addWord(self, newWord):
|
||||||
self.theDict.add_to_pwl(newWord)
|
self.theDict.add_to_session(newWord)
|
||||||
|
NWSpellCheck.addWord(self, newWord)
|
||||||
return
|
return
|
||||||
|
|
||||||
def listDictionaries(self):
|
def listDictionaries(self):
|
||||||
@@ -58,10 +60,9 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
for spTag, spProvider in enchant.list_dicts():
|
for spTag, spProvider in enchant.list_dicts():
|
||||||
spList = []
|
spList = []
|
||||||
if spTag[:2] in isoLanguage.ISO_639_1:
|
if spTag[:2] in isoLanguage.ISO_639_1:
|
||||||
langName = isoLanguage.ISO_639_1[spTag[:2]]
|
spList.append(isoLanguage.ISO_639_1[spTag[:2]])
|
||||||
else:
|
else:
|
||||||
langName = spTag[:2]
|
spList.append(spTag[:2])
|
||||||
spList.append(langName)
|
|
||||||
if len(spTag) > 3:
|
if len(spTag) > 3:
|
||||||
spList.append("(%s)" % spTag[3:])
|
spList.append("(%s)" % spTag[3:])
|
||||||
spList.append("[%s]" % spProvider.name)
|
spList.append("[%s]" % spProvider.name)
|
||||||
@@ -82,7 +83,7 @@ class NWSpellEnchantDummy:
|
|||||||
def suggest(self, theWord):
|
def suggest(self, theWord):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def add_to_pwl(self, theWord):
|
def add_to_session(self, theWord):
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class NWSpellEnchantDummy
|
# END Class NWSpellEnchantDummy
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""novelWriter Spell Check Simple
|
||||||
|
|
||||||
|
novelWriter – Spell Check Simple
|
||||||
|
==================================
|
||||||
|
Simple spell checker based on difflib
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2019-06-11 [0.1.5]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import nw
|
||||||
|
|
||||||
|
from os import path, listdir
|
||||||
|
from difflib import get_close_matches
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
from nw.tools.spellcheck import NWSpellCheck
|
||||||
|
from nw.constants import isoLanguage
|
||||||
|
|
||||||
|
class NWSpellSimple(NWSpellCheck):
|
||||||
|
|
||||||
|
WORDS = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
NWSpellCheck.__init__(self)
|
||||||
|
logger.debug("Simple spell checking activated")
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
|
|
||||||
|
self.WORDS = []
|
||||||
|
dictFile = path.join(self.mainConf.dictPath,theLang+".dict")
|
||||||
|
try:
|
||||||
|
with open(dictFile,mode="r",encoding="utf-8") as wordsFile:
|
||||||
|
for theLine in wordsFile:
|
||||||
|
if len(theLine) == 0 or theLine.startswith("#"):
|
||||||
|
continue
|
||||||
|
self.WORDS.append(theLine.strip().lower())
|
||||||
|
logger.debug("Spell check word list for language %s loaded" % theLang)
|
||||||
|
logger.debug("Word list contains %d words" % len(self.WORDS))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to load spell check word list for language %s" % theLang)
|
||||||
|
logger.error(str(e))
|
||||||
|
|
||||||
|
self._readProjectDictionary(projectDict)
|
||||||
|
for pWord in self.PROJW:
|
||||||
|
if pWord not in self.WORDS:
|
||||||
|
self.WORDS.append(pWord)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def checkWord(self, theWord):
|
||||||
|
"""Check if a word exists in the word list. Make sure to keep
|
||||||
|
this function as fast as possible as it is called for every
|
||||||
|
word by the syntax highlighter.
|
||||||
|
"""
|
||||||
|
theWord = theWord.replace(self.mainConf.fmtApostrophe,"'").lower()
|
||||||
|
return theWord in self.WORDS
|
||||||
|
|
||||||
|
def suggestWords(self, theWord):
|
||||||
|
"""Get suggestions for correct word from difflib, and make sure
|
||||||
|
the first character is upper case if that was also the case for
|
||||||
|
the word be3ing checked. Also make sure the apostrophe is
|
||||||
|
changed to the one in the dictionary, and then put back in the
|
||||||
|
results.
|
||||||
|
"""
|
||||||
|
theWord = theWord.strip()
|
||||||
|
if len(theWord) == 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
firstUp = theWord[0] == theWord[0].upper()
|
||||||
|
theWord = theWord.lower()
|
||||||
|
|
||||||
|
theMatches = get_close_matches(theWord, self.WORDS, n=10, cutoff=0.75)
|
||||||
|
theOptions = []
|
||||||
|
for aWord in theMatches:
|
||||||
|
if len(aWord) == 0:
|
||||||
|
continue
|
||||||
|
if firstUp:
|
||||||
|
aWord = aWord[0].upper() + aWord[1:]
|
||||||
|
aWord = aWord.replace("'",self.mainConf.fmtApostrophe)
|
||||||
|
theOptions.append(aWord)
|
||||||
|
|
||||||
|
return theOptions
|
||||||
|
|
||||||
|
def addWord(self, newWord):
|
||||||
|
newWord = newWord.strip().lower()
|
||||||
|
if newWord not in self.WORDS:
|
||||||
|
self.WORDS.append(newWord)
|
||||||
|
NWSpellCheck.addWord(self, newWord)
|
||||||
|
return
|
||||||
|
|
||||||
|
def listDictionaries(self):
|
||||||
|
|
||||||
|
retList = []
|
||||||
|
for dictFile in listdir(self.mainConf.dictPath):
|
||||||
|
|
||||||
|
theBits = path.splitext(dictFile)
|
||||||
|
if len(theBits) != 2:
|
||||||
|
continue
|
||||||
|
if theBits[1] != ".dict":
|
||||||
|
continue
|
||||||
|
|
||||||
|
spTag = theBits[0]
|
||||||
|
spList = []
|
||||||
|
if spTag[:2] in isoLanguage.ISO_639_1:
|
||||||
|
spList.append(isoLanguage.ISO_639_1[spTag[:2]])
|
||||||
|
else:
|
||||||
|
spList.append(spTag[:2])
|
||||||
|
if len(spTag) > 3:
|
||||||
|
spList.append("(%s)" % spTag[3:])
|
||||||
|
spList.append("[internal]")
|
||||||
|
spName = " ".join(spList)
|
||||||
|
retList.append((spTag, spName))
|
||||||
|
|
||||||
|
return retList
|
||||||
|
|
||||||
|
# END Class NWSpellSimple
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[Main]
|
[Main]
|
||||||
timestamp = 2019-11-03 00:40:02
|
timestamp = 2019-11-08 00:27:54
|
||||||
theme = default
|
theme = default
|
||||||
syntax = default_light
|
syntax = default_light
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@ repdash = True
|
|||||||
repdots = True
|
repdots = True
|
||||||
fmtsinglequote = ‘, ’
|
fmtsinglequote = ‘, ’
|
||||||
fmtdoublequote = “, ”
|
fmtdoublequote = “, ”
|
||||||
spellcheck = en_GB
|
spelltool = internal
|
||||||
|
spellcheck = en
|
||||||
showtabsnspaces = False
|
showtabsnspaces = False
|
||||||
showlineendings = False
|
showlineendings = False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user