import * as execa from 'execa' ; import { ChildProcess } from 'child_process' ; import EventEmitter from '../utils/emitter' ; import { XI_CORE_BIN , XI_CORE_DIR } from '../utils/environment' ; import ViewProxy from './view-proxy' ; import { CoreMethod , CoreResponse } from './types/core' ; import { defineStyle } from './style-map' ; export type CoreOptions = { env ? : { [ key ] : string | undefined ; } ; configDir ? : string ; } ; /**
 * This is a class that manages xi-core. It creates ViewProxies which are simple
 * emitters that link xi-core's internal views with out actual ViewControllers.
 * It is also responsible for encoding/decoding messages to and from xi-core, and
 * managing the spawned process.
 */ export default class Core extends EventEmitter { private child : ChildProcess ; private proxies : { [ key ] : ViewProxy ; } ; param { Object } env constructor ( opts ) { super ( ) ; this . proxies = { } ; this . child = execa ( XI_CORE_BIN , [ ] , { env : opts . env || { } } ) ; this . child . on ( 'close' , this . coreClosed . bind ( this ) ) ; this . stdout ( ) . setEncoding ( 'utf8' ) ; this . stderr ( ) . setEncoding ( 'utf8' ) ; this . stdout ( ) . on ( 'data' , this . eventFromCore . bind ( this ) ) ; this . stderr ( ) . on ( 'data' , this . errorFromCore . bind ( this ) ) ; this . send ( CoreMethod . CLIENT_STARTED , { client_extras_dir : XI_CORE_DIR , config_dir : opts . configDir || XI_CORE_DIR } ) ; } /**
     * Public API
     */ param { CoreMethod } method param { Object } params param { Object } rest return { Boolean } public send ( method , params = { } , rest = { } ) { const data = { method , params , ... rest } ; try { this . stdin ( ) . write ( `${ JSON . stringify ( data ) }\n` ) ; return true ; } catch ( e ) { console . error ( e ) ; return false ; } } public close ( ) { this . child . kill ( ) ; } /**
     * Private API
     */ private stdin ( ) { return this . child . stdin ; } private stdout ( ) { return this . child . stdout ; } private stderr ( ) { return this . child . stderr ; } param { String } data private eventFromCore ( raw ) { parseMessages ( raw ) . forEach ( ( msg ) => { if ( 'result' in msg ) { this . proxies [ msg . result ] = new ViewProxy ( this . proxySend , msg . id , msg . result ) ; this . emit ( 'new_view' , this . proxies [ msg . result ] ) ; return ; } switch ( msg . method ) { case CoreResponse . AVAILABLE_THEMES : { this . send ( CoreMethod . SET_THEME , { theme_name : 'base16-eighties.dark' } ) ; return ; } case CoreResponse . AVAILABLE_PLUGINS : case CoreResponse . CONFIG_CHANGED : case CoreResponse . PLUGIN_STARTED : case CoreResponse . THEME_CHANGED : { return ; } case CoreResponse . DEF_STYLE : { defineStyle ( msg . params ) ; return ; } case CoreResponse . SCROLL_TO : case CoreResponse . UPDATE : { this . proxies [ msg . params . view_id ] . emit ( msg . method , msg . params ) ; return ; } default : { console . warn ( 'Unhandled message from core: ' , msg ) ; } } } ) ; } param { String } data private errorFromCore ( data ) { console . log ( `${ data }` ) ; } param { Number } code param { String } signal private coreClosed ( code , signal ) { console . log ( 'core proc closed: ' , code , signal ) ; } param { CoreMethod } method param { Object } params private proxySend = ( method , params = { } ) : void => { this . send ( method , params ) ; } ; } param { String } string return { Array } function parseMessages ( raw ) { const parsed = [ ] ; const lines = raw . split ( '\n' ) ; for ( let i = 0 ; i < lines . length ; ++ i ) { if ( typeof lines [ i ] !== 'string' || lines [ i ] === '' ) { continue ; } try { parsed . push ( JSON . parse ( lines [ i ] ) ) ; } catch ( err ) { console . warn ( 'Error parsing message from core!' ) ; console . error ( err ) ; } } return parsed ; }