@@ -11,9 +11,12 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
1111import iconv from 'iconv-lite'
1212import crypto from 'crypto'
1313import consts from 'browser/lib/consts'
14+ import styles from '../components/CodeEditor.styl'
1415import fs from 'fs'
15- const { ipcRenderer } = require ( 'electron' )
16+ const { ipcRenderer, remote } = require ( 'electron' )
1617import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
18+ const spellcheck = require ( 'browser/lib/spellcheck' )
19+ const buildEditorContextMenu = require ( 'browser/lib/contextMenuBuilder' )
1720import TurndownService from 'turndown'
1821import { gfm } from 'turndown-plugin-gfm'
1922
@@ -30,7 +33,7 @@ export default class CodeEditor extends React.Component {
3033 leading : false ,
3134 trailing : true
3235 } )
33- this . changeHandler = e => this . handleChange ( e )
36+ this . changeHandler = ( editor , changeObject ) => this . handleChange ( editor , changeObject )
3437 this . focusHandler = ( ) => {
3538 ipcRenderer . send ( 'editor:focused' , true )
3639 }
@@ -62,6 +65,12 @@ export default class CodeEditor extends React.Component {
6265 this . scrollToLineHandeler = this . scrollToLine . bind ( this )
6366
6467 this . formatTable = ( ) => this . handleFormatTable ( )
68+ this . contextMenuHandler = function ( editor , event ) {
69+ const menu = buildEditorContextMenu ( editor , event )
70+ if ( menu != null ) {
71+ setTimeout ( ( ) => menu . popup ( remote . getCurrentWindow ( ) ) , 30 )
72+ }
73+ }
6574 this . editorActivityHandler = ( ) => this . handleEditorActivity ( )
6675
6776 this . turndownService = new TurndownService ( )
@@ -232,6 +241,7 @@ export default class CodeEditor extends React.Component {
232241 this . editor . on ( 'blur' , this . blurHandler )
233242 this . editor . on ( 'change' , this . changeHandler )
234243 this . editor . on ( 'paste' , this . pasteHandler )
244+ this . editor . on ( 'contextmenu' , this . contextMenuHandler )
235245 eventEmitter . on ( 'top:search' , this . searchHandler )
236246
237247 eventEmitter . emit ( 'code:init' )
@@ -248,6 +258,10 @@ export default class CodeEditor extends React.Component {
248258
249259 this . textEditorInterface = new TextEditorInterface ( this . editor )
250260 this . tableEditor = new TableEditor ( this . textEditorInterface )
261+ if ( this . props . spellCheck ) {
262+ this . editor . addPanel ( this . createSpellCheckPanel ( ) , { position : 'bottom' } )
263+ }
264+
251265 eventEmitter . on ( 'code:format-table' , this . formatTable )
252266
253267 this . tableEditorOptions = options ( {
@@ -317,22 +331,28 @@ export default class CodeEditor extends React.Component {
317331 const snippetLines = snippets [ i ] . content . split ( '\n' )
318332 let cursorLineNumber = 0
319333 let cursorLinePosition = 0
334+
335+ let cursorIndex
320336 for ( let j = 0 ; j < snippetLines . length ; j ++ ) {
321- const cursorIndex = snippetLines [ j ] . indexOf ( templateCursorString )
337+ cursorIndex = snippetLines [ j ] . indexOf ( templateCursorString )
338+
322339 if ( cursorIndex !== - 1 ) {
323340 cursorLineNumber = j
324341 cursorLinePosition = cursorIndex
325- cm . replaceRange (
326- snippets [ i ] . content . replace ( templateCursorString , '' ) ,
327- wordBeforeCursor . range . from ,
328- wordBeforeCursor . range . to
329- )
330- cm . setCursor ( {
331- line : cursor . line + cursorLineNumber ,
332- ch : cursorLinePosition
333- } )
342+
343+ break
334344 }
335345 }
346+
347+ cm . replaceRange (
348+ snippets [ i ] . content . replace ( templateCursorString , '' ) ,
349+ wordBeforeCursor . range . from ,
350+ wordBeforeCursor . range . to
351+ )
352+ cm . setCursor ( {
353+ line : cursor . line + cursorLineNumber ,
354+ ch : cursorLinePosition + cursor . ch - wordBeforeCursor . text . length
355+ } )
336356 } else {
337357 cm . replaceRange (
338358 snippets [ i ] . content ,
@@ -389,9 +409,11 @@ export default class CodeEditor extends React.Component {
389409 this . editor . off ( 'paste' , this . pasteHandler )
390410 eventEmitter . off ( 'top:search' , this . searchHandler )
391411 this . editor . off ( 'scroll' , this . scrollHandler )
412+ this . editor . off ( 'contextmenu' , this . contextMenuHandler )
392413 const editorTheme = document . getElementById ( 'editorTheme' )
393414 editorTheme . removeEventListener ( 'load' , this . loadStyleHandler )
394415
416+ spellcheck . setLanguage ( null , spellcheck . SPELLCHECK_DISABLED )
395417 eventEmitter . off ( 'code:format-table' , this . formatTable )
396418 }
397419
@@ -459,6 +481,16 @@ export default class CodeEditor extends React.Component {
459481 needRefresh = true
460482 }
461483
484+ if ( prevProps . spellCheck !== this . props . spellCheck ) {
485+ if ( this . props . spellCheck === false ) {
486+ spellcheck . setLanguage ( this . editor , spellcheck . SPELLCHECK_DISABLED )
487+ let elem = document . getElementById ( 'editor-bottom-panel' )
488+ elem . parentNode . removeChild ( elem )
489+ } else {
490+ this . editor . addPanel ( this . createSpellCheckPanel ( ) , { position : 'bottom' } )
491+ }
492+ }
493+
462494 if ( needRefresh ) {
463495 this . editor . refresh ( )
464496 }
@@ -472,10 +504,11 @@ export default class CodeEditor extends React.Component {
472504 CodeMirror . autoLoadMode ( this . editor , syntax . mode )
473505 }
474506
475- handleChange ( e ) {
476- this . value = this . editor . getValue ( )
507+ handleChange ( editor , changeObject ) {
508+ spellcheck . handleChange ( editor , changeObject )
509+ this . value = editor . getValue ( )
477510 if ( this . props . onChange ) {
478- this . props . onChange ( e )
511+ this . props . onChange ( editor )
479512 }
480513 }
481514
@@ -712,6 +745,25 @@ export default class CodeEditor extends React.Component {
712745 />
713746 )
714747 }
748+
749+ createSpellCheckPanel ( ) {
750+ const panel = document . createElement ( 'div' )
751+ panel . className = 'panel bottom'
752+ panel . id = 'editor-bottom-panel'
753+ const dropdown = document . createElement ( 'select' )
754+ dropdown . title = 'Spellcheck'
755+ dropdown . className = styles [ 'spellcheck-select' ]
756+ dropdown . addEventListener ( 'change' , ( e ) => spellcheck . setLanguage ( this . editor , dropdown . value ) )
757+ const options = spellcheck . getAvailableDictionaries ( )
758+ for ( const op of options ) {
759+ const option = document . createElement ( 'option' )
760+ option . value = op . value
761+ option . innerHTML = op . label
762+ dropdown . appendChild ( option )
763+ }
764+ panel . appendChild ( dropdown )
765+ return panel
766+ }
715767}
716768
717769CodeEditor . propTypes = {
@@ -722,7 +774,8 @@ CodeEditor.propTypes = {
722774 className : PropTypes . string ,
723775 onBlur : PropTypes . func ,
724776 onChange : PropTypes . func ,
725- readOnly : PropTypes . bool
777+ readOnly : PropTypes . bool ,
778+ spellCheck : PropTypes . bool
726779}
727780
728781CodeEditor . defaultProps = {
@@ -732,5 +785,6 @@ CodeEditor.defaultProps = {
732785 fontSize : 14 ,
733786 fontFamily : 'Monaco, Consolas' ,
734787 indentSize : 4 ,
735- indentType : 'space'
788+ indentType : 'space' ,
789+ spellCheck : false
736790}
0 commit comments