Skip to content

Commit

Permalink
Add code and fill out README
Browse files Browse the repository at this point in the history
  • Loading branch information
wearhere committed Feb 6, 2016
1 parent c027cc3 commit e871dd5
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 1 deletion.
13 changes: 13 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
// If you are using SublimeLinter, after modifying this config file, be sure to close and reopen
// the file(s) you were editing to see the changes take effect.

// Prohibit the use of undeclared variables.
"undef": true,

// Make JSHint aware of Node globals.
"node": true,

// Warn for unused variables and function parameters.
"unused": true
}
147 changes: 146 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,147 @@
# electron-spell-check-provider
Because Electron's spell-check APIs are just a little too low-level.

Electron's [spell-check API][setSpellCheckProvider] looks straightforward… until
you have to pull in and configure `node-spellchecker`:

```diff
webFrame.setSpellCheckProvider('en-US', true, {
spellCheck: function(text) {
+ return !(require('spellchecker').isMisspelled(text));
}
});
```

Which improperly flags contractions:

<img src="docs/false_positive.png" height="300" alt="false positive">

And only underlines misspelled words, leaving you to show suggestions [yourself][DIY context menu]

Or you can use this module:

```js
webFrame.setSpellCheckProvider('en-US', true, new SpellCheckProvider('en-US'));
```

## Installation

```js
npm install electron-spell-check-provider --save
```

_Note:_ this uses a native module, so don't forget to
[rebuild your modules][rebuild] after installing.

## Usage

```js
// In the renderer process:
var webFrame = require('electron').webFrame;
var SpellCheckProvider = require('electron-spell-check-provider');

webFrame.setSpellCheckProvider('en-US', true, new SpellCheckProvider('en-US'));
```

'en-US' is the only supported language at present.

### But how do I show spelling suggestions (in the context menu)?

As the text selection changes, Electron will poll the spell-check provider.
If the current word is misspelled, the provider will emit a `'misspelling'` event
with spelling suggestions:

```js
webFrame.setSpellCheckProvider('en-US', true,
new SpellCheckProvider('en-US').on('misspelling', function(suggestions) {
console.log('The text at the site of the cursor is misspelled.',
'Maybe the user meant to type:', suggestions);
}
}));
```

If you save these suggestions, you can then show them in a [menu][menu] when the
`'contextmenu'` event next fires.

Here's a full implementation that uses
[`electron-editor-context-menu`][electron-editor-context-menu] to build the menu
for you in addition to handling some other gotchas:

```js
/**
* Enables spell-checking and the right-click context menu in text editors.
* Electron (`webFrame.setSpellCheckProvider`) only underlines misspelled words;
* we must manage the menu ourselves.
*
* Run this in the renderer process.
*/
var remote = require('electron').remote;
var webFrame = require('electron').webFrame;
var SpellCheckProvider = require('electron-spell-check-provider');
var buildEditorContextMenu = require('electron-editor-context-menu');

var selection;
function resetSelection() {
selection = {
isMisspelled: false,
spellingSuggestions: []
};
}
resetSelection();

// Reset the selection when clicking around, before the spell-checker runs and the context menu shows.
window.addEventListener('mousedown', resetSelection);

// The spell-checker runs when the user clicks on text and before the 'contextmenu' event fires.
// Thus, we may retrieve spell-checking suggestions to put in the menu just before it shows.
webFrame.setSpellCheckProvider(
'en-US',
// Not sure what this parameter (`autoCorrectWord`) does: https://github.com/atom/electron/issues/4371
// The documentation for `webFrame.setSpellCheckProvider` passes `true` so we do too.
true,
new SpellCheckProvider('en-US').on('misspelling', function(suggestions) {
// Prime the context menu with spelling suggestions _if_ the user has selected text. Electron
// may sometimes re-run the spell-check provider for an outdated selection e.g. if the user
// right-clicks some misspelled text and then an image.
if (window.getSelection().toString()) {
selection.isMisspelled = true;
// Take the first three suggestions if any.
selection.spellingSuggestions = suggestions.slice(0, 3);
}
}));

window.addEventListener('contextmenu', function(e) {
// Only show the context menu in text editors.
if (!e.target.closest('textarea, input, [contenteditable="true"]')) return;

var menu = buildEditorContextMenu(selection);

// The 'contextmenu' event is emitted after 'selectionchange' has fired but possibly before the
// visible selection has changed. Try to wait to show the menu until after that, otherwise the
// visible selection will update after the menu dismisses and look weird.
setTimeout(function() {
menu.popup(remote.getCurrentWindow());
}, 30);
});
```

