Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit 31296a4

Browse files
authored
Merge pull request #332 from dmoonfire/locale-issues-tests
Fix issues with Windows and Mac checking, reduce noise
2 parents 8e7f796 + f1482b7 commit 31296a4

File tree

8 files changed

+225
-54
lines changed

8 files changed

+225
-54
lines changed

DEVELOP.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Here are additional details for debugging for working with the library.
2+
3+
## Plugins
4+
5+
_Spell Check_ allows for plugins to provide additional spell checking functionality. See the `PLUGINS.md` file in the repository on how to write a plugin.
6+
7+
## Debugging
8+
9+
Debugging messages for this library can be enabled by going into the developer console and running the following:
10+
11+
```
12+
localStorage.debug = 'spell-check:*'
13+
```
14+
15+
A reload of the window may be required.

README.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ To enable _Spell Check_ for your current file type: put your cursor in the file,
2020

2121
## Changing the dictionary
2222

23-
To change the language of the dictionary, set the "Locales" configuration option to the IETF tag (en-US, fr-FR, etc). More than one language can be used, simply separate them by commas.
23+
Except for Mac, Atom needs to know what language to use to perform spell-checking. To list these, set the "Locales" configuration option to the IETF tag (en-US, fr-FR, etc). More than one language can be used, simply separate them by commas.
24+
25+
If no locale is given, then Atom will attempt to infer the language based on environment variables and settings.
26+
27+
If any value is given for the "Locales", then Atom will not automatically add the browser language. So, if your browser is United States English (`en-US`), leaving this blank will still do US English checking. However, if it the "Locales" is set to French (`fr-FR`), then the checker will only check French. If the "Locales" is set to `en-US, fr-FR`, then both languages will be checked.
2428

2529
### Missing Languages
2630

2731
This plugin uses the existing system dictionaries. If a locale is selected that is not installed, a warning will pop up when a document that would be spell-checked is loaded. To disable this, either remove the incorrect language from the "Locales" configuration or clear the check on "Use Locales" to disable it entirely.
2832

2933
To get the search paths used to look for a dictionary, make sure the "Notices Mode" is set to "console" or "both", then reload Atom. The developer's console will have the directory list.
3034

31-
#### Windows 8 and Higher
35+
## Mac
36+
37+
On the Mac, checking "Use System" will use the operating system's spellchecking library. This uses all of the user's loaded dictionaries and doesn't require any customization within Atom.
38+
39+
Checking "Use Locales" and providing locales would use Hunspell as additional dictionaries. Having "Use Locales" checked but no locales given will do nothing.
40+
41+
## Windows 8 and Higher
3242

3343
For Windows 8 and 10, this package uses the Windows spell checker, so you must install the language using the regional settings before the language can be chosen inside Atom.
3444

@@ -40,11 +50,13 @@ If your Windows user does not have Administration privileges, you'll need to do
4050

4151
![Download the "Basic Typing" language option](docs/windows-10-language-settings-3.png)
4252

43-
Once the additional language is added, Atom will need to be restarted.
53+
Once the additional language is added, Atom will need to be restarted and configured to use it. Add the IEFT tag into the "Locales" setting for the language to be set.
54+
55+
If a Hunspell dictionary is found on a path (see below), it will be used in favor of the Windows API.
4456

45-
*Previously, setting `SPELLCHECKER_PREFER_HUNSPELL` environment variable would change how checking works. Now this is controlled by the system and locale checker to use the operating system version or Hunspell respectively.*
57+
## Linux
4658

47-
If locale is not set, Atom will attempt to use the current locale from the environment variable; if that is missing, `en-US` will be used. The dictionary for `en-US` is shipping with Atom but all other locale-based checkers will need to be downloaded from another source.
59+
For all Linux-based operating systems, "Use System" does nothing. It can remained checked but has no impact. "Use Locales" is required for spell-checking.
4860

4961
### Debian, Ubuntu, and Mint
5062

