(function () { var electron = require('electron'); var remote = electron.remote; var app = remote.app; var webFrame = electron.webFrame; var path = require('path'); var osLocale = require('os-locale'); var os = require('os'); var semver = require('semver'); var spellchecker = require('spellchecker'); // `remote.require` since `Menu` is a main-process module. var buildEditorContextMenu = remote.require('electron-editor-context-menu'); var EN_VARIANT = /^en/; // Prevent the spellchecker from showing contractions as errors. var ENGLISH_SKIP_WORDS = [ 'ain', 'couldn', 'didn', 'doesn', 'hadn', 'hasn', 'mightn', 'mustn', 'needn', 'oughtn', 'shan', 'shouldn', 'wasn', 'weren', 'wouldn' ]; function setupLinux(locale) { if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { // apt-get install hunspell- can be run for easy access to other dictionaries var location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell'; console.log('Detected Linux. Setting up spell check with locale', locale, 'and dictionary location', location); spellchecker.setDictionary(locale, location); } else { console.log('Detected Linux. Using default en_US spell check dictionary'); } } function setupWin7AndEarlier(locale) { if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { var location = process.env.HUNSPELL_DICTIONARIES; console.log('Detected Windows 7 or below. Setting up spell-check with locale', locale, 'and dictionary location', location); spellchecker.setDictionary(locale, location); } else { console.log('Detected Windows 7 or below. Using default en_US spell check dictionary'); } } // We load locale this way and not via app.getLocale() because this call returns // 'es_ES' and not just 'es.' And hunspell requires the fully-qualified locale. var locale = osLocale.sync().replace('-', '_'); // The LANG environment variable is how node spellchecker finds its default language: // https://github.com/atom/node-spellchecker/blob/59d2d5eee5785c4b34e9669cd5d987181d17c098/lib/spellchecker.js#L29 if (!process.env.LANG) { process.env.LANG = locale; } if (process.platform === 'linux') { setupLinux(locale); } else if (process.platform === 'windows' && semver.lt(os.release(), '8.0.0')) { setupWin7AndEarlier(locale); } else { // OSX and Windows 8+ have OS-level spellcheck APIs console.log('Using OS-level spell check API with locale', process.env.LANG); } var simpleChecker = window.spellChecker = { spellCheck: function(text) { return !this.isMisspelled(text); }, isMisspelled: function(text) { var misspelled = spellchecker.isMisspelled(text); // The idea is to make this as fast as possible. For the many, many calls which // don't result in the red squiggly, we minimize the number of checks. if (!misspelled) { return false; } // Only if we think we've found an error do we check the locale and skip list. if (locale.match(EN_VARIANT) && _.contains(ENGLISH_SKIP_WORDS, text)) { return false; } return true; }, getSuggestions: function(text) { return spellchecker.getCorrectionsForMisspelling(text); }, add: function(text) { spellchecker.add(text); } }; 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, simpleChecker ); window.addEventListener('contextmenu', function(e) { // Only show the context menu in text editors. if (!e.target.closest('textarea, input, [contenteditable="true"]')) { return; } var selectedText = window.getSelection().toString(); var isMisspelled = selectedText && simpleChecker.isMisspelled(selectedText); var spellingSuggestions = isMisspelled && simpleChecker.getSuggestions(selectedText).slice(0, 5); var menu = buildEditorContextMenu({ isMisspelled: isMisspelled, spellingSuggestions: spellingSuggestions, }); // 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); }); })();