## Contributing

We welcome pull requests! In particular, we'd love to see support for additional
languages.

Please lint your code.

## Copyright and License

Copyright 2016 Mixmax, Inc., licensed under the MIT License.

[setSpellCheckProvider]: https://github.com/atom/electron/blob/master/docs/api/web-frame.md#webframesetspellcheckproviderlanguage-autocorrectword-provider
[DIY context menu]: https://github.com/atom/electron/pull/942#issuecomment-136223543
[menu]: https://github.com/atom/electron/blob/master/docs/api/menu.md
[electron-editor-context-menu]: https://github.com/mixmaxhq/electron-editor-context-menu
[rebuild]: https://github.com/atom/electron/blob/master/docs/tutorial/using-native-node-modules.md#the-easy-way

## Release History

* 1.0.0 Initial release.

Binary file added docs/false_positive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions electron-spell-check-provider.sublime-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"folders": [{
"path": ".",
"folder_exclude_patterns": [
"node_modules"
]
}],
"settings": {
"tab_size": 2,
"ensure_newline_at_eof_on_save": true,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true,
"detect_indentation": false,
"rulers": [100]
}
}
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "electron-spell-check-provider",
"version": "1.0.0",
"description": "Because Electron's spell-check APIs are just a little too low-level.",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mixmaxhq/electron-spell-check-provider.git"
},
"keywords": [
"electron",
"spellchecker",
"spell",
"checker",
"spell-checker"
],
"author": "Jeff Wear <[email protected]> (https://mixmax.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/mixmaxhq/electron-spell-check-provider/issues"
},
"homepage": "https://github.com/mixmaxhq/electron-spell-check-provider#readme",
"dependencies": {
"spellchecker": "^3.2.1",
"underscore": "^1.8.3"
}
}
36 changes: 36 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var _ = require('underscore');
var EventEmitter = require('events');
var SKIP_WORDS = require('./skipWords');
var spellchecker = require('spellchecker');
var util = require('util');

/**
* Creates a spell-check provider to be passed to `webFrame.setSpellCheckProvider`.
*
* @param {String} language - The language that is being spell-checked. 'en-US'
* is the only supported value at present.
*
* @return {SpellCheckProvider}
*/
var SpellCheckProvider = function(language) {
EventEmitter.call(this);

this._language = language;
};

util.inherits(SpellCheckProvider, EventEmitter);

_.extend(SpellCheckProvider.prototype, {
spellCheck: function(text) {
var skipWords = SKIP_WORDS[this._language];
if (_.contains(skipWords, text)) return true;

var textIsMisspelled = spellchecker.isMisspelled(text);
if (textIsMisspelled) {
this.emit('misspelling', spellchecker.getCorrectionsForMisspelling(text));
}
return !textIsMisspelled;
}
});

module.exports = SpellCheckProvider;
23 changes: 23 additions & 0 deletions src/skipWords.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Words that the spellchecker should not show as errors.
var SKIP_WORDS = {
'en-US': [
// Prevent the spellchecker from showing contractions as errors.
'ain',
'couldn',
'didn',
'doesn',
'hadn',
'hasn',
'mightn',
'mustn',
'needn',
'oughtn',
'shan',
'shouldn',
'wasn',
'weren',
'wouldn'
]
};

module.exports = SKIP_WORDS;

0 comments on commit e871dd5

Please sign in to comment.