@@ -86,6 +98,27 @@ sudo ln -s en_GB-large.dic en_GB.dic
8698
sudo ln -s en_GB-large.aff en_GB.aff
8799
```
88100

89-
## Plugins
101+
## Hunspell Dictionaries
102+
103+
For all platforms, a Hunspell-compatible dictionary is also supported. To use this, a `.dic` and `.aff` need to be located in one of the default search directories or in a directory entered into "Locale paths" (multiples may be entered with commas separating them). If the appropriate files are found for the locale and "Use Locales" is checked, then the dictionary will be used.
104+
105+
For example, if the following is set, then `/usr/share/hunspell/en_US.dic` will be used:
106+
107+
- Use Locales: checked
108+
- Locales: `en-US`
109+
- Locale Paths: `/usr/share/hunspell`
110+
111+
If "Locales" is not provided, then the user's current language will be inferred from environmental settings.
112+
113+
In addition to what is provided, the following paths are checked:
114+
115+
- `/usr/share/hunspell` (Linux only)
116+
- `/usr/share/myspell` (Linux only)
117+
- `/usr/share/myspell/dicts` (Linux only)
118+
- `/` (Mac only)
119+
- `/System/Library/Spelling` (Mac only)
120+
- `C:\` (Windows only)
121+
122+
Dictionaries can be downloaded from various sites (such as [wooorm's repository](https://github.com/wooorm/dictionaries) or [LibreOffice's](https://github.com/LibreOffice/dictionaries)), but the file has to be renamed `locale.dic` and `locale.aff`.
90123

91-
_Spell Check_ allows for plugins to provide additional spell checking functionality. See the `PLUGINS.md` file in the repository on how to write a plugin.
124+
*Example locations to download are not an endorsement.*

lib/locale-checker.coffee

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
spellchecker = require 'spellchecker'
22
pathspec = require 'atom-pathspec'
33
env = require './checker-env'
4+
debug = require 'debug'
45

56
# The locale checker is a checker that takes a locale string (`en-US`) and
67
# optionally a path and then checks it.
@@ -10,12 +11,17 @@ class LocaleChecker
1011
enabled: true
1112
reason: null
1213
paths: null
14+
checkDictionaryPath: true
15+
checkDefaultPaths: true
1316

14-
constructor: (locale, paths) ->
17+
constructor: (locale, paths, hasSystemChecker, inferredLocale) ->
1518
@locale = locale
1619
@paths = paths
1720
@enabled = true
18-
#console.log @getId(), "enabled", @isEnabled()
21+
@hasSystemChecker = hasSystemChecker
22+
@inferredLocale = inferredLocale
23+
@log = debug('spell-check:locale-checker').extend(locale)
24+
@log 'enabled', @isEnabled(), 'hasSystemChecker', @hasSystemChecker, 'inferredLocale', @inferredLocale
1925

2026
deactivate: ->
2127
return
@@ -33,7 +39,9 @@ class LocaleChecker
3339
@deferredInit()
3440
id = @getId()
3541
if @enabled
36-
@spellchecker.checkSpellingAsync(text).then (incorrect) ->
42+
@spellchecker.checkSpellingAsync(text).then (incorrect) =>
43+
if @log.enabled
44+
@log 'check', incorrect
3745
{id, invertIncorrectAsCorrect: true, incorrect}
3846
else
3947
{id, status: @getStatus()}
@@ -43,59 +51,76 @@ class LocaleChecker
4351
@spellchecker.getCorrectionsForMisspelling(word)
4452

4553
deferredInit: ->
46-
# The system checker is not enabled for Linux platforms (no built-in checker).
47-
if not @enabled
48-
@reason = "Darwin does not use locale-based checking without SPELLCHECKER_PREFER_HUNSPELL set."
49-
return
50-
5154
# If we already have a spellchecker, then we don't have to do anything.
5255
if @spellchecker
5356
return
5457

5558
# Initialize the spell checker which can take some time. We also force
5659
# the use of the Hunspell library even on Mac OS X. The "system checker"
5760
# is the one that uses the built-in dictionaries from the operating system.
58-
@spellchecker = new spellchecker.Spellchecker
59-
@spellchecker.setSpellcheckerType spellchecker.ALWAYS_USE_HUNSPELL
61+
checker = new spellchecker.Spellchecker
62+
checker.setSpellcheckerType spellchecker.ALWAYS_USE_HUNSPELL
6063

6164
# Build up a list of paths we are checking so we can report them fully
6265
# to the user if we fail.
6366
searchPaths = []
64-
65-
# Windows uses its own API and the paths are unimportant, only attempting
66-
# to load it works.
67-
if env.isWindows()
68-
#if env.useWindowsSystemDictionary()
69-
# return
70-
searchPaths.push "C:\\"
71-
72-
# Check the paths supplied by the user.
7367
for path in @paths
7468
searchPaths.push pathspec.getPath(path)
7569

76-
# For Linux, we have to search the directory paths to find the dictionary.
77-
if env.isLinux()
78-
searchPaths.push "/usr/share/hunspell"
79-
searchPaths.push "/usr/share/myspell"
80-
searchPaths.push "/usr/share/myspell/dicts"
70+
# Add operating system specific paths to the search list.
71+
if @checkDefaultPaths
72+
if env.isLinux()
73+
searchPaths.push "/usr/share/hunspell"
74+
searchPaths.push "/usr/share/myspell"
75+
searchPaths.push "/usr/share/myspell/dicts"
8176

82-
# OS X uses the following paths.
83-
if env.isDarwin()
84-
searchPaths.push "/"
85-
searchPaths.push "/System/Library/Spelling"
77+
if env.isDarwin()
78+
searchPaths.push "/"
79+
searchPaths.push "/System/Library/Spelling"
8680

87-
# Try the packaged library inside the node_modules. `getDictionaryPath` is
88-
# not available, so we have to fake it. This will only work for en-US.
89-
searchPaths.push spellchecker.getDictionaryPath()
81+
if env.isWindows()
82+
searchPaths.push "C:\\"
9083

9184
# Attempt to load all the paths for the dictionary until we find one.
85+
@log 'checking paths', searchPaths
9286
for path in searchPaths
93-
if @spellchecker.setDictionary @locale, path
87+
if checker.setDictionary @locale, path
88+
@log 'found checker', path
89+
@spellchecker = checker
90+
return
91+
92+
# On Windows, if we can't find the dictionary using the paths, then we also
93+
# try the spelling API. This uses system checker with the given locale, but
94+
# doesn't provide a path. We do this at the end to let Hunspell be used if
95+
# the user provides that.
96+
if env.isWindows()
97+
systemChecker = new spellchecker.Spellchecker
98+
systemChecker.setSpellcheckerType spellchecker.ALWAYS_USE_SYSTEM
99+
if systemChecker.setDictionary @locale, ""
100+
@log 'using Windows Spell API'
101+
@spellchecker = systemChecker
102+
return
103+
104+
# If all else fails, try the packaged en-US dictionary in the `spellcheck`
105+
# library.
106+
if @checkDictionaryPath
107+
if checker.setDictionary @locale, spellchecker.getDictionaryPath()
108+
@log 'using packaged locale', path
109+
@spellchecker = checker
94110
return
95111

112+
# If we are using the system checker and we infered the locale, then we
113+
# don't want to show an error. This is because the system checker may have
114+
# handled it already.
115+
if @hasSystemChecker and @inferredLocale
116+
@log 'giving up quietly because of system checker and inferred locale'
117+
@enabled = false
118+
@reason = "Cannot load the locale dictionary for `" + @locale + "`. No warning because system checker is in use and locale is inferred."
119+
return
120+
96121
# If we fell through all the if blocks, then we couldn't load the dictionary.
97122
@enabled = false
98-
@reason = "Cannot load the system dictionary for `" + @locale + "`."
123+
@reason = "Cannot load the locale dictionary for `" + @locale + "`."
99124
message = "The package `spell-check` cannot load the " \
100125
+ "checker for `" \
101126
+ @locale + "`." \

