const acequire = ( require ( 'brace' ) as any ) . acequire ; const { Range } = acequire ( 'ace/range' ) ; import { ENCOUNTERS } from 'app/Encounters' ; import { setWordCount } from './actions/Editor' ; import REGEX from './Regex' ; import { store } from './Store' ; const IGNORE = Object . keys ( ENCOUNTERS ) ; const elementRegexes = new RegExp ( '(' + [ REGEX . HTML_TAG , REGEX . TRIGGER , REGEX . ID , REGEX . OP ] . map ( ( regex ) : string => { return regex . toString ( ) . match ( REGEX . EXTRACT_REGEX ) [ 1 ] ; } ) . join ( '|' ) + ')[^\s]*' , 'gm' ) ; export default class Spellcheck { private contentsModified = true ; private dictionary : any ; private session : any ; private spellchecking = false ; constructor ( session , dictionary ) { this . session = session ; this . dictionary = dictionary ; } public static cleanCorpus ( text ) { text = text . toLowerCase ( ) . replace ( elementRegexes , ' ' ) ; return text ; } public static getWordCount ( text ) { return text . trim ( ) . split ( /\s+/ ) . length ; } public static getUniqueWords ( text ) { return text . replace ( /\n/g , ' ' ) . split ( ' ' ) . filter ( ( s ) : boolean => ( Boolean ( s ) && s . length > 0 ) ) . map ( ( s ) : string => s . replace ( REGEX . NOT_WORD , '' ) ) . filter ( ( s , i , arr ) : boolean => arr . indexOf ( s ) === i ) ; } public onChange ( ) { this . contentsModified = true ; } public spellcheck ( ) { if ( ! this . contentsModified || this . spellchecking ) { return false ; } this . spellchecking = true ; try { for ( const k of Object . keys ( this . session . getMarkers ( true ) ) ) { this . session . removeMarker ( k ) ; } for ( let i = this . session . getDocument ( ) . getLength ( ) - 1 ; i >= 0 ; i -- ) { this . session . removeGutterDecoration ( i , 'misspelled' ) ; } const text = Spellcheck . cleanCorpus ( this . session . getDocument ( ) . getValue ( ) ) ; store . dispatch ( setWordCount ( Spellcheck . getWordCount ( text ) ) ) ; const words = Spellcheck . getUniqueWords ( text ) ; const misspellings = words . filter ( ( word ) : boolean => { return ( ! this . dictionary . check ( word ) && IGNORE . indexOf ( word . toLowerCase ( ) ) === - 1 ) ; } ) ; const misspellingsRegex = new RegExp ( '\\b(' + misspellings . join ( '|' ) + ')\\b' , 'g' ) ; this . session . getDocument ( ) . getAllLines ( ) . forEach ( ( line , i ) => { line = line . replace ( elementRegexes , '' ) ; let match = misspellingsRegex . exec ( line ) ; if ( match && match [ 0 ] !== '' ) { this . session . addGutterDecoration ( i , 'misspelled' ) ; } while ( match && match [ 0 ] !== '' ) { const range = new Range ( i , match . index , i , match . index + match [ 0 ] . length ) ; this . session . addMarker ( range , 'misspelled' , 'typo' , true ) ; match = misspellingsRegex . exec ( line ) ; } } ) ; } finally { this . spellchecking = false ; this . contentsModified = false ; } return true ; } }