Improvements to interpreter and screen buffer

openid
Marcin Kulik 13 years ago
parent a591423e3b
commit a378358abf

@ -78,6 +78,7 @@ class AsciiIo.AnsiInterpreter
})
PATTERNS:
"\x00": (data) ->
"\x07": (data) -> @sb.bell()
"\x08": (data) -> @sb.backspace()
"\x09": (data) -> # Moves the cursor to the next tab stop
@ -145,10 +146,10 @@ class AsciiIo.AnsiInterpreter
# Save cursor position, switch to alternate screen buffer, and clear screen.
@sb.saveCursor()
@sb.switchToAlternateBuffer()
@sb.clearScreen()
@sb.clear()
else if action is "l"
# Clear screen, switch to normal screen buffer, and restore cursor position.
@sb.clearScreen()
@sb.clear()
@sb.switchToNormalBuffer()
@sb.restoreCursor()
else
@ -165,7 +166,7 @@ class AsciiIo.AnsiInterpreter
"\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
"\x1bM": ->
@sb.ri @n or 1
@sb.ri()
"\x1b\x37": (data) -> # save cursor pos and char attrs
@sb.saveCursor()
@ -269,4 +270,4 @@ class AsciiIo.AnsiInterpreter
break unless match
data
data.length is 0

@ -11,6 +11,9 @@ class AsciiIo.ScreenBuffer
setBrush: (brush) ->
@brush = brush
clear: ->
@lineData.length = 0
getLine: (n) ->
n = (if typeof n isnt "undefined" then n else @cursorY)
@ -108,9 +111,6 @@ class AsciiIo.ScreenBuffer
@cursorLeft()
@updateLine()
bell: ->
# nothing to do
print: (text) ->
text = Utf8.decode(text)
@ -174,7 +174,7 @@ class AsciiIo.ScreenBuffer
@lineData[@cursorY] = line.slice(0, @cursorX).concat(" ".times(n).split(""), line.slice(@cursorX, @cols - n))
@updateLine()
ri: (n) ->
ri: (n=1) ->
i = 0
while i < n
if @cursorY is 0

