Skip to content

Commit aef2c78

Browse files
committed
Bundle our own spellchecker
Signed-off-by: Yukai Huang <[email protected]>
1 parent 023161d commit aef2c78

File tree

5 files changed

+121
-3
lines changed

5 files changed

+121
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
"standard": "~13.1.0",
189189
"string-loader": "~0.0.1",
190190
"style-loader": "~0.23.1",
191+
"typo-js": "^1.0.3",
191192
"uglifyjs-webpack-plugin": "~1.2.7",
192193
"url-loader": "~1.0.1",
193194
"webpack": "~4.39.0",

public/js/lib/editor/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import config from './config'
77
import statusBarTemplate from './statusbar.html'
88
import toolBarTemplate from './toolbar.html'
99
import './markdown-lint'
10+
import CodeMirrorSpellChecker from './spellcheck'
1011
import { initTableEditor } from './table-editor'
1112
import { availableThemes } from './constants'
1213

@@ -723,6 +724,8 @@ export default class Editor {
723724
placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)"
724725
})
725726

727+
// eslint-disable-next-line
728+
new CodeMirrorSpellChecker(CodeMirror)
726729
this.tableEditor = initTableEditor(this.editor)
727730

728731
return this.editor

public/js/lib/editor/spellcheck.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/* eslint-env browser */
2+
3+
// Modified from https://github.com/sparksuite/codemirror-spell-checker
4+
5+
import Typo from 'typo-js'
6+
import { serverurl } from '../config'
7+
8+
const dictionaryDownloadUrls = {
9+
en_US: {
10+
aff: `${serverurl}/vendor/codemirror-spell-checker/en_US.aff`,
11+
dic: `${serverurl}/vendor/codemirror-spell-checker/en_US.dic`
12+
}
13+
}
14+
15+
class CodeMirrorSpellChecker {
16+
/**
17+
* @param {CodeMirror} cm
18+
* @param {string} lang
19+
*/
20+
constructor (cm, lang = 'en_US') {
21+
// Verify
22+
if (typeof cm !== 'function' || typeof cm.defineMode !== 'function') {
23+
console.log(
24+
'CodeMirror Spell Checker: You must provide an instance of CodeMirror via the option `codeMirrorInstance`'
25+
)
26+
return
27+
}
28+
29+
this.numLoaded = 0
30+
this.affLoading = false
31+
this.dicLoading = false
32+
this.affData = ''
33+
this.dicData = ''
34+
this.typo = undefined
35+
36+
this.setupCM.bind(this)(cm, lang)
37+
}
38+
39+
setupCM (cm, lang) {
40+
cm.defineMode('spell-checker', config => {
41+
// Load AFF/DIC data
42+
if (!this.affLoading) {
43+
this.affLoading = true
44+
45+
const xhrAff = new XMLHttpRequest()
46+
xhrAff.open('GET', dictionaryDownloadUrls[lang].aff, true)
47+
xhrAff.onload = () => {
48+
if (xhrAff.readyState === 4 && xhrAff.status === 200) {
49+
this.affData = xhrAff.responseText
50+
this.numLoaded++
51+
52+
if (this.numLoaded === 2) {
53+
this.typo = new Typo(lang, this.affData, this.dicData, { platform: 'any' })
54+
}
55+
}
56+
}
57+
xhrAff.send(null)
58+
}
59+
60+
if (!this.dicLoading) {
61+
this.dicLoading = true
62+
const xhrDic = new XMLHttpRequest()
63+
xhrDic.open('GET', dictionaryDownloadUrls[lang].dic, true)
64+
xhrDic.onload = () => {
65+
if (xhrDic.readyState === 4 && xhrDic.status === 200) {
66+
this.dicData = xhrDic.responseText
67+
this.numLoaded++
68+
69+
if (this.numLoaded === 2) {
70+
this.typo = new Typo(lang, this.affData, this.dicData, { platform: 'any' })
71+
}
72+
}
73+
}
74+
xhrDic.send(null)
75+
}
76+
77+
// Define what separates a word
78+
const regexWord = '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ '
79+
80+
// Create the overlay and such
81+
const overlay = {
82+
token: (stream) => {
83+
let ch = stream.peek()
84+
let word = ''
85+
86+
if (regexWord.includes(ch)) {
87+
stream.next()
88+
return null
89+
}
90+
91+
while ((ch = stream.peek()) != null && !regexWord.includes(ch)) {
92+
word += ch
93+
stream.next()
94+
}
95+
96+
if (this.typo && !this.typo.check(word)) {
97+
return 'spell-error' // CSS class: cm-spell-error
98+
}
99+
100+
return null
101+
}
102+
}
103+
104+
const mode = cm.getMode(config, config.backdrop || 'text/plain')
105+
106+
return cm.overlayMode(mode, overlay, true)
107+
})
108+
}
109+
}
110+
111+
// Export
112+
export default CodeMirrorSpellChecker

webpack.common.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ module.exports = {
203203
'script-loader!codemirror',
204204
'script-loader!inlineAttachment',
205205
'script-loader!jqueryTextcomplete',
206-
'script-loader!codemirrorSpellChecker',
207206
'script-loader!codemirrorInlineAttachment',
208207
'script-loader!ot',
209208
'flowchart.js',
@@ -259,7 +258,6 @@ module.exports = {
259258
'script-loader!codemirror',
260259
'script-loader!inlineAttachment',
261260
'script-loader!jqueryTextcomplete',
262-
'script-loader!codemirrorSpellChecker',
263261
'script-loader!codemirrorInlineAttachment',
264262
'script-loader!ot',
265263
'flowchart.js',
@@ -371,7 +369,6 @@ module.exports = {
371369
codemirror: path.join(__dirname, 'node_modules/@hackmd/codemirror/codemirror.min.js'),
372370
inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'),
373371
jqueryTextcomplete: path.join(__dirname, 'public/vendor/jquery-textcomplete/jquery.textcomplete.js'),
374-
codemirrorSpellChecker: path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.js'),
375372
codemirrorInlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/codemirror.inline-attachment.js'),
376373
ot: path.join(__dirname, 'public/vendor/ot/ot.min.js'),
377374
mermaid: path.join(__dirname, 'node_modules/mermaid/dist/mermaid.min.js'),

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12495,6 +12495,11 @@ typedarray@^0.0.6, typedarray@~0.0.5:
1249512495
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1249612496
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
1249712497

12498+
typo-js@^1.0.3:
12499+
version "1.0.3"
12500+
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a"
12501+
integrity sha1-VNjrx5SfGngQkItgAsaEFSbJnVo=
12502+
1249812503
uc.micro@^1.0.1, uc.micro@^1.0.5:
1249912504
version "1.0.6"
1250012505
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"

0 commit comments

Comments
 (0)