describe 'AsciiIo.AnsiInterpreter', -> data = interpreter = calls = checkNumber = undefined parse = (data, expectedRest) -> [commands, rest] = interpreter.parse data calls = commands checkNumber = 0 if arguments.length is 2 expect(rest).toEqual expectedRest expectCall = (args...) -> n = checkNumber checkNumber += 1 # console.log calls[n] expect(calls[n]).toEqual args expectNoCall = -> # console.log calls[0] expect(calls.length).toEqual 0 isSwallowed = (d = data) -> parse d, '' expectNoCall() beforeEach -> calls = [] interpreter = new AsciiIo.AnsiInterpreter describe '#parse', -> it 'returns not parsed data back', -> parse '\x1b', '\x1b' expectNoCall() it 'returns empty string if all data was parsed', -> parse 'abc def', '' describe 'C0 set control character', -> # A single character with an ASCII code within the ranges: 000 to 037 and # 200 to 237 octal, 00 - 1F and 80 - 9F hex. describe 'x07', -> it 'calls bell', -> parse '\x07' expectCall 'bell' describe 'x08', -> it 'calls backspace', -> parse '\x08' expectCall 'backspace' describe 'x09', -> it 'calls goToNextHorizontalTabStop', -> parse '\x09' expectCall 'goToNextHorizontalTabStop' describe 'x0a', -> it 'calls lineFeed', -> parse '\x0a' expectCall 'lineFeed' describe 'x0b', -> it 'calls verticalTab', -> parse '\x0b' expectCall 'verticalTab' describe 'x0c', -> it 'calls formFeed', -> parse '\x0c' expectCall 'formFeed' describe 'x0d', -> it 'calls carriageReturn', -> parse '\x0d' expectCall 'carriageReturn' describe 'x84', -> it 'calls index', -> parse '\x84' expectCall 'index' describe 'x85', -> it 'calls newLine', -> parse '\x85' expectCall 'newLine' describe 'x88', -> it 'calls setHorizontalTabStop', -> parse '\x88' expectCall 'setHorizontalTabStop' describe 'x8d', -> it 'calls reverseIndex', -> parse '\x8d' expectCall 'reverseIndex' describe 'other', -> it "is swallowed", -> for c in ['\x00', '\x0e', '\x0f', '\x82', '\x94'] isSwallowed c describe 'printable character', -> describe 'from ASCII range (0x20-0x7e)', -> it 'calls print', -> parse '\x20foobar\x7e', '' expectCall 'print', '\x20foobar\x7e' describe 'from Unicode', -> it 'calls print', -> parse '\xe2ab\xe8ZZ', '' expectCall 'print', '\xe2ab\xe8ZZ' parse '\xc2\x09\xc4b\xc5c', '' expectCall 'print', '\xc2\x09\xc4b\xc5c' parse '\xa1A\xc9\xfe', '' expectCall 'print', '\xa1A\xc9\xfe' describe 'escape sequence', -> # 2 or 3 character string starting with ESCape. (Four or more character # strings are allowed but not defined.) beforeEach -> data = '\x1b' describe 'with C0 control nested inside another escape sequence', -> # C0 Control = 00-1F # Interpret the character, then resume processing the sequence. # Example: CR, LF, XON, and XOFF work as normal within an ESCape # sequence. # it 'stops interpreting current seq and handles nested C0 control char', -> # data += '[1\x1b\x0dm' # parse data, '' # expectCall 'cr' # expectCall 'updateBrush', bright: true describe 'with intermediate', -> # Intermediate = 20-2F !"#$%&'()*+,-./ # Expect zero or more intermediates, a parameter terminates a private # function, an alphabetic terminates a standard sequence. Example: ESC # ( A defines standard character set, ESC ( 0 a DEC set. describe '(', -> beforeEach -> data += '(' describe '0', -> beforeEach -> data += '0' it 'calls setSpecialCharset', -> parse data expectCall 'setSpecialCharset' describe 'A', -> beforeEach -> data += 'A' it 'calls setUkCharset', -> parse data expectCall 'setUkCharset' describe 'B', -> beforeEach -> data += 'B' it 'calls setUsCharset', -> parse data expectCall 'setUsCharset' describe 'followed by parameter', -> # private function describe 'followed by an alphabetic', -> # standard sequence describe 'with parameter', -> # Parameter = 30-3F 0123456789:;<=>? # End of a private 2-character escape sequence. Example: ESC = sets # special keypad mode, ESC > clears it. describe '7', -> beforeEach -> data += '7' it 'saves terminal state', -> parse data expectCall 'saveTerminalState' describe '8', -> beforeEach -> data += '8' it 'restores terminal state', -> parse data expectCall 'restoreTerminalState' describe '=', -> beforeEach -> data += '=' it 'is swallowed', isSwallowed describe '>', -> beforeEach -> data += '>' it 'is swallowed', isSwallowed describe 'with uppercase', -> # Uppercase = 40-5F @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ # Translate it into a C1 control character and act on it. Example: ESC # D does indexes down, ESC M indexes up. (CSI is special) describe 'M', -> # Reverse Index, go up one line, reverse scroll if necessary beforeEach -> data += 'M' it 'goes up 1 line', -> parse data expectCall 'reverseIndex' describe 'P', -> # Device Control String, terminated by ST beforeEach -> data += 'Pfoobar\\' # it 'is swallowed', isSwallowed describe ']', -> # Operating system command describe '0;...BELL', -> beforeEach -> data += ']0;foobar\x07' it 'is swallowed', isSwallowed describe '1;...BELL', -> beforeEach -> data += ']1;foobar\x07' it 'is swallowed', isSwallowed describe '2;...BELL', -> beforeEach -> data += ']2;foobar\x07' it 'is swallowed', isSwallowed describe 'with lowercase', -> # Lowercase = 60-7E `abcdefghijlkmnopqrstuvwxyz{|}~ # End of a standard 2-character escape sequence. Example: ESC c resets # the terminal. describe 'with delete', -> # Delete = 7F # Ignore it, and continue interpreting the ESCape sequence C1 and G1: # Treat the same as their 7-bit counterparts describe 'with control sequence', -> # A string starting with CSI (233 octal, 9B hex) or with ESC[ # (Left-Bracket) and terminated by an alphabetic character. Any number of # parameter characters (digits 0 to 9, semicolon, and question mark) may # appear within the Control Sequence. The terminating character may be # preceded by an intermediate character (such as space). beforeEach -> data += '[' describe '@', -> it 'calls reserveCharacters', -> data += '3@' parse data expectCall 'reserveCharacters', 3 describe 'A', -> it 'calls priorRow(1) if no number given', -> data += 'A' parse data expectCall 'priorRow', 1 it 'calls priorRow(n) if number given', -> data += '3A' parse data expectCall 'priorRow', 3 describe 'B', -> it 'calls nextRow(1) if no number given', -> data += 'B' parse data expectCall 'nextRow', 1 it 'calls nextRow(n) if number given', -> data += '3B' parse data expectCall 'nextRow', 3 describe 'C', -> it 'calls nextColumn(1) if no number given', -> data += 'C' parse data expectCall 'nextColumn', 1 it 'calls nextColumn(n) if number given', -> data += '3C' parse data expectCall 'nextColumn', 3 describe 'D', -> it 'calls priorColumn(1) if no number given', -> data += 'D' parse data expectCall 'priorColumn', 1 it 'calls priorColumn(n) if number given', -> data += '3D' parse data expectCall 'priorColumn', 3 describe 'E', -> it 'calls nextRowFirstColumn(n)', -> data += '3E' parse data expectCall 'nextRowFirstColumn', 3 describe 'F', -> it 'calls priorRowFirstColumn(n)', -> data += '3F' parse data expectCall 'priorRowFirstColumn', 3 describe 'G', -> it 'calls goToColumn(n)', -> data += '3G' parse data expectCall 'goToColumn', 3 describe 'H', -> it 'calls goToRowAndColumn(n, m) when n and m given', -> data += '3;4H' parse data expectCall 'goToRowAndColumn', 3, 4 it 'calls goToRowAndColumn(1, m) when no n given', -> data += ';3H' parse data expectCall 'goToRowAndColumn', 1, 3 it 'calls goToRowAndColumn(n, 1) when no m given', -> data += '3;H' parse data expectCall 'goToRowAndColumn', 3, 1 it 'calls goToRowAndColumn(n, 1) when no m given (no semicolon)', -> data += '3H' parse data expectCall 'goToRowAndColumn', 3, 1 it 'calls goToRowAndColumn(1, 1) when no n nor m given', -> data += 'H' parse data expectCall 'goToRowAndColumn', 1, 1 describe 'I', -> it 'calls goToNextHorizontalTabStop', -> data += '2I' parse data expectCall 'goToNextHorizontalTabStop', 2 describe 'J', -> it 'calls eraseToScreenEnd when no n given', -> data += 'J' parse data expectCall 'eraseToScreenEnd' it 'calls eraseToScreenEnd when 0 given', -> data += '0J' # TODO check if it's 0 or 3 parse data expectCall 'eraseToScreenEnd' it 'calls eraseFromScreenStart when 1 given', -> data += '1J' parse data expectCall 'eraseFromScreenStart' it 'calls eraseScreen when 2 given', -> data += '2J' parse data expectCall 'eraseScreen' describe 'K', -> it 'calls eraseToRowEnd when no n given', -> data += 'K' parse data expectCall 'eraseToRowEnd' it 'calls eraseToRowEnd when 0 given', -> data += '0K' # TODO: check if its 0 or 3 parse data expectCall 'eraseToRowEnd' it 'calls eraseFromRowStart when 1 given', -> data += '1K' parse data expectCall 'eraseFromRowStart' it 'calls eraseRow when 2 given', -> data += '2K' parse data expectCall 'eraseRow' describe 'L', -> it 'calls insertLines(1) when no n given', -> data += 'L' parse data expectCall 'insertLines', 1 it 'calls insertLines(n) when n given', -> data += '3L' parse data expectCall 'insertLines', 3 describe 'M', -> it 'calls deleteLines(1) when no n given', -> data += 'M' parse data expectCall 'deleteLines', 1 it 'calls deleteLines(n) when n given', -> data += '3M' parse data expectCall 'deleteLines', 3 describe 'P', -> it 'calls deleteCharacters(1) when no n given', -> data += 'P' parse data expectCall 'deleteCharacters', 1 it 'calls deleteCharacters(n) when n given', -> data += '3P' parse data expectCall 'deleteCharacters', 3 describe 'S', -> it 'calls scrollUp(n)', -> data += '4S' parse data expectCall 'scrollUp', 4 describe 'T', -> it 'calls scrollDown(n)', -> data += '4T' parse data expectCall 'scrollDown', 4 describe 'X', -> it 'calls eraseCharacters(n)', -> data += '4X' parse data expectCall 'eraseCharacters', 4 describe 'Z', -> it 'calls goToPriorHorizontalTabStop(n)', -> data += '5Z' parse data expectCall 'goToPriorHorizontalTabStop', 5 describe 'b', -> # TODO describe 'c', -> beforeEach -> data += '>c' it 'is swallowed', isSwallowed describe 'd', -> it 'calls goToRow(n)', -> data += '3d' parse data expectCall 'goToRow', 3 describe 'f', -> # TODO describe 'g', -> # TODO describe 'l', -> # TODO describe 'm', -> it 'calls handleSGR([n, m, ...]) when n and m given', -> data += '1;4;33m' spyOn interpreter, 'handleSGR' parse data expect(interpreter.handleSGR).toHaveBeenCalledWith([1, 4, 33]) it 'calls handleSGR([]) when no n nor m given', -> data += 'm' spyOn interpreter, 'handleSGR' parse data expect(interpreter.handleSGR).toHaveBeenCalledWith([]) describe 'n', -> # TODO describe 'r', -> # TODO describe 'from private standards', -> # first character after CSI is one of: " < = > (074-077 octal, 3C-3F ) describe 'DEC/xterm specific', -> describe '$~', -> beforeEach -> data += '$~' it 'is swallowed', isSwallowed describe '?', -> beforeEach -> data += '?' describe '1h', -> beforeEach -> data += '1h' it 'is swallowed', isSwallowed describe '25h', -> beforeEach -> data += '25h' it 'shows cursor', -> parse data expectCall 'showCursor' describe '25l', -> beforeEach -> data += '25l' it 'hides cursor', -> parse data expectCall 'hideCursor' describe '47h', -> beforeEach -> data += '47h' it 'switches to alternate buffer', -> parse data expectCall 'switchToAlternateBuffer' describe '47l', -> beforeEach -> data += '47l' it 'switches to normal buffer', -> parse data expectCall 'switchToNormalBuffer' describe '1049h', -> beforeEach -> data += '1049h' it 'saves cursor position, switches to alternate buffer and clear screen', -> parse data expectCall 'switchToAlternateBuffer' expectCall 'eraseScreen' describe '1049l', -> beforeEach -> data += '1049l' it 'clears screen, switches to normal buffer and restores cursor position', -> parse data expectCall 'eraseScreen' expectCall 'switchToNormalBuffer'