mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
fix(spellcheck): Enables spellcheck menu for basic inputs (#1600)
Summary: Just moves some code so we can easily attach spelling menus to the basic inputs. Test Plan: Updated existing tests Reviewers: juan, drew Reviewed By: drew Differential Revision: https://phab.nylas.com/D2721
This commit is contained in:
parent
a3483c0a2b
commit
fc59d88337
4 changed files with 70 additions and 54 deletions
|
@ -1,58 +1,27 @@
|
|||
import {DOMUtils, ComposerExtension, NylasSpellchecker} from 'nylas-exports';
|
||||
import {remote} from 'electron';
|
||||
const MenuItem = remote.require('menu-item');
|
||||
|
||||
const SpellcheckCache = {};
|
||||
|
||||
export default class SpellcheckComposerExtension extends ComposerExtension {
|
||||
|
||||
static isMisspelled(word) {
|
||||
if (SpellcheckCache[word] === undefined) {
|
||||
SpellcheckCache[word] = NylasSpellchecker.isMisspelled(word);
|
||||
}
|
||||
return SpellcheckCache[word];
|
||||
}
|
||||
|
||||
static onContentChanged({editor}) {
|
||||
SpellcheckComposerExtension.update(editor);
|
||||
}
|
||||
|
||||
static onShowContextMenu = ({editor, menu})=> {
|
||||
static onShowContextMenu = ({editor, menu}) => {
|
||||
const selection = editor.currentSelection();
|
||||
const range = DOMUtils.Mutating.getRangeAtAndSelectWord(selection, 0);
|
||||
const word = range.toString();
|
||||
|
||||
if (SpellcheckComposerExtension.isMisspelled(word)) {
|
||||
const corrections = NylasSpellchecker.getCorrectionsForMisspelling(word);
|
||||
if (corrections.length > 0) {
|
||||
corrections.forEach((correction)=> {
|
||||
menu.append(new MenuItem({
|
||||
label: correction,
|
||||
click: SpellcheckComposerExtension.applyCorrection.bind(SpellcheckComposerExtension, editor, range, selection, correction),
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
menu.append(new MenuItem({ label: 'No Guesses Found', enabled: false}));
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
menu.append(new MenuItem({
|
||||
label: 'Learn Spelling',
|
||||
click: SpellcheckComposerExtension.learnSpelling.bind(SpellcheckComposerExtension, editor, word),
|
||||
}));
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
}
|
||||
}
|
||||
|
||||
static applyCorrection = (editor, range, selection, correction)=> {
|
||||
DOMUtils.Mutating.applyTextInRange(range, selection, correction);
|
||||
SpellcheckComposerExtension.update(editor);
|
||||
}
|
||||
|
||||
static learnSpelling = (editor, word)=> {
|
||||
NylasSpellchecker.add(word);
|
||||
delete SpellcheckCache[word];
|
||||
SpellcheckComposerExtension.update(editor);
|
||||
NylasSpellchecker.appendSpellingItemsToMenu({
|
||||
menu,
|
||||
word,
|
||||
onCorrect: (correction) => {
|
||||
DOMUtils.Mutating.applyTextInRange(range, selection, correction);
|
||||
SpellcheckComposerExtension.update(editor);
|
||||
},
|
||||
onDidLearn: () => {
|
||||
SpellcheckComposerExtension.update(editor);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static update = (editor) => {
|
||||
|
@ -141,7 +110,7 @@ export default class SpellcheckComposerExtension extends ComposerExtension {
|
|||
break;
|
||||
}
|
||||
|
||||
if (SpellcheckComposerExtension.isMisspelled(match[0])) {
|
||||
if (NylasSpellchecker.isMisspelled(match[0])) {
|
||||
// The insertion point is currently at the end of this misspelled word.
|
||||
// Do not mark it until the user types a space or leaves.
|
||||
if ((selectionSnapshot.focusNode === node) && (selectionSnapshot.focusOffset === match.index + match[0].length)) {
|
||||
|
@ -190,5 +159,3 @@ export default class SpellcheckComposerExtension extends ComposerExtension {
|
|||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
SpellcheckComposerExtension.SpellcheckCache = SpellcheckCache;
|
||||
|
|
|
@ -4,6 +4,7 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
|
||||
import SpellcheckComposerExtension from '../lib/spellcheck-composer-extension';
|
||||
import {NylasSpellchecker} from 'nylas-exports';
|
||||
|
||||
const initialHTML = fs.readFileSync(path.join(__dirname, 'fixtures', 'california-with-misspellings-before.html')).toString();
|
||||
const expectedHTML = fs.readFileSync(path.join(__dirname, 'fixtures', 'california-with-misspellings-after.html')).toString();
|
||||
|
@ -12,7 +13,7 @@ describe("SpellcheckComposerExtension", ()=> {
|
|||
beforeEach(()=> {
|
||||
// Avoid differences between node-spellcheck on different platforms
|
||||
const spellings = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'california-spelling-lookup.json')));
|
||||
spyOn(SpellcheckComposerExtension, 'isMisspelled').andCallFake(word=> spellings[word])
|
||||
spyOn(NylasSpellchecker, 'isMisspelled').andCallFake(word=> spellings[word])
|
||||
});
|
||||
|
||||
describe("update", ()=> {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
path = require('path')
|
||||
|
||||
spellchecker = require('spellchecker')
|
||||
{remote} = require('electron')
|
||||
MenuItem = remote.require('menu-item')
|
||||
|
||||
class NylasSpellchecker
|
||||
constructor: ->
|
||||
@languageAvailable = false
|
||||
@cache = {}
|
||||
|
||||
lang = navigator.language
|
||||
@setLanguage(lang)
|
||||
|
@ -63,15 +65,19 @@ class NylasSpellchecker
|
|||
dict
|
||||
|
||||
#### spellchecker methods ####
|
||||
setDictionary: (lang, dict) => @setLanguage(lang, dict)
|
||||
setDictionary: (lang, dict) =>
|
||||
@setLanguage(lang, dict)
|
||||
|
||||
add: spellchecker.add
|
||||
add: (word) =>
|
||||
delete @cache[word]
|
||||
spellchecker.add(word)
|
||||
|
||||
isMisspelled: (text) =>
|
||||
isMisspelled: (word) =>
|
||||
if @languageAvailable
|
||||
spellchecker.isMisspelled(text)
|
||||
@cache[word] ?= spellchecker.isMisspelled(word)
|
||||
@cache[word]
|
||||
else
|
||||
return false
|
||||
false
|
||||
|
||||
getAvailableDictionaries: ->
|
||||
if process.platform is 'linux'
|
||||
|
@ -87,6 +93,28 @@ class NylasSpellchecker
|
|||
spellchecker.getCorrectionsForMisspelling(args...)
|
||||
else return []
|
||||
|
||||
appendSpellingItemsToMenu: ({menu, word, onCorrect, onDidLearn}) =>
|
||||
if @isMisspelled(word)
|
||||
corrections = @getCorrectionsForMisspelling(word)
|
||||
if corrections.length > 0
|
||||
corrections.forEach (correction) =>
|
||||
menu.append(new MenuItem({
|
||||
label: correction,
|
||||
click: -> onCorrect(correction)
|
||||
}))
|
||||
else
|
||||
menu.append(new MenuItem({ label: 'No Guesses Found', enabled: false}))
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }))
|
||||
menu.append(new MenuItem({
|
||||
label: 'Learn Spelling',
|
||||
click: =>
|
||||
@add(word)
|
||||
onDidLearn?(word)
|
||||
}))
|
||||
menu.append(new MenuItem({ type: 'separator' }))
|
||||
|
||||
|
||||
Spellchecker: => @
|
||||
|
||||
module.exports = new NylasSpellchecker
|
||||
module.exports = new NylasSpellchecker()
|
||||
|
|
|
@ -202,10 +202,30 @@ class WindowEventHandler
|
|||
return unless event.target.type in ['text', 'password', 'email', 'number', 'range', 'search', 'tel', 'url']
|
||||
hasSelectedText = event.target.selectionStart isnt event.target.selectionEnd
|
||||
|
||||
if hasSelectedText
|
||||
wordStart = event.target.selectionStart
|
||||
wordEnd = event.target.selectionEnd
|
||||
else
|
||||
wordStart = event.target.value.lastIndexOf(" ", event.target.selectionStart)
|
||||
wordStart = 0 if wordStart is -1
|
||||
wordEnd = event.target.value.indexOf(" ", event.target.selectionStart)
|
||||
wordEnd = event.target.value.length if wordEnd is -1
|
||||
word = event.target.value.substr(wordStart, wordEnd - wordStart)
|
||||
|
||||
{remote} = require('electron')
|
||||
Menu = remote.require('menu')
|
||||
MenuItem = remote.require('menu-item')
|
||||
menu = new Menu()
|
||||
|
||||
NylasSpellchecker = require('./nylas-spellchecker')
|
||||
NylasSpellchecker.appendSpellingItemsToMenu
|
||||
menu: menu,
|
||||
word: word,
|
||||
onCorrect: (correction) =>
|
||||
insertionPoint = wordStart + correction.length
|
||||
event.target.value = event.target.value.replace(word, correction)
|
||||
event.target.setSelectionRange(insertionPoint, insertionPoint)
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: 'Cut'
|
||||
enabled: hasSelectedText
|
||||
|
|
Loading…
Reference in a new issue