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:
Ben Gotow 2016-03-14 10:23:57 -07:00
parent a3483c0a2b
commit fc59d88337
4 changed files with 70 additions and 54 deletions

View file

@ -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;

View file

@ -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", ()=> {

View file

@ -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()

View file

@ -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