lib/main.coffee

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{CompositeDisposable} = require 'atom'
2+
debug = require 'debug'
23

34
SpellCheckView = null
45
spellCheckViews = {}
@@ -7,6 +8,9 @@ LARGE_FILE_SIZE = 2 * 1024 * 1024
78

89
module.exports =
910
activate: ->
11+
log = debug('spell-check')
12+
log('initializing')
13+
1014
@subs = new CompositeDisposable
1115

1216
# Since the spell-checking is done on another process, we gather up all the

lib/spell-check-manager.coffee

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
debug = require 'debug'
2+
env = require './checker-env'
3+
14
class SpellCheckerManager
25
checkers: []
36
checkerPaths: []
@@ -35,20 +38,22 @@ class SpellCheckerManager
3538
@removeSpellChecker @knownWordsChecker
3639
@knownWordsChecker = null
3740

38-
# Handle system checker.
41+
# Handle system checker. We also will remove the locale checkers if we
42+
# change the system checker because we show different messages if we cannot
43+
# find a locale based on the use of the system checker.
3944
removeSystemChecker = false
45+
removeLocaleCheckers = false
4046

4147
if @useSystem isnt data.useSystem
4248
@useSystem = data.useSystem
4349
removeSystemChecker = true
50+
removeLocaleCheckers = true
4451

