mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 18:51:55 +08:00
Add Windows spell checker and fixes (#1624)
* Add Windows spell checker and fixes * Fix build failure on macOS * Fix preference visibility
This commit is contained in:
parent
0dc7038083
commit
245962a6a1
@ -1,6 +1,6 @@
|
|||||||
# Spelling
|
# Spelling
|
||||||
|
|
||||||
Mark Text can automatically check your text for misspelled words as you type and suggest corrections. You just need to enable spell checking in settings under *spelling* to never miss a misspelled word. We're using Hunspell for Linux and Windows and on macOS you can choose between Hunspell or the system spell checker (default). You can control the default proofing language via settings but can change the language at runtime via right-click menu `Change Language` entry under `Spelling` without changing the default language. By default Mark Text only support American English for Hunspell and the local available languages for macOS spell checker. You can download 42 languages for Hunspell and many more for macOS.
|
Mark Text can automatically check your text for misspelled words as you type and suggest corrections. You just need to enable spell checking in settings under *spelling* to never miss a misspelled word. We're using Hunspell for Linux and older Windows versions and on macOS and Window 10 you can choose between Hunspell or the system spell checker (default). You can control the default proofing language via settings but can change the language at runtime via right-click menu `Change Language` entry under `Spelling` without changing the default language. By default Mark Text only support American English for Hunspell and the local available languages for the system spell checker. You can download 42 languages for Hunspell and many more for macOS and Windows 10 via system settings.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -22,7 +22,11 @@ You can add words to the selected dictionary by right-clicking on a misspelled w
|
|||||||
|
|
||||||
### macOS spell checker
|
### macOS spell checker
|
||||||
|
|
||||||
You need to add the needed language dictionaries via *Language & Region* in your system preferences pane.
|
You need to add the additional language dictionaries via *"Language & Region"* in your system preferences pane.
|
||||||
|
|
||||||
|
### Windows spell checker
|
||||||
|
|
||||||
|
On Windows 10, you need to add additional language dictionaries via *"Language"* in your *"Time & language"* settings. Add the additional language(s) and download the *"Basic typing"* language option for each language.
|
||||||
|
|
||||||
### Hunspell
|
### Hunspell
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
@ -10,9 +10,10 @@ git clone https://github.com/marktext/marktext.git
|
|||||||
|
|
||||||
Before you can get started developing, you need set up your build environment:
|
Before you can get started developing, you need set up your build environment:
|
||||||
|
|
||||||
- Node.js `>=v12.0.0`, npm and yarn
|
- Node.js `>=v12.0.0` and yarn
|
||||||
- Python `v2.7.x` for node-gyp
|
- Python `v2.7.x` for node-gyp
|
||||||
- C++ compiler and development tools
|
- C++ compiler and development tools
|
||||||
|
- Build is supported on Linux, macOS and Windows 10
|
||||||
|
|
||||||
**Additional development dependencies on Linux:**
|
**Additional development dependencies on Linux:**
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hfelix/electron-localshortcut": "^3.1.1",
|
"@hfelix/electron-localshortcut": "^3.1.1",
|
||||||
"@hfelix/electron-spellchecker": "^1.0.0-rc.3",
|
"@hfelix/electron-spellchecker": "1.0.0-rc.5",
|
||||||
"@octokit/rest": "^16.33.1",
|
"@octokit/rest": "^16.33.1",
|
||||||
"arg": "^4.1.1",
|
"arg": "^4.1.1",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
|
@ -81,14 +81,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const search = location.search
|
const params = new URLSearchParams(window.location.search)
|
||||||
const params = search.split('?')[1].split('&').reduce((acc, i) => {
|
|
||||||
const tokens = i.split('=')
|
// --- WORKAROUND ---
|
||||||
return {
|
// `node-spellchecker` is loaded by `internal/modules/cjs/loader.js` before
|
||||||
...acc,
|
// we can set the envoriment variable in our bootstrap routine.
|
||||||
[tokens[0]]: tokens[1]
|
const type = params.get('type')
|
||||||
}
|
const spellcheckerIsHunspell = params.get('slp') === '1'
|
||||||
}, {})
|
if (spellcheckerIsHunspell && type !== 'settings') {
|
||||||
|
// Set option to always use Hunspell instead OS spell checker.
|
||||||
|
process.env['SPELLCHECKER_PREFER_HUNSPELL'] = 1 // eslint-disable-line dot-notation
|
||||||
|
}
|
||||||
|
// --- END WORKAROUND ---
|
||||||
|
|
||||||
const THEMES_COLOR = {
|
const THEMES_COLOR = {
|
||||||
'one-dark': 'rgba(77, 120, 204, 1)',
|
'one-dark': 'rgba(77, 120, 204, 1)',
|
||||||
'dark': '#409eff',
|
'dark': '#409eff',
|
||||||
@ -100,7 +105,7 @@
|
|||||||
|
|
||||||
const dots = document.querySelectorAll('.dot')
|
const dots = document.querySelectorAll('.dot')
|
||||||
Array.from(dots).forEach(dot => {
|
Array.from(dots).forEach(dot => {
|
||||||
dot.style.background = THEMES_COLOR[params['theme']] || 'rgba(33, 181, 111, 1)'
|
dot.style.background = THEMES_COLOR[params.get('theme')] || 'rgba(33, 181, 111, 1)'
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_openSettingsWindow () {
|
_openSettingsWindow () {
|
||||||
const settingWins = this._windowManager.getWindowsByType(WindowType.SETTING)
|
const settingWins = this._windowManager.getWindowsByType(WindowType.SETTINGS)
|
||||||
if (settingWins.length >= 1) {
|
if (settingWins.length >= 1) {
|
||||||
// A setting window is already created
|
// A setting window is already created
|
||||||
const browserSettingWindow = settingWins[0].win.browserWindow
|
const browserSettingWindow = settingWins[0].win.browserWindow
|
||||||
|
@ -199,7 +199,7 @@ class WindowManager extends EventEmitter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {WindowType} type the WindowType one of ['base', 'editor', 'setting']
|
* @param {WindowType} type the WindowType one of ['base', 'editor', 'settings']
|
||||||
* @returns {{id: number, win: BaseWindow}[]} Return the windows of the given {type}
|
* @returns {{id: number, win: BaseWindow}[]} Return the windows of the given {type}
|
||||||
*/
|
*/
|
||||||
getWindowsByType (type) {
|
getWindowsByType (type) {
|
||||||
|
@ -14,7 +14,7 @@ import { isLinux } from '../config'
|
|||||||
export const WindowType = {
|
export const WindowType = {
|
||||||
BASE: 'base', // You shold never create a `BASE` window.
|
BASE: 'base', // You shold never create a `BASE` window.
|
||||||
EDITOR: 'editor',
|
EDITOR: 'editor',
|
||||||
SETTING: 'setting'
|
SETTINGS: 'settings'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WindowLifecycle = {
|
export const WindowLifecycle = {
|
||||||
|
@ -11,7 +11,7 @@ class SettingWindow extends BaseWindow {
|
|||||||
*/
|
*/
|
||||||
constructor (accessor) {
|
constructor (accessor) {
|
||||||
super(accessor)
|
super(accessor)
|
||||||
this.type = WindowType.SETTING
|
this.type = WindowType.SETTINGS
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
5
src/renderer/bootstrap.js
vendored
5
src/renderer/bootstrap.js
vendored
@ -99,7 +99,10 @@ const bootstrapRenderer = () => {
|
|||||||
global.marktext = marktext
|
global.marktext = marktext
|
||||||
|
|
||||||
// Set option to always use Hunspell instead OS spell checker.
|
// Set option to always use Hunspell instead OS spell checker.
|
||||||
if (spellcheckerIsHunspell) {
|
if (spellcheckerIsHunspell && type !== 'settings') {
|
||||||
|
// HACK: This code doesn't do anything because `node-spellchecker` is loaded by
|
||||||
|
// `internal/modules/cjs/loader.js` before we can set the envoriment variable here.
|
||||||
|
// The code is additionally added to `index.ejs` to workaound the problem.
|
||||||
process.env['SPELLCHECKER_PREFER_HUNSPELL'] = 1 // eslint-disable-line dot-notation
|
process.env['SPELLCHECKER_PREFER_HUNSPELL'] = 1 // eslint-disable-line dot-notation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +97,8 @@ import { DEFAULT_EDITOR_FONT_FAMILY } from '@/config'
|
|||||||
import { showContextMenu } from '@/contextMenu/editor'
|
import { showContextMenu } from '@/contextMenu/editor'
|
||||||
import notice from '@/services/notification'
|
import notice from '@/services/notification'
|
||||||
import Printer from '@/services/printService'
|
import Printer from '@/services/printService'
|
||||||
import { offsetToWordCursor, validateLineCursor, SpellChecker } from '@/spellchecker'
|
import { isOsSpellcheckerSupported, offsetToWordCursor, validateLineCursor, SpellChecker } from '@/spellchecker'
|
||||||
import { isOsx, animatedScrollTo } from '@/util'
|
import { delay, isOsx, animatedScrollTo } from '@/util'
|
||||||
import { moveImageToFolder, uploadImage } from '@/util/fileSystem'
|
import { moveImageToFolder, uploadImage } from '@/util/fileSystem'
|
||||||
import { guessClipboardFilePath } from '@/util/clipboard'
|
import { guessClipboardFilePath } from '@/util/clipboard'
|
||||||
import { getCssForOptions } from '@/util/pdf'
|
import { getCssForOptions } from '@/util/pdf'
|
||||||
@ -373,19 +373,21 @@ export default {
|
|||||||
// Special case when the OS supports multiple spell checker because the
|
// Special case when the OS supports multiple spell checker because the
|
||||||
// language may be invalid (provider 1 may support language xyz
|
// language may be invalid (provider 1 may support language xyz
|
||||||
// but provider 2 not). Otherwise ignore this event.
|
// but provider 2 not). Otherwise ignore this event.
|
||||||
const multiProviderSupported = isOsx
|
if (isOsSpellcheckerSupported() && value !== oldValue) {
|
||||||
if (multiProviderSupported && value !== oldValue) {
|
|
||||||
const { spellchecker } = this
|
const { spellchecker } = this
|
||||||
const { isHunspell } = spellchecker
|
const { isHunspell } = spellchecker
|
||||||
if (value === isHunspell) {
|
if (value === isHunspell) {
|
||||||
this.spellcheckerIgnorChanges = false
|
this.spellcheckerIgnorChanges = false
|
||||||
|
|
||||||
// Apply language from settings that may have changed.
|
// NOTE: Set timout because the language may be changed if it's not supported.
|
||||||
const { spellcheckerLanguage } = this
|
delay(500).then(() => {
|
||||||
const { isEnabled, lang } = spellchecker
|
// Apply language from settings that may have changed.
|
||||||
if (isEnabled && spellcheckerLanguage !== lang) {
|
const { spellcheckerLanguage } = this
|
||||||
this.switchSpellcheckLanguage(spellcheckerLanguage)
|
const { isEnabled, isHunspell, lang } = spellchecker
|
||||||
}
|
if (value === isHunspell && isEnabled && spellcheckerLanguage !== lang) {
|
||||||
|
this.switchSpellcheckLanguage(spellcheckerLanguage)
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
// Ignore all settings language changes that occur when another
|
// Ignore all settings language changes that occur when another
|
||||||
// spell check provider is selected.
|
// spell check provider is selected.
|
||||||
@ -641,20 +643,27 @@ export default {
|
|||||||
|
|
||||||
// Translate offsets into a cursor with the given line.
|
// Translate offsets into a cursor with the given line.
|
||||||
const wordRange = offsetToWordCursor(selection, left, right)
|
const wordRange = offsetToWordCursor(selection, left, right)
|
||||||
this.spellchecker.getWordSuggestion(word)
|
|
||||||
.then(wordSuggestions => {
|
// NOTE: Need to check whether the word is misspelled because
|
||||||
const replaceCallback = replacement => {
|
// suggestions may be empty even if word is misspelled.
|
||||||
// wordRange := replace this range with the replacement
|
if (this.spellchecker.isMisspelled(word)) {
|
||||||
this.editor.replaceWordInline(selection, wordRange, replacement, true)
|
this.spellchecker.getWordSuggestion(word)
|
||||||
}
|
.then(wordSuggestions => {
|
||||||
showContextMenu(event, selection, this.spellchecker, word, wordSuggestions, replaceCallback)
|
const replaceCallback = replacement => {
|
||||||
})
|
// wordRange := replace this range with the replacement
|
||||||
|
this.editor.replaceWordInline(selection, wordRange, replacement, true)
|
||||||
|
}
|
||||||
|
showContextMenu(event, selection, this.spellchecker, word, wordSuggestions, replaceCallback)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
showContextMenu(event, selection, this.spellchecker, word, null, null)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No word selected or fallback
|
// No word selected or fallback
|
||||||
showContextMenu(event, selection, isEnabled ? this.spellchecker : null, '', [], null)
|
showContextMenu(event, selection, isEnabled ? this.spellchecker : null, '', null, null)
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('keyup', this.keyup)
|
document.addEventListener('keyup', this.keyup)
|
||||||
|
@ -126,7 +126,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleLeftBottomClick (name) {
|
handleLeftBottomClick (name) {
|
||||||
if (name === 'setting') {
|
if (name === 'settings') {
|
||||||
this.$store.dispatch('OPEN_SETTING_WINDOW')
|
this.$store.dispatch('OPEN_SETTING_WINDOW')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@ export default (spellchecker, selectedWord, wordSuggestions, replaceCallback) =>
|
|||||||
|
|
||||||
spellingSubmenu.push(SEPARATOR)
|
spellingSubmenu.push(SEPARATOR)
|
||||||
|
|
||||||
// Word suggestions
|
// Handle misspelled word if wordSuggestions is set, otherwise word is correct.
|
||||||
if (selectedWord && wordSuggestions && wordSuggestions.length > 0) {
|
if (selectedWord && wordSuggestions) {
|
||||||
spellingSubmenu.push({
|
spellingSubmenu.push({
|
||||||
label: 'Add to Dictionary',
|
label: 'Add to Dictionary',
|
||||||
click (menuItem, targetWindow) {
|
click (menuItem, targetWindow) {
|
||||||
@ -63,16 +63,19 @@ export default (spellchecker, selectedWord, wordSuggestions, replaceCallback) =>
|
|||||||
spellchecker.ignoreWord(selectedWord)
|
spellchecker.ignoreWord(selectedWord)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
spellingSubmenu.push(SEPARATOR)
|
|
||||||
for (const word of wordSuggestions) {
|
if (wordSuggestions.length > 0) {
|
||||||
spellingSubmenu.push({
|
spellingSubmenu.push(SEPARATOR)
|
||||||
label: word,
|
for (const word of wordSuggestions) {
|
||||||
click () {
|
spellingSubmenu.push({
|
||||||
// Notify Muya to replace the word. We cannot just use Chromium to
|
label: word,
|
||||||
// replace the word because the change is not forwarded to Muya.
|
click () {
|
||||||
replaceCallback(word)
|
// Notify Muya to replace the word. We cannot just use Chromium to
|
||||||
}
|
// replace the word because the change is not forwarded to Muya.
|
||||||
})
|
replaceCallback(word)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spellingSubmenu.push({
|
spellingSubmenu.push({
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
<bool
|
<bool
|
||||||
description="Whether the experimental spell checker is enabled to check for spelling mistakes."
|
description="Whether the experimental spell checker is enabled to check for spelling mistakes."
|
||||||
:bool="spellcheckerEnabled"
|
:bool="spellcheckerEnabled"
|
||||||
:onChange="value => onSelectChange('spellcheckerEnabled', value)"
|
:onChange="handleSpellcheckerEnabled"
|
||||||
></bool>
|
></bool>
|
||||||
<separator></separator>
|
<separator></separator>
|
||||||
<bool
|
<bool
|
||||||
description="When enabled, Hunspell is used instead the OS spell checker (macOS only). The change take effect after application restart or for new editor windows."
|
description="When enabled, Hunspell is used instead the OS spell checker on macOS and Windows 10. The change take effect after application restart or for new editor windows."
|
||||||
:bool="spellcheckerIsHunspell"
|
:bool="spellcheckerIsHunspell"
|
||||||
:disable="!isOsx || !spellcheckerEnabled"
|
:disable="!isOsSpellcheckerSupported || !spellcheckerEnabled"
|
||||||
:onChange="value => onSelectChange('spellcheckerIsHunspell', value)"
|
:onChange="value => onSelectChange('spellcheckerIsHunspell', value)"
|
||||||
></bool>
|
></bool>
|
||||||
<bool
|
<bool
|
||||||
@ -25,6 +25,7 @@
|
|||||||
:disable="!spellcheckerEnabled"
|
:disable="!spellcheckerEnabled"
|
||||||
:onChange="value => onSelectChange('spellcheckerAutoDetectLanguage', value)"
|
:onChange="value => onSelectChange('spellcheckerAutoDetectLanguage', value)"
|
||||||
></bool>
|
></bool>
|
||||||
|
<separator></separator>
|
||||||
<cur-select
|
<cur-select
|
||||||
description="The default language for spelling."
|
description="The default language for spelling."
|
||||||
:value="spellcheckerLanguage"
|
:value="spellcheckerLanguage"
|
||||||
@ -33,13 +34,18 @@
|
|||||||
:onChange="value => onSelectChange('spellcheckerLanguage', value)"
|
:onChange="value => onSelectChange('spellcheckerLanguage', value)"
|
||||||
></cur-select>
|
></cur-select>
|
||||||
<div
|
<div
|
||||||
v-if="isOsx && spellcheckerIsHunspell"
|
v-if="isOsx && !isHunspellSelected && spellcheckerEnabled"
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
Please add the needed language dictionaries via Language & Region in your system preferences pane.
|
Please add needed language dictionaries via "Language & Region" in your system preferences pane.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="isWindows && !isHunspellSelected && spellcheckerEnabled"
|
||||||
|
class="description"
|
||||||
|
>
|
||||||
|
Please add needed language dictionaries via "Language" in your "Time & language" settings. Add the additional language and download the "Basic typing" language option.
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isHunspellSelected && spellcheckerEnabled">
|
<div v-if="isHunspellSelected && spellcheckerEnabled">
|
||||||
<separator></separator>
|
|
||||||
<div class="description">List of available Hunspell dictionaries. Please add additional language dictionaries via drop-down menu below.</div>
|
<div class="description">List of available Hunspell dictionaries. Please add additional language dictionaries via drop-down menu below.</div>
|
||||||
<el-table
|
<el-table
|
||||||
:data="availableDictionaries"
|
:data="availableDictionaries"
|
||||||
@ -89,9 +95,9 @@ import { mapState } from 'vuex'
|
|||||||
import CurSelect from '../common/select'
|
import CurSelect from '../common/select'
|
||||||
import Bool from '../common/bool'
|
import Bool from '../common/bool'
|
||||||
import Separator from '../common/separator'
|
import Separator from '../common/separator'
|
||||||
import { isOsx } from '@/util'
|
import { isOsx, isLinux, isWindows } from '@/util'
|
||||||
import { getAvailableHunspellDictionaries, SpellChecker } from '@/spellchecker'
|
import { isOsSpellcheckerSupported, getAvailableHunspellDictionaries, SpellChecker } from '@/spellchecker'
|
||||||
import { getLanguageName, HUNSPELL_DICTIONARY_LANGUAGE_MAP } from '@/spellchecker/languageMap.js'
|
import { getLanguageName, HUNSPELL_DICTIONARY_LANGUAGE_MAP } from '@/spellchecker/languageMap'
|
||||||
import { downloadHunspellDictionary, deleteHunspellDictionary } from '@/spellchecker/dictionaryDownloader'
|
import { downloadHunspellDictionary, deleteHunspellDictionary } from '@/spellchecker/dictionaryDownloader'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -102,6 +108,9 @@ export default {
|
|||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
this.isOsx = isOsx
|
this.isOsx = isOsx
|
||||||
|
this.isLinux = isLinux
|
||||||
|
this.isWindows = isWindows
|
||||||
|
this.isOsSpellcheckerSupported = isOsSpellcheckerSupported()
|
||||||
this.dictionariesLanguagesOptions = HUNSPELL_DICTIONARY_LANGUAGE_MAP
|
this.dictionariesLanguagesOptions = HUNSPELL_DICTIONARY_LANGUAGE_MAP
|
||||||
this.hunspellDictionaryDownloadCache = {}
|
this.hunspellDictionaryDownloadCache = {}
|
||||||
return {
|
return {
|
||||||
@ -118,19 +127,14 @@ export default {
|
|||||||
spellcheckerAutoDetectLanguage: state => state.preferences.spellcheckerAutoDetectLanguage,
|
spellcheckerAutoDetectLanguage: state => state.preferences.spellcheckerAutoDetectLanguage,
|
||||||
spellcheckerLanguage: state => state.preferences.spellcheckerLanguage,
|
spellcheckerLanguage: state => state.preferences.spellcheckerLanguage,
|
||||||
isHunspellSelected: state => {
|
isHunspellSelected: state => {
|
||||||
return !isOsx || state.preferences.spellcheckerIsHunspell
|
return !isOsSpellcheckerSupported() || state.preferences.spellcheckerIsHunspell
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
spellcheckerIsHunspell: function (value, oldValue) {
|
spellcheckerIsHunspell: function (value, oldValue) {
|
||||||
if (isOsx && value !== oldValue && value) {
|
if (this.isOsSpellcheckerSupported && value !== oldValue) {
|
||||||
const { spellcheckerLanguage } = this
|
this.ensureDictLanguage(value)
|
||||||
const index = HUNSPELL_DICTIONARY_LANGUAGE_MAP.findIndex(d => d.value === spellcheckerLanguage)
|
|
||||||
if (index === -1) {
|
|
||||||
// Language is not supported by Hunspell.
|
|
||||||
this.onSelectChange('spellcheckerLanguage', 'en-US')
|
|
||||||
}
|
|
||||||
this.refreshDictionaryList()
|
this.refreshDictionaryList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,8 +145,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
if (isOsx && this.spellChecker) {
|
if (!isLinux && this.spellchecker) {
|
||||||
this.spellChecker.provider.unsubscribe()
|
this.spellchecker.provider.unsubscribe()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -152,14 +156,14 @@ export default {
|
|||||||
// Search hunspell dictionaries on disk.
|
// Search hunspell dictionaries on disk.
|
||||||
dictionaries = getAvailableHunspellDictionaries()
|
dictionaries = getAvailableHunspellDictionaries()
|
||||||
} else {
|
} else {
|
||||||
// On macOS we only receive the dictionaries when the spell checker is active.
|
// We only receive the dictionaries from OS spell checker via the instance.
|
||||||
if (!this.spellChecker) {
|
if (!this.spellchecker) {
|
||||||
// Create a new spell checker provider without attach it.
|
// Create a new spell checker provider without attach it.
|
||||||
this.spellChecker = new SpellChecker()
|
this.spellchecker = new SpellChecker()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive available dictionaries from OS.
|
// Receive available dictionaries from OS.
|
||||||
dictionaries = this.spellChecker.getAvailableDictionaries()
|
dictionaries = this.spellchecker.getAvailableDictionaries()
|
||||||
}
|
}
|
||||||
|
|
||||||
return dictionaries.map(item => {
|
return dictionaries.map(item => {
|
||||||
@ -172,6 +176,47 @@ export default {
|
|||||||
refreshDictionaryList () {
|
refreshDictionaryList () {
|
||||||
this.availableDictionaries = this.getAvailableDictionaries()
|
this.availableDictionaries = this.getAvailableDictionaries()
|
||||||
},
|
},
|
||||||
|
ensureDictLanguage (isHunspell) {
|
||||||
|
const { isOsSpellcheckerSupported, spellcheckerLanguage } = this
|
||||||
|
if (isHunspell || !isOsSpellcheckerSupported) {
|
||||||
|
// Validate language for Hunspell.
|
||||||
|
const index = HUNSPELL_DICTIONARY_LANGUAGE_MAP.findIndex(d => d.value === spellcheckerLanguage)
|
||||||
|
if (index === -1) {
|
||||||
|
// Use fallback because language is not supported by Hunspell.
|
||||||
|
this.onSelectChange('spellcheckerLanguage', 'en-US')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Validate language for OS spellchecker. We only receive the dictionaries from
|
||||||
|
// OS spell checker via the instance.
|
||||||
|
if (!this.spellchecker) {
|
||||||
|
// Create a new spell checker provider without attach it.
|
||||||
|
this.spellchecker = new SpellChecker()
|
||||||
|
}
|
||||||
|
|
||||||
|
const dicts = this.spellchecker.getAvailableDictionaries()
|
||||||
|
const index = dicts.findIndex(d => d === spellcheckerLanguage)
|
||||||
|
if (index === -1 && dicts.length >= 1) {
|
||||||
|
// Language is not supported, prefer OS language.
|
||||||
|
var lang = process.env.LANG
|
||||||
|
lang = lang ? lang.split('.')[0] : null
|
||||||
|
if (lang) {
|
||||||
|
lang = lang.replace(/_/g, '-')
|
||||||
|
if (dicts.findIndex(d => d === lang) === -1) {
|
||||||
|
lang = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.onSelectChange('spellcheckerLanguage', lang || dicts[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSpellcheckerEnabled (value) {
|
||||||
|
if (value) {
|
||||||
|
const { spellcheckerIsHunspell } = this
|
||||||
|
this.ensureDictLanguage(spellcheckerIsHunspell)
|
||||||
|
}
|
||||||
|
this.onSelectChange('spellcheckerEnabled', value)
|
||||||
|
},
|
||||||
onSelectChange (type, value) {
|
onSelectChange (type, value) {
|
||||||
this.$store.dispatch('SET_SINGLE_PREFERENCE', { type, value })
|
this.$store.dispatch('SET_SINGLE_PREFERENCE', { type, value })
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import os from 'os'
|
||||||
import { remote } from 'electron'
|
import { remote } from 'electron'
|
||||||
import { SpellCheckHandler, fallbackLocales, normalizeLanguageCode } from '@hfelix/electron-spellchecker'
|
import { SpellCheckHandler, fallbackLocales, normalizeLanguageCode } from '@hfelix/electron-spellchecker'
|
||||||
import { isOsx, cloneObj } from '../util'
|
import { cloneObj, isOsx, isLinux, isWindows } from '@/util'
|
||||||
|
|
||||||
// NOTE: Hardcoded in "@hfelix/electron-spellchecker/src/spell-check-handler.js"
|
// NOTE: Hardcoded in "@hfelix/electron-spellchecker/src/spell-check-handler.js"
|
||||||
export const dictionaryPath = path.join(remote.app.getPath('userData'), 'dictionaries')
|
export const dictionaryPath = path.join(remote.app.getPath('userData'), 'dictionaries')
|
||||||
@ -83,6 +84,25 @@ export const getAvailableHunspellDictionaries = () => {
|
|||||||
return dict
|
return dict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isOsSpellcheckerSupported = () => {
|
||||||
|
let envOverwrite = !!process.env['SPELLCHECKER_PREFER_HUNSPELL'] // eslint-disable-line dot-notation
|
||||||
|
if (isLinux || envOverwrite) {
|
||||||
|
return false
|
||||||
|
} else if (isOsx) {
|
||||||
|
return true
|
||||||
|
} else if (isWindows) {
|
||||||
|
// NOTE: Normally we need to initialize the spellchecker and check the result.
|
||||||
|
const windowsVersion = os.release().match(/^(\d+)\./)
|
||||||
|
if (windowsVersion && windowsVersion[1]) {
|
||||||
|
const windowsMajor = Number(windowsVersion[1])
|
||||||
|
if (windowsMajor >= 10) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High level spell checker API.
|
* High level spell checker API.
|
||||||
*
|
*
|
||||||
@ -98,7 +118,7 @@ export class SpellChecker {
|
|||||||
*/
|
*/
|
||||||
constructor (enabled = true) {
|
constructor (enabled = true) {
|
||||||
// Hunspell is used on Linux and Windows but macOS can use Hunspell if prefered.
|
// Hunspell is used on Linux and Windows but macOS can use Hunspell if prefered.
|
||||||
this.isHunspell = !isOsx || !!process.env['SPELLCHECKER_PREFER_HUNSPELL'] // eslint-disable-line dot-notation
|
this.isHunspell = !isOsSpellcheckerSupported() || !!process.env['SPELLCHECKER_PREFER_HUNSPELL'] // eslint-disable-line dot-notation
|
||||||
|
|
||||||
// Initialize spell check provider. If spell check is not enabled don't
|
// Initialize spell check provider. If spell check is not enabled don't
|
||||||
// initialize the handler to not load the native module.
|
// initialize the handler to not load the native module.
|
||||||
@ -118,6 +138,7 @@ export class SpellChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.provider = new SpellCheckHandler()
|
this.provider = new SpellCheckHandler()
|
||||||
|
this.isHunspell = this.provider.isHunspell
|
||||||
|
|
||||||
// The spell checker is now initialized but not yet enabled. You need to call `init`.
|
// The spell checker is now initialized but not yet enabled. You need to call `init`.
|
||||||
this.isEnabled = false
|
this.isEnabled = false
|
||||||
|
@ -50,7 +50,7 @@ const state = {
|
|||||||
autoSwitchTheme: 2,
|
autoSwitchTheme: 2,
|
||||||
|
|
||||||
spellcheckerEnabled: false,
|
spellcheckerEnabled: false,
|
||||||
spellcheckerIsHunspell: false, // macOS only
|
spellcheckerIsHunspell: false, // macOS/Windows 10 only
|
||||||
spellcheckerNoUnderline: false,
|
spellcheckerNoUnderline: false,
|
||||||
spellcheckerAutoDetectLanguage: false,
|
spellcheckerAutoDetectLanguage: false,
|
||||||
spellcheckerLanguage: 'en-US',
|
spellcheckerLanguage: 'en-US',
|
||||||
|
@ -5,7 +5,11 @@ export const delay = time => {
|
|||||||
let rejectFn
|
let rejectFn
|
||||||
const p = new Promise((resolve, reject) => {
|
const p = new Promise((resolve, reject) => {
|
||||||
rejectFn = reject
|
rejectFn = reject
|
||||||
timerId = setTimeout(resolve, time)
|
timerId = setTimeout(() => {
|
||||||
|
p.cancel = () => {}
|
||||||
|
rejectFn = null
|
||||||
|
resolve()
|
||||||
|
}, time)
|
||||||
})
|
})
|
||||||
|
|
||||||
p.cancel = () => {
|
p.cancel = () => {
|
||||||
|
22
yarn.lock
22
yarn.lock
@ -805,12 +805,12 @@
|
|||||||
electron-is-accelerator "^0.1.0"
|
electron-is-accelerator "^0.1.0"
|
||||||
keyboardevents-areequal "^0.2.1"
|
keyboardevents-areequal "^0.2.1"
|
||||||
|
|
||||||
"@hfelix/electron-spellchecker@^1.0.0-rc.3":
|
"@hfelix/electron-spellchecker@1.0.0-rc.5":
|
||||||
version "1.0.0-rc.3"
|
version "1.0.0-rc.5"
|
||||||
resolved "https://registry.npmjs.org/@hfelix/electron-spellchecker/-/electron-spellchecker-1.0.0-rc.3.tgz#6a76eaf16a4c15987cdda00d42bd9896a0590e70"
|
resolved "https://registry.yarnpkg.com/@hfelix/electron-spellchecker/-/electron-spellchecker-1.0.0-rc.5.tgz#2e7c81857bbdd167aaea45b223a017bd89b19f7a"
|
||||||
integrity sha512-qG2YxRtoOLycA7vRJENds4POxV7VC8yVmDXi7dIz3czRUZgNJNCQDR5YoRQ9xPXVLWT3oYB/RLwOS3RJRP3AnA==
|
integrity sha512-ma5osTva2F4/mF0h6Sbq0BTbMhZI0lBovjd/Ki2WesS9O5tIf75vdFeaZh0NVA7yynNsj5RmhfDTvrKOOg79MQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hfelix/spellchecker" "^4.0.11"
|
"@hfelix/spellchecker" "^4.0.12-rc.2"
|
||||||
bcp47 "^1.1.2"
|
bcp47 "^1.1.2"
|
||||||
cld "^2.5.1"
|
cld "^2.5.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
@ -824,12 +824,12 @@
|
|||||||
resolved "https://registry.npmjs.org/@hfelix/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-1.1.1.tgz#7e1d4fd913759c381b7919cc7faf4c0c641d457c"
|
resolved "https://registry.npmjs.org/@hfelix/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-1.1.1.tgz#7e1d4fd913759c381b7919cc7faf4c0c641d457c"
|
||||||
integrity sha512-1eVkDSqoRQkF2FrPPia2EZ3310c0TvFKYvSuJbaxHpRKbI6eVHcVGKpmOSDli6Qdn3Bu0h7ozfgMZbAEBD+BLQ==
|
integrity sha512-1eVkDSqoRQkF2FrPPia2EZ3310c0TvFKYvSuJbaxHpRKbI6eVHcVGKpmOSDli6Qdn3Bu0h7ozfgMZbAEBD+BLQ==
|
||||||
|
|
||||||
"@hfelix/spellchecker@^4.0.11":
|
"@hfelix/spellchecker@^4.0.12-rc.2":
|
||||||
version "4.0.11"
|
version "4.0.12-rc.2"
|
||||||
resolved "https://registry.npmjs.org/@hfelix/spellchecker/-/spellchecker-4.0.11.tgz#bc86881e419c7e12c88ab996a5bbbe78f044385c"
|
resolved "https://registry.yarnpkg.com/@hfelix/spellchecker/-/spellchecker-4.0.12-rc.2.tgz#3a3630c222e567eca68476a1bce1c919dc452378"
|
||||||
integrity sha512-xBVSHB6OPnqD+KBL5cQO0o20/TIezigD0U1a7V+e+5OvAmCnqdWo23IOdZrUDA7bBhNvK2Pml9jF3U7HRo272A==
|
integrity sha512-FrO+96Di+EiJiib9AiLMXn3PBNg8gayn1r8JBPBM8ME2UwTs8f7GJF6yYCW99a0N8Lqwo13rplLqY6UVG5gEtQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
nan "^2.13.2"
|
nan "^2.14.0"
|
||||||
|
|
||||||
"@markedjs/html-differ@^3.0.0":
|
"@markedjs/html-differ@^3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
@ -8175,7 +8175,7 @@ mute-stream@0.0.8:
|
|||||||
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
||||||
|
|
||||||
nan@2.14.0, nan@^2.12.1, nan@^2.13.2, nan@^2.9.2:
|
nan@2.14.0, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.9.2:
|
||||||
version "2.14.0"
|
version "2.14.0"
|
||||||
resolved "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
resolved "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||||
|
Loading…
Reference in New Issue
Block a user