@ -3,6 +3,11 @@ describe AsciiIo.AnsiInterpreter, ->
cols = 80
lines = 24
isSwallowed = (d) ->
screenBuffer = {} # will throw 'undefined is not a function'
interpreter = new AsciiIo.AnsiInterpreter(screenBuffer)
expect(interpreter.feed(d || data)).toEqual(true)
beforeEach ->
screenBuffer = new AsciiIo.ScreenBuffer(cols, lines)
interpreter = new AsciiIo.AnsiInterpreter(screenBuffer)
@ -13,15 +18,6 @@ describe AsciiIo.AnsiInterpreter, ->
# 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 'x00', ->
beforeEach ->
screenBuffer = {} # will throw 'undefined is not a function'
interpreter = new AsciiIo.AnsiInterpreter(screenBuffer)
it 'is ignored', ->
data += '\x00'
interpreter.feed(data)
describe 'x07', ->
it 'calls bell', ->
data += '\x07'
@ -50,6 +46,12 @@ describe AsciiIo.AnsiInterpreter, ->
interpreter.feed(data)
expect(screenBuffer.cr).toHaveBeenCalled()
describe 'other', ->
it "is swallowed", ->
for c in ['\x00', '\x09', '\x0e', '\x0f', '\x82', '\x94']
isSwallowed(c)
describe 'printable character', ->
describe 'from ASCII range (0x20-0x7e)', ->
it 'calls print', ->
@ -80,8 +82,11 @@ describe AsciiIo.AnsiInterpreter, ->
beforeEach ->
data += '\x1b'
describe 'with ESC + control character inside', ->
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 'aaa', ->
# data += '[1\x1b\x0dm'
@ -91,7 +96,126 @@ describe AsciiIo.AnsiInterpreter, ->
# expect(screenBuffer.cr).toHaveBeenCalled()
# expect(screenBuffer.setSGR).toHaveBeenCalledWith([1])
describe 'control sequence', ->
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 'B', ->
beforeEach ->
data += 'B'
it 'is swallowed', isSwallowed
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 cursor', ->
spyOn screenBuffer, 'saveCursor'
interpreter.feed(data)
expect(screenBuffer.saveCursor).toHaveBeenCalled()
describe '8', ->
beforeEach ->
data += '8'
it 'restores cursor', ->
spyOn screenBuffer, 'restoreCursor'
interpreter.feed(data)
expect(screenBuffer.restoreCursor).toHaveBeenCalled()
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', ->
spyOn screenBuffer, 'ri'
interpreter.feed(data)
expect(screenBuffer.ri).toHaveBeenCalled()
describe 'P', ->
# Device Control String, terminated by ST
beforeEach ->
data += 'Pfoobar\\'
it 'is swallowed', isSwallowed
describe ']', ->
# Operating system command
beforeEach ->
screenBuffer = {} # will throw 'undefined is not a function'
interpreter = new AsciiIo.AnsiInterpreter(screenBuffer)
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
@ -102,11 +226,11 @@ describe AsciiIo.AnsiInterpreter, ->
data += '['
describe 'buffering', ->
it 'allows parsing in chunks', ->
spyOn interpreter, 'handleSGR'
interpreter.feed(data)
interpreter.feed('m')
expect(interpreter.handleSGR).toHaveBeenCalled()
# it 'allows parsing in chunks', ->
# spyOn interpreter, 'handleSGR'
# interpreter.feed(data)
# interpreter.feed('m')
# expect(interpreter.handleSGR).toHaveBeenCalled()
describe '@', ->
it 'calls reserveCharacters', ->
@ -290,6 +414,95 @@ describe AsciiIo.AnsiInterpreter, ->
interpreter.feed(data)
expect(screenBuffer.deleteCharacter).toHaveBeenCalledWith(3)
describe 'c', ->
beforeEach ->
data += '>c'
it 'is swallowed', isSwallowed
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', ->
spyOn screenBuffer, 'showCursor'
interpreter.feed(data)
expect(screenBuffer.showCursor).toHaveBeenCalledWith(true)
describe '25l', ->
beforeEach ->
data += '25l'
it 'hides cursor', ->
spyOn screenBuffer, 'showCursor'
interpreter.feed(data)
expect(screenBuffer.showCursor).toHaveBeenCalledWith(false)
describe '47h', ->
beforeEach ->
data += '47h'
it 'switches to alternate buffer', ->
spyOn screenBuffer, 'switchToAlternateBuffer'
interpreter.feed(data)
expect(screenBuffer.switchToAlternateBuffer).toHaveBeenCalled()
describe '47l', ->
beforeEach ->
data += '47l'
it 'switches to normal buffer', ->
spyOn screenBuffer, 'switchToNormalBuffer'
interpreter.feed(data)
expect(screenBuffer.switchToNormalBuffer).toHaveBeenCalled()
describe '1049h', ->
beforeEach ->
data += '1049h'
it 'saves cursor position, switches to alternate buffer and clear screen', ->
spyOn screenBuffer, 'saveCursor'
spyOn screenBuffer, 'switchToAlternateBuffer'
spyOn screenBuffer, 'clear'
interpreter.feed(data)
expect(screenBuffer.saveCursor).toHaveBeenCalled()
expect(screenBuffer.switchToAlternateBuffer).toHaveBeenCalled()
expect(screenBuffer.clear).toHaveBeenCalled()
describe '1049l', ->
beforeEach ->
data += '1049l'
it 'clears screen, switches to normal buffer and restores cursor position', ->
spyOn screenBuffer, 'clear'
spyOn screenBuffer, 'switchToNormalBuffer'
spyOn screenBuffer, 'restoreCursor'
interpreter.feed(data)
expect(screenBuffer.clear).toHaveBeenCalled()
expect(screenBuffer.switchToNormalBuffer).toHaveBeenCalled()
expect(screenBuffer.restoreCursor).toHaveBeenCalled()
describe '#handleSGR', ->
numbers = undefined

Loading…
Cancel
Save