export type uint8 = number ; export type ClientMessage = { tag : 'CompleteRequest' ; val : CompleteRequest ; } | { tag : 'RunRequest' ; val : RunRequest ; } | { tag : 'KeyEvent' ; val : KeyEvent ; } ; export interface CompleteRequest { id : number ; cwd : string ; input : string ; pos : number ; } export interface CompleteResponse { id : number ; error : string ; pos : number ; completions : string [ ] ; } export interface RunRequest { cell : number ; cwd : string ; argv : string [ ] ; } export interface KeyEvent { cell : number ; keys : string ; } export interface RowSpans { row : number ; spans : Span [ ] ; } export interface Span { attr : number ; text : string ; } export interface Cursor { row : number ; col : number ; hidden : boolean ; } export interface TermUpdate { rows : RowSpans [ ] ; cursor : Cursor ; rowCount : number ; } export interface Pair { key : string ; val : string ; } export interface Hello { alias : Pair [ ] ; env : Pair [ ] ; } export interface CmdError { error : string ; } export interface Exit { exitCode : number ; } export type Output = { tag : 'CmdError' ; val : CmdError ; } | { tag : 'TermUpdate' ; val : TermUpdate ; } | { tag : 'Exit' ; val : Exit ; } ; export interface CellOutput { cell : number ; output : Output ; } export type ServerMsg = { tag : 'Hello' ; val : Hello ; } | { tag : 'CompleteResponse' ; val : CompleteResponse ; } | { tag : 'CellOutput' ; val : CellOutput ; } ; export class Reader { private ofs = 0 ; constructor ( readonly view ) { } private readUint8 ( ) { return this . view . getUint8 ( this . ofs ++ ) ; } private readInt ( ) { let val = 0 ; let shift = 0 ; for ( ; ; ) { const b = this . readUint8 ( ) ; val |= ( b & 0x7f ) << shift ; if ( ( b & 0x80 ) === 0 ) break ; shift += 7 ; } return val ; } private readBoolean ( ) { return this . readUint8 ( ) !== 0 ; } private readBytes ( ) { const len = this . readInt ( ) ; const slice = new DataView ( this . view . buffer , this . ofs , len ) ; this . ofs += len ; return slice ; } private readString ( ) { const bytes = this . readBytes ( ) ; return new TextDecoder ( ) . decode ( bytes ) ; } private readArray < T > ( elem ) { const len = this . readInt ( ) ; const arr = new Array ( len ) ; for ( let i = 0 ; i < len ; i ++ ) { arr [ i ] = elem ( ) ; } return arr ; } readClientMessage ( ) { switch ( this . readUint8 ( ) ) { case 1 : return { tag : 'CompleteRequest' , val : this . readCompleteRequest ( ) } ; case 2 : return { tag : 'RunRequest' , val : this . readRunRequest ( ) } ; case 3 : return { tag : 'KeyEvent' , val : this . readKeyEvent ( ) } ; default : throw new Error ( 'parse error' ) ; } } readCompleteRequest ( ) { return { id : this . readInt ( ) , cwd : this . readString ( ) , input : this . readString ( ) , pos : this . readInt ( ) , } ; } readCompleteResponse ( ) { return { id : this . readInt ( ) , error : this . readString ( ) , pos : this . readInt ( ) , completions : this . readArray ( ( ) => this . readString ( ) ) , } ; } readRunRequest ( ) { return { cell : this . readInt ( ) , cwd : this . readString ( ) , argv : this . readArray ( ( ) => this . readString ( ) ) , } ; } readKeyEvent ( ) { return { cell : this . readInt ( ) , keys : this . readString ( ) , } ; } readRowSpans ( ) { return { row : this . readInt ( ) , spans : this . readArray ( ( ) => this . readSpan ( ) ) , } ; } readSpan ( ) { return { attr : this . readInt ( ) , text : this . readString ( ) , } ; } readCursor ( ) { return { row : this . readInt ( ) , col : this . readInt ( ) , hidden : this . readBoolean ( ) , } ; } readTermUpdate ( ) { return { rows : this . readArray ( ( ) => this . readRowSpans ( ) ) , cursor : this . readCursor ( ) , rowCount : this . readInt ( ) , } ; } readPair ( ) { return { key : this . readString ( ) , val : this . readString ( ) , } ; } readHello ( ) { return { alias : this . readArray ( ( ) => this . readPair ( ) ) , env : this . readArray ( ( ) => this . readPair ( ) ) , } ; } readCmdError ( ) { return { error : this . readString ( ) , } ; } readExit ( ) { return { exitCode : this . readInt ( ) , } ; } readOutput ( ) { switch ( this . readUint8 ( ) ) { case 1 : return { tag : 'CmdError' , val : this . readCmdError ( ) } ; case 2 : return { tag : 'TermUpdate' , val : this . readTermUpdate ( ) } ; case 3 : return { tag : 'Exit' , val : this . readExit ( ) } ; default : throw new Error ( 'parse error' ) ; } } readCellOutput ( ) { return { cell : this . readInt ( ) , output : this . readOutput ( ) , } ; } readServerMsg ( ) { switch ( this . readUint8 ( ) ) { case 1 : return { tag : 'Hello' , val : this . readHello ( ) } ; case 2 : return { tag : 'CompleteResponse' , val : this . readCompleteResponse ( ) } ; case 3 : return { tag : 'CellOutput' , val : this . readCellOutput ( ) } ; default : throw new Error ( 'parse error' ) ; } } } export class Writer { public ofs = 0 ; public buf = new Uint8Array ( ) ; writeBoolean ( val ) { this . writeUint8 ( val ? 1 : 0 ) ; } writeUint8 ( val ) { if ( val > 0xff ) throw new Error ( 'overflow' ) ; this . buf [ this . ofs ++ ] = val ; } writeInt ( val ) { if ( val < 0 ) throw new Error ( 'negative' ) ; for ( ; ; ) { const b = val & 0x7f ; val = val >> 7 ; if ( val === 0 ) { this . writeUint8 ( b ) ; return ; } this . writeUint8 ( b | 0x80 ) ; } } writeString ( str ) { this . writeInt ( str . length ) ; for ( let i = 0 ; i < str . length ; i ++ ) { this . buf [ this . ofs ++ ] = str . charCodeAt ( i ) ; } } writeArray < T > ( arr , f ) { this . writeInt ( arr . length ) ; for ( const elem of arr ) { f ( elem ) ; } } writeClientMessage ( msg ) { switch ( msg . tag ) { case 'CompleteRequest' : this . writeUint8 ( 1 ) ; this . writeCompleteRequest ( msg . val ) ; break ; case 'RunRequest' : this . writeUint8 ( 2 ) ; this . writeRunRequest ( msg . val ) ; break ; case 'KeyEvent' : this . writeUint8 ( 3 ) ; this . writeKeyEvent ( msg . val ) ; break ; } } writeCompleteRequest ( msg ) { this . writeInt ( msg . id ) ; this . writeString ( msg . cwd ) ; this . writeString ( msg . input ) ; this . writeInt ( msg . pos ) ; } writeCompleteResponse ( msg ) { this . writeInt ( msg . id ) ; this . writeString ( msg . error ) ; this . writeInt ( msg . pos ) ; this . writeArray ( msg . completions , ( val ) => { this . writeString ( val ) ; } ) ; } writeRunRequest ( msg ) { this . writeInt ( msg . cell ) ; this . writeString ( msg . cwd ) ; this . writeArray ( msg . argv , ( val ) => { this . writeString ( val ) ; } ) ; } writeKeyEvent ( msg ) { this . writeInt ( msg . cell ) ; this . writeString ( msg . keys ) ; } writeRowSpans ( msg ) { this . writeInt ( msg . row ) ; this . writeArray ( msg . spans , ( val ) => { this . writeSpan ( val ) ; } ) ; } writeSpan ( msg ) { this . writeInt ( msg . attr ) ; this . writeString ( msg . text ) ; } writeCursor ( msg ) { this . writeInt ( msg . row ) ; this . writeInt ( msg . col ) ; this . writeBoolean ( msg . hidden ) ; } writeTermUpdate ( msg ) { this . writeArray ( msg . rows , ( val ) => { this . writeRowSpans ( val ) ; } ) ; this . writeCursor ( msg . cursor ) ; this . writeInt ( msg . rowCount ) ; } writePair ( msg ) { this . writeString ( msg . key ) ; this . writeString ( msg . val ) ; } writeHello ( msg ) { this . writeArray ( msg . alias , ( val ) => { this . writePair ( val ) ; } ) ; this . writeArray ( msg . env , ( val ) => { this . writePair ( val ) ; } ) ; } writeCmdError ( msg ) { this . writeString ( msg . error ) ; } writeExit ( msg ) { this . writeInt ( msg . exitCode ) ; } writeOutput ( msg ) { switch ( msg . tag ) { case 'CmdError' : this . writeUint8 ( 1 ) ; this . writeCmdError ( msg . val ) ; break ; case 'TermUpdate' : this . writeUint8 ( 2 ) ; this . writeTermUpdate ( msg . val ) ; break ; case 'Exit' : this . writeUint8 ( 3 ) ; this . writeExit ( msg . val ) ; break ; } } writeCellOutput ( msg ) { this . writeInt ( msg . cell ) ; this . writeOutput ( msg . output ) ; } writeServerMsg ( msg ) { switch ( msg . tag ) { case 'Hello' : this . writeUint8 ( 1 ) ; this . writeHello ( msg . val ) ; break ; case 'CompleteResponse' : this . writeUint8 ( 2 ) ; this . writeCompleteResponse ( msg . val ) ; break ; case 'CellOutput' : this . writeUint8 ( 3 ) ; this . writeCellOutput ( msg . val ) ; break ; } } }