UNPKG

terminal.js

Version:

terminal emulation library for javascript.

284 lines (245 loc) 7.17 kB
"use strict"; var myUtil = require("./util.js"); function getChanged(oldObj, newObj) { var result = {}; var i; if(newObj instanceof Array) { for(i = 0; i < newObj.length || i < oldObj.length; i++) { if(newObj[i] !== oldObj[i]) result[i] = newObj[i]; } } else { for(i in newObj) { if(newObj[i] !== oldObj[i]) result[i] = newObj[i]; } } return result; } function diffAttr(attr1, attr2) { var a, p, i, j; a = Object.keys(attr1); for(i = 0; i < a.length; i++) { if(!attr2[a[i]]) return false; p = Object.keys(attr1[a[i]]); for(j = 0; j < p.length; j++) { if(attr1[a[i]][p[j]] !== attr2[a[i]][p[j]]) return false; } } return true; } function TermDiff(oldState, newState) { this._changes = []; this._cursor = null; this._scrollRegion = null; this._savedCursor = null; this._modes = null; this._leds = null; this._size = null; this._tabs = null; this._columns = null; this._rows = null; if(typeof oldState === "object" && oldState.getLine) { this.oldState = oldState; this.newState = newState; this._mkDiff(oldState, newState); this._mkCursor(oldState, newState); this._mkScrollRegion(oldState, newState); this._mkModes(oldState, newState); this._mkLeds(oldState, newState); this._mkSize(oldState, newState); this._mkTabs(oldState, newState); } else if(typeof oldState === "string") { var json = JSON.parse(oldState); this._loadJson(json); } else { this._loadJson(oldState); } } module.exports = TermDiff; TermDiff.prototype._mkCursor = function(oldState, newState){ if(oldState.cursor.x !== newState.cursor.x || oldState.cursor.y !== newState.cursor.y) this._cursor = myUtil.extend({}, newState.cursor); if(oldState._savedCursor.x !== newState._savedCursor.x || oldState._savedCursor.y !== newState._savedCursor.y) this._savedCursor = myUtil.extend({}, newState._savedCursor); }; TermDiff.prototype._mkScrollRegion = function(oldState, newState){ this._scrollRegion = newState._scrollRegion.slice(); }; TermDiff.prototype._mkModes = function(oldState, newState){ this._modes = getChanged(oldState._modes, newState._modes); }; TermDiff.prototype._mkLeds = function(oldState, newState){ this._leds = getChanged(oldState._leds, newState._leds); }; TermDiff.prototype._mkSize = function(oldState, newState){ if(oldState.columns !== newState.columns || oldState.rows !== newState.rows) { this._rows = newState.rows; this._columns = newState.columns; } }; TermDiff.prototype._mkTabs = function(oldState, newState){ this._tabs = newState._tabs.slice(); }; TermDiff.prototype._getChange = function(line) { var l = {l: line}; for(var i = this._changes.length - 1; i >= 0; i--) { if(this._changes[i].l === line) return this._changes[i]; else if(this._changes[i].l < line) { this._changes.splice(i+1, 0, l); return l; } } this._changes.unshift(l); return l; }; TermDiff.prototype._cmpLines = function(line1, line2) { var i, j, a, p; if(line1 === line2) return true; else if(line1 === undefined || line2 === undefined) return false; else if(line1.str !== line2.str) return false; return diffAttr(line1.attr, line2.attr) && diffAttr(line2.attr, line1.attr); }; TermDiff.prototype._mkDiff = function(oldState, newState) { var m = Math.max(1, oldState.getBufferRowCount()), n = Math.max(1, newState.getBufferRowCount()); var left = -1, up = -m, diag = left + up; var seq = new Array(m * n); var dir = seq.slice(0); var i,j,k,l, toJ, toK; for(i = 0; i < seq.length; i++) { j = i % m; k = ~~(i / m); // Cast to int var hasDiffs = this._cmpLines(oldState.getLine(j), newState.getLine(k)); if(hasDiffs) dir[i] = diag; else if(seq[i + left] <= seq[i + up]) dir[i] = up; else dir[i] = left; seq[i] = ~~(diag === dir[i]) + ~~(j === 0 ? 0 : seq[i + dir[i]]); } k = n-1; j = m-1; for(i = seq.length - 1; i >= 0; j--, k--, i+=dir[i]) { // Goto next common line for(; !isNaN(i) && dir[i] !== diag; i += dir[i]); toJ = i % m; toK = ~~(i / m); // Cast to int if(isNaN(i)) toJ = toK = -1; // changed or inserted for(; k > toK; j = Math.max(j-1, toJ), k--) { this._getChange(k)[j > toJ ? "." : "+"] = newState.getLine(k); } // line is in old, but not in new for(; j > toJ; j--) { l = this._getChange(toK+1); l["-"] = (l["-"] || 0) + 1; } if(j === 0 && (dir[i] === diag || dir[i] === left)) dir[i] = up; } }; TermDiff.prototype.toJSON = function() { return { changes: this._changes, cursor: this._cursor, savedCursor: this._savedCursor, leds: this._leds, modes: this._modes, size: this._size, tabs: this._tabs, scrollRegion: this._scrollRegion }; }; TermDiff.prototype.toString = function() { var i,j; var result = []; var lastline = 0; var oldNbr = this._changes[0] ? this._changes[0].l : 0; for(i = 0; i < this._changes.length; i++, lastline++, oldNbr++) { for(; lastline < this._changes[i].l; lastline++, oldNbr++) { result.push(" " + this.newState.getLine(lastline).str); } for(j = 0; j < this._changes[i]["-"]; j++) { result.push("-" + this.oldState.getLine(oldNbr).str); } if(this._changes[i]["+"]) { result.push("+" + this._changes[i]["+"].str); oldNbr--; } if(this._changes[i]["."]) { result.push("." + this._changes[i]["."].str); } } return result.join("\n"); }; TermDiff.prototype._loadJson = function(diff) { this._cursor = diff.cursor; this._savedCursor = diff.savedCursor; this._scrollRegion = diff.scrollRegion; this._modes = diff.modes; this._leds = diff.leds; this._rows = diff.rows; this._columns = diff.columns; this._changes = diff.changes; this._tabs = diff.tabs; }; TermDiff.prototype.apply = function(diff) { if(this._columns || this._rows) this._applySize(diff); if(this._cursor) this._applyCursor(diff); if(this._scrollRegion) this._applyScrollRegion(diff); if(this._leds) this._applyLeds(diff); if(this._tabs) this._applyTabs(diff); if(this._savedCursor)this._applySavedCursor(diff); if(this._modes) this._applyModes(diff); if(this._changes) this._applyChanges(diff); }; TermDiff.prototype._applySize = function(t) { t.resize({columns: this._columns, rows: this._rows }); }; TermDiff.prototype._applyCursor = function(t) { t.setCursor(this._cursor.x, this._cursor.y); }; TermDiff.prototype._applyScrollRegion = function(t) { t.setScrollRegion(this._scrollRegion[0], this._scrollRegion[1]); }; TermDiff.prototype._applyLeds = function(t) { for(var k in this._leds) t.setLed(k, this._leds[k]); }; TermDiff.prototype._applySavedCursor = function(t) { t._savedCursor.x = this._savedCursor.x; t._savedCursor.y = this._savedCursor.y; }; TermDiff.prototype._applyTabs = function(t) { t.tabs = this._tabs.slice(); }; TermDiff.prototype._applyModes = function(t) { for (var m in this._modes) { t.setMode(m,this._modes[m]); } }; TermDiff.prototype._applyChanges = function(t) { for(var i = 0; i < this._changes.length; i++) { var c = this._changes[i]; if (c["-"]) t._removeLine(c.l, c["-"]); // removing lines if (c["+"]) t._insertLine(c.l, c["+"]); // adding lines else if (c["."]) t.setLine(c.l, c["."]); // replacing lines } };