UNPKG

@inkdropapp/codemirror-spell-checker

Version:
117 lines (91 loc) 3.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = SpellChecker; exports.initTypo = initTypo; exports.loadDictionary = loadDictionary; var _nodeFetch = _interopRequireDefault(require("node-fetch")); var _typoJs = _interopRequireDefault(require("typo-js")); var _fs = _interopRequireDefault(require("fs")); var _path = _interopRequireDefault(require("path")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const dataDir = _path.default.join(__dirname, 'data'); const baseDicUrl = 'https://raw.githubusercontent.com/titoBouzout/Dictionaries/master/'; function SpellChecker(CodeMirror) { // Verify if (typeof CodeMirror !== 'function' || typeof CodeMirror.defineMode !== 'function') { throw new Error('You must provide a class of CodeMirror'); } CodeMirror.defineOption('spellCheckLang', undefined, async function (cm, newVal) { if (newVal) { try { CodeMirror.signal(cm, 'spell-checker:dictionary-loading', newVal); SpellChecker.typo = await initTypo(newVal); CodeMirror.signal(cm, 'spell-checker:dictionary-loaded', newVal); } catch (e) { console.error('Failed to init Typo:', e); CodeMirror.signal(cm, 'spell-checker:error', e); } } }); CodeMirror.defineMode('spell-checker', function (config) { // Define what separates a word const rx_word = '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ '; const nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af -ー]/; // Create the overlay and such const overlay = { token: function (stream) { var ch; var word = ''; var ignore = false; var state = stream.lineOracle.state; while ((ch = stream.peek()) != null && (rx_word.includes(ch) || nonASCIISingleCaseWordChar.test(ch))) { ignore = true; stream.next(); state = stream.lineOracle.state; } if (ignore) return null; while ((ch = stream.peek()) != null && !rx_word.includes(ch) && !nonASCIISingleCaseWordChar.test(ch)) { word += ch; stream.next(); state = stream.lineOracle.state; if (state.base.codeblock || state.base.indentedCode || state.base.code === 1 || typeof state.baseCur === 'string' && (state.baseCur.indexOf('url') >= 0 || state.baseCur.indexOf('string') >= 0)) { return null; } } if (SpellChecker.typo && word.length > 0 && !SpellChecker.typo.check(word)) return 'spell-error'; // CSS class: cm-spell-error return null; } }; const mode = CodeMirror.getMode(config, config.backdrop || 'text/plain'); return CodeMirror.overlayMode(mode, overlay, true); }); } async function initTypo(lang) { const data = await loadDictionary(lang); return new _typoJs.default(lang, data.aff, data.dic, { platform: 'any' }); } async function loadDictionary(lang) { const affPath = _path.default.join(dataDir, `${lang}.aff`); const dicPath = _path.default.join(dataDir, `${lang}.dic`); const data = {}; if (!_fs.default.existsSync(affPath)) { const url = `${baseDicUrl}/${lang}.aff`; const res = await (0, _nodeFetch.default)(url); data.aff = await res.text(); _fs.default.writeFileSync(affPath, data.aff); } else { data.aff = _fs.default.readFileSync(affPath, 'utf-8'); } if (!_fs.default.existsSync(dicPath)) { const url = `${baseDicUrl}/${lang}.dic`; const res = await (0, _nodeFetch.default)(url); data.dic = await res.text(); _fs.default.writeFileSync(dicPath, data.dic); } else { data.dic = _fs.default.readFileSync(dicPath, 'utf-8'); } return data; }