4552
if removeSystemChecker and @systemChecker
4653
@removeSpellChecker @systemChecker
4754
@systemChecker = undefined
4855

4956
# Handle locale checkers.
50-
removeLocaleCheckers = false
51-
5257
if not _.isEqual(@locales, data.locales)
5358
# If the locales is blank, then we always create a default one. However,
5459
# any new data.locales will remain blank.
@@ -119,6 +124,9 @@ class SpellCheckerManager
119124
promises.push Promise.resolve checker.check(args, text)
120125

121126
Promise.all(promises).then (allResults) =>
127+
if @log.enabled
128+
@log "check results", allResults, text
129+
122130
for results in allResults
123131
if results.invertIncorrectAsCorrect and results.incorrect
124132
# We need to add the opposite of the incorrect as correct elements in
@@ -145,7 +153,12 @@ class SpellCheckerManager
145153

146154
# If we don't have any incorrect spellings, then there is nothing to worry
147155
# about, so just return and stop processing.
156+
if @log.enabled
157+
@log "merged correct ranges", correct
158+
@log "merged incorrect ranges", incorrects
159+
148160
if incorrects.length is 0
161+
@log "no spelling errors"
149162
return {misspellings: []}
150163

151164
# Build up an intersection of all the incorrect ranges. We only treat a word
@@ -165,6 +178,7 @@ class SpellCheckerManager
165178

166179
# If we have no intersection, then nothing to report as a problem.
167180
if intersection.length is 0
181+
@log "no spelling after intersections"
168182
return {misspellings: []}
169183

170184
# Remove all of the confirmed correct words from the resulting incorrect
@@ -173,6 +187,9 @@ class SpellCheckerManager
173187
if correct.ranges.length > 0
174188
intersection.subtract(correct)
175189

190+
if @log.enabled
191+
@log "check intersections", intersection
192+
176193
# Convert the text ranges (index into the string) into Atom buffer
177194
# coordinates ( row and column).
178195
row = 0
@@ -316,7 +333,11 @@ class SpellCheckerManager
316333
])
317334

318335
init: ->
336+
# Set up logging.
337+
@log = debug "spell-check:spell-check-manager"
338+
319339
# Set up the system checker.
340+
hasSystemChecker = @useSystem and env.isSystemSupported()
320341
if @useSystem and @systemChecker is null
321342
SystemChecker = require './system-checker'
322343
@systemChecker = new SystemChecker
@@ -335,10 +356,14 @@ class SpellCheckerManager
335356
@localeCheckers = []
336357

337358
# If we have a blank location, use the default based on the process. If
338-
# set, then it will be the best language.
359+
# set, then it will be the best language. We keep track if we are using
360+
# the default locale to control error messages.
361+
inferredLocale = false
362+
339363
if not @locales.length
340364
defaultLocale = process.env.LANG
341365
if defaultLocale
366+
inferredLocale = true
342367
@locales = [defaultLocale.split('.')[0]]
343368

344369
# If we can't figure out the language from the process, check the
@@ -351,18 +376,20 @@ class SpellCheckerManager
351376
if defaultLocale and defaultLocale.length is 5
352377
separatorChar = defaultLocale.charAt(2)
353378
if separatorChar is '_' or separatorChar is '-'
379+
inferredLocale = true
354380
@locales = [defaultLocale]
355381

356382
# If we still can't figure it out, use US English. It isn't a great
357383
# choice, but it is a reasonable default not to mention is can be used
358384
# with the fallback path of the `spellchecker` package.
359385
if not @locales.length
386+
inferredLocale = true
360387
@locales = ['en_US']
361388

362389
# Go through the new list and create new locale checkers.
363390
LocaleChecker = require "./locale-checker"
364391
for locale in @locales
365-
checker = new LocaleChecker locale, @localePaths
392+
checker = new LocaleChecker locale, @localePaths, hasSystemChecker, inferredLocale
366393
@addSpellChecker checker
367394
@localeCheckers.push checker
368395

0 commit comments

Comments
 (0)