refactor. clean up. more colors.

This commit is contained in:
Christopher Jeffrey 2013-02-07 12:19:49 -06:00
parent 04f4145524
commit 1428b7137b
1 changed files with 273 additions and 118 deletions

View File

@ -44,36 +44,38 @@ Program.prototype.term = function(is) {
};
Program.prototype.listen = function() {
if (!this.input.isTTY || !this.output.isTTY) return;
if (!this.input.isTTY || !this.output.isTTY) {
return;
}
var readline = require('readline')
, self = this;
this._raw = true;
if (!this.input.isRaw) {
this.input.setRawMode(true);
this.input.setRawMode(true);
this.input.on('keypress', function(ch, key) {
key = key || 0;
if (key.ctrl && key.name === 'c') {
if (process.listeners('SIGINT').length) {
process.emit('SIGINT');
this.input.on('keypress', function(ch, key) {
key = key || 0;
if (key.ctrl && key.name === 'c') {
if (process.listeners('SIGINT').length) {
process.emit('SIGINT');
}
if (self.listeners('SIGINT').length) {
self.emit('SIGINT');
}
return;
}
if (self.listeners('SIGINT').length) {
self.emit('SIGINT');
}
return;
}
self.emit('keypress', ch, key);
});
self.emit('keypress', ch, key);
});
this.input.on('data', function(data) {
self.emit('data', data);
});
this.input.on('data', function(data) {
self.emit('data', data);
});
readline.emitKeypressEvents(this.input);
readline.emitKeypressEvents(this.input);
this.input.resume();
this.input.resume();
}
this.output.on('resize', function() {
self.cols = self.output.columns;
@ -81,18 +83,15 @@ Program.prototype.listen = function() {
self.emit('resize');
});
// this.getCursor(function(err, cursor) {
// if (err) return;
// self.x = cursor.x;
// self.y = cursor.y;
// });
this.on('newListener', function fn(type) {
if (type === 'mouse') {
self.removeListener('newListener', fn);
self.bindMouse();
}
});
// self.bindMouse();
// self.bindResponse();
};
// XTerm mouse events
@ -142,7 +141,7 @@ Program.prototype._bindMouse = function(s) {
}
// XTerm / X10
if (parts = /^\x1b\[M([\x00\u0020-\xffff]{3})/.exec(s)) {
if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) {
var b = parts[1].charCodeAt(0)
, x = parts[1].charCodeAt(1)
, y = parts[1].charCodeAt(2)
@ -352,6 +351,7 @@ Program.prototype._bindMouse = function(s) {
}
};
// All possible responses from the terminal
Program.prototype.bindResponse = function() {
this.on('data', this._bindResponse.bind(this));
this.bindResponse = function() {};
@ -435,33 +435,18 @@ Program.prototype._bindResponse = function(s) {
: 'unavailable'
});
}
return this.emit('response', {
error: 'Unhandled: ' + JSON.stringify(parts)
});
}
// CSI Ps n Device Status Report (DSR).
// Ps = 5 -> Status Report. Result (``OK'') is
// CSI 0 n
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
// Result is
// CSI r ; c R
// CSI ? Ps n
// Device Status Report (DSR, DEC-specific).
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
// ? r ; c R (assumes page is zero).
// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
// or CSI ? 1 1 n (not ready).
// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
// or CSI ? 2 1 n (locked).
// Ps = 2 6 -> Report Keyboard status as
// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
// The last two parameters apply to VT400 & up, and denote key-
// board ready and LK01 respectively.
// Ps = 5 3 -> Report Locator status as
// CSI ? 5 3 n Locator available, if compiled-in, or
// CSI ? 5 0 n No Locator, if not.
// CSI Ps n Device Status Report (DSR).
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
// Result is
// CSI r ; c R
// CSI ? Ps n
// Device Status Report (DSR, DEC-specific).
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
// ? r ; c R (assumes page is zero).
if (parts = /^\x1b\[(\?)?(\d+);(\d+)R/.exec(s)) {
@ -534,6 +519,10 @@ Program.prototype._bindResponse = function(s) {
}
});
}
return this.emit('response', {
error: 'Unhandled: ' + JSON.stringify(parts)
});
}
// CSI Ps ; Ps ; Ps t
@ -557,14 +546,19 @@ Program.prototype._bindResponse = function(s) {
windowTitle: parts[2]
});
}
return this.emit('response', {
error: 'Unhandled: ' + JSON.stringify(parts)
});
}
};
Program.prototype.receive = function(text, callback) {
var listeners = this.listeners('keypress')
, bak = listeners.slice();
, bak = listeners.slice()
, self = this;
if (!this._raw) {
if (!this.input.isRaw) {
throw new Error('Input must be raw.');
}
@ -575,26 +569,53 @@ Program.prototype.receive = function(text, callback) {
text = null;
}
this.input.once('data', function(data) {
listeners.push.apply(listeners, bak);
if (typeof data !== 'string') {
data = data.toString('utf8');
}
return callback(null, data);
});
return process.nextTick(function() {
self.input.once('data', function(data) {
listeners.push.apply(listeners, bak);
if (typeof data !== 'string') {
data = data.toString('utf8');
}
return callback(null, data);
});
if (text) this.write(text);
if (text) self.write(text);
});
};
Program.prototype.receive_ = function(text, callback) {
var listeners = this.listeners('keypress')
, bak = listeners.slice()
, self = this;
if (!this.input.isRaw) {
throw new Error('Input must be raw.');
}
listeners.length = 0;
if (!callback) {
callback = text;
text = null;
}
return process.nextTick(function() {
self.input.once('data', function(data) {
listeners.push.apply(listeners, bak);
self.once('response', function(event) {
return callback(null, event[Object.keys(event)[0]]);
});
self._bindResponse(data);
});
if (text) self.write(text);
});
};
Program.prototype.write =
Program.prototype.echo = function(text, attr) {
if (attr) {
this.attr(attr, true);
this.write(text);
this.attr(attr, false);
return;
}
return this.output.write(text);
return attr
? this.output.write(this.text(text, attr))
: this.output.write(text);
};
Program.prototype.setx = function(x) {
@ -1041,6 +1062,25 @@ Program.prototype.eraseInLine = function(param) {
Program.prototype.sgr =
Program.prototype.attr =
Program.prototype.charAttributes = function(param, val) {
return this.write(this._attr(param, val));
};
Program.prototype.text = function(text, attr) {
return this._attr(attr, true) + text + this._attr(attr, false);
};
Program.prototype._attr = function(param, val) {
var self = this
, parts = param.split(/\s*[,;]\s*/)
, out = '';
if (parts.length > 1) {
parts.forEach(function(part) {
out += self._attr(part, val);
});
return out;
}
if (param.indexOf('no ') === 0) {
param = param.substring(3);
val = false;
@ -1048,118 +1088,233 @@ Program.prototype.charAttributes = function(param, val) {
param = param.substring(1);
val = false;
}
switch (param) {
// attributes
case 'normal':
return this.write('\x1b[m');
return '\x1b[m';
case 'bold':
return val === false
? this.write('\x1b[22m')
: this.write('\x1b[1m');
? '\x1b[22m'
: '\x1b[1m';
case 'underlined':
return val === false
? this.write('\x1b[24m')
: this.write('\x1b[4m');
? '\x1b[24m'
: '\x1b[4m';
case 'blink':
return val === false
? this.write('\x1b[25m')
: this.write('\x1b[5m');
? '\x1b[25m'
: '\x1b[5m';
case 'inverse':
return val === false
? this.write('\x1b[27m')
: this.write('\x1b[7m');
? '\x1b[27m'
: '\x1b[7m';
break;
case 'invisible':
return val === false
? this.write('\x1b[28m')
: this.write('\x1b[8m');
? '\x1b[28m'
: '\x1b[8m';
case 'invisible':
return val === false
? this.write('\x1b[28m')
: this.write('\x1b[8m');
? '\x1b[28m'
: '\x1b[8m';
// 8-color foreground
case 'black fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[30m');
? '\x1b[39m'
: '\x1b[30m';
case 'red fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[31m');
? '\x1b[39m'
: '\x1b[31m';
case 'green fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[32m');
return val === false
? '\x1b[39m'
: '\x1b[32m';
case 'yellow fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[33m');
? '\x1b[39m'
: '\x1b[33m';
case 'blue fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[34m');
? '\x1b[39m'
: '\x1b[34m';
case 'magenta fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[35m');
? '\x1b[39m'
: '\x1b[35m';
case 'cyan fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[36m');
? '\x1b[39m'
: '\x1b[36m';
case 'white fg':
return val === false
? this.write('\x1b[39m')
: this.write('\x1b[37m');
? '\x1b[39m'
: '\x1b[37m';
case 'default fg':
return this.write('\x1b[39m');
return '\x1b[39m';
// 8-color background
case 'black bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[40m');
? '\x1b[49m'
: '\x1b[40m';
case 'red bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[41m');
? '\x1b[49m'
: '\x1b[41m';
case 'green bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[42m');
? '\x1b[49m'
: '\x1b[42m';
case 'yellow bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[43m');
? '\x1b[49m'
: '\x1b[43m';
case 'blue bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[44m');
? '\x1b[49m'
: '\x1b[44m';
case 'magenta bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[45m');
? '\x1b[49m'
: '\x1b[45m';
case 'cyan bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[46m');
? '\x1b[49m'
: '\x1b[46m';
case 'white bg':
return val === false
? this.write('\x1b[49m')
: this.write('\x1b[47m');
? '\x1b[49m'
: '\x1b[47m';
case 'default bg':
return this.write('\x1b[49m');
return '\x1b[49m';
// 16-color foreground
case 'light black fg':
return val === false
? '\x1b[39m'
: '\x1b[90m';
case 'light red fg':
return val === false
? '\x1b[39m'
: '\x1b[91m';
case 'light green fg':
return val === false
? '\x1b[39m'
: '\x1b[92m';
case 'light yellow fg':
return val === false
? '\x1b[39m'
: '\x1b[93m';
case 'light blue fg':
return val === false
? '\x1b[39m'
: '\x1b[94m';
case 'light magenta fg':
return val === false
? '\x1b[39m'
: '\x1b[95m';
case 'light cyan fg':
return val === false
? '\x1b[39m'
: '\x1b[96m';
case 'light white fg':
return val === false
? '\x1b[39m'
: '\x1b[97m';
// 16-color background
case 'light black bg':
return val === false
? '\x1b[49m'
: '\x1b[100m';
case 'light red bg':
return val === false
? '\x1b[49m'
: '\x1b[101m';
case 'light green bg':
return val === false
? '\x1b[49m'
: '\x1b[102m';
case 'light yellow bg':
return val === false
? '\x1b[49m'
: '\x1b[103m';
case 'light blue bg':
return val === false
? '\x1b[49m'
: '\x1b[104m';
case 'light magenta bg':
return val === false
? '\x1b[49m'
: '\x1b[105m';
case 'light cyan bg':
return val === false
? '\x1b[49m'
: '\x1b[106m';
case 'light white bg':
return val === false
? '\x1b[49m'
: '\x1b[107m';
// non-16-color rxvt default fg and bg
case 'default fg bg':
if (this.term('rxvt')) {
return '\x1b[100m';
}
return this._attr('default fg') + this._attr('default bg');
default:
return this.write('\x1b[' + param + 'm');
// 256-color fg and bg
var m = /^(\d+) (fg|bg)$/.exec(param);
if (m) {
var color = +m[1] - 1;
if (val === false) {
return this._attr('default ' + m[2]);
}
if (color < 16) {
if (m[2] === 'fg') {
if (color < 8) {
color += 30;
} else if (color < 16) {
color += 90;
}
} else if (m[2] === 'bg') {
if (color < 8) {
color += 40;
} else if (color < 16) {
color += 100;
}
}
return '\x1b[' + color + 'm';
}
if (m[2] === 'fg') {
return '\x1b[38;5;' + color + 'm';
}
if (m[2] === 'bg') {
return '\x1b[48;5;' + color + 'm';
}
}
return '\x1b[' + param + 'm';
}
};
Program.prototype.fg =
Program.prototype.setForeground = function(color, val) {
return this.attr(color + ' fg', val);
color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
return this.attr(color, val);
};
Program.prototype.bg =
Program.prototype.setBackground = function(color, val) {
return this.attr(color + ' bg', val);
color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
return this.attr(color, val);
};
// CSI Ps n Device Status Report (DSR).