Display ANSI in file area desc view

This commit is contained in:
Bryan Ashby 2017-08-06 11:31:21 -06:00
parent 9cb31367fd
commit 0a079ee4d8
2 changed files with 81 additions and 115 deletions

View File

@ -21,7 +21,7 @@ exports.renderStringLength = renderStringLength;
exports.formatByteSizeAbbr = formatByteSizeAbbr; exports.formatByteSizeAbbr = formatByteSizeAbbr;
exports.formatByteSize = formatByteSize; exports.formatByteSize = formatByteSize;
exports.cleanControlCodes = cleanControlCodes; exports.cleanControlCodes = cleanControlCodes;
exports.createCleanAnsi = createCleanAnsi; exports.prepAnsi = prepAnsi;
// :TODO: create Unicode verison of this // :TODO: create Unicode verison of this
const VOWELS = [ 'a', 'e', 'i', 'o', 'u' ]; const VOWELS = [ 'a', 'e', 'i', 'o', 'u' ];
@ -373,48 +373,35 @@ function cleanControlCodes(input, options) {
return cleaned; return cleaned;
} }
function createCleanAnsi(input, options, cb) { function prepAnsi(input, options, cb) {
if(!input) { if(!input) {
return cb(''); return cb(null, '');
} }
options.width = options.width || 80; options.termWidth = options.termWidth || 80;
options.height = options.height || 25; options.termHeight = options.termHeight || 25;
const canvas = new Array(options.height); options.cols = options.cols || options.termWidth || 80;
for(let i = 0; i < options.height; ++i) { options.rows = options.rows || options.termHeight || 25;
canvas[i] = new Array(options.width);
for(let j = 0; j < options.width; ++j) {
canvas[i][j] = {};
}
}
const parserOpts = { options.startCol = options.startCol || 1;
termHeight : options.height,
termWidth : options.width,
};
const parser = new ANSIEscapeParser(parserOpts); const canvas = Array.from( { length : options.rows }, () => Array.from( { length : options.cols}, () => new Object() ) );
const parser = new ANSIEscapeParser( { termHeight : options.termHeight, termWidth : options.termWidth } );
const canvasPos = { const state = {
col : 0,
row : 0, row : 0,
col : 0,
}; };
let sgr; let lastRow = 0;
function ensureCell() { parser.on('position update', (row, col) => {
// we've pre-allocated a matrix, but allow for > supplied dimens up front. They will be trimmed @ finalize state.row = row - 1;
if(!canvas[canvasPos.row]) { state.col = col - 1;
canvas[canvasPos.row] = new Array(options.width);
for(let j = 0; j < options.width; ++j) { lastRow = Math.max(state.row, lastRow);
canvas[canvasPos.row][j] = {}; });
}
}
canvas[canvasPos.row][canvasPos.col] = canvas[canvasPos.row][canvasPos.col] || {};
//canvas[canvasPos.row][0].width = Math.max(canvas[canvasPos.row][0].width || 0, canvasPos.col);
}
parser.on('literal', literal => { parser.on('literal', literal => {
// //
@ -422,99 +409,83 @@ function createCleanAnsi(input, options, cb) {
// //
literal = literal.replace(/\r?\n|[\r\u2028\u2029]/g, ''); literal = literal.replace(/\r?\n|[\r\u2028\u2029]/g, '');
for(let i = 0; i < literal.length; ++i) { for(let c of literal) {
const c = literal.charAt(i); if(state.col < options.cols && state.row < options.rows) {
canvas[state.row][state.col].char = c;
ensureCell(); if(state.sgr) {
canvas[state.row][state.col].sgr = state.sgr;
canvas[canvasPos.row][canvasPos.col].char = c; state.sgr = null;
}
if(sgr) {
canvas[canvasPos.row][canvasPos.col].sgr = sgr;
sgr = null;
} }
canvasPos.col += 1; state.col += 1;
} }
}); });
parser.on('control', (match, opCode) => { parser.on('control', (match, opCode) => {
if('m' !== opCode) { //
return; // don't care' // Movement is handled via 'position update', so we really only care about
// display opCodes
//
switch(opCode) {
case 'm' :
state.sgr = (state.sgr || '') + match;
break;
default :
if(-1 === [ 'C' ].indexOf(opCode)) {
console.log(`ignore opCode: ${opCode}`); // :TODO: REMOVE ME
}
} }
sgr = match;
}); });
parser.on('position update', (row, col) => { function getLastPopulatedColumn(row) {
canvasPos.row = row - 1; let col = row.length;
canvasPos.col = Math.min(col - 1, options.width); while(--col > 0) {
}); if(row[col].char || row[col].sgr) {
break;
}
}
return col;
}
parser.on('complete', () => { parser.on('complete', () => {
for(let row = 0; row < options.height; ++row) { let output = '';
let col = 0; let lastSgr = '';
canvas.slice(0, lastRow + 1).forEach(row => {
const lastCol = getLastPopulatedColumn(row) + 1;
//while(col <= canvas[row][0].width) { let i;
while(col < options.width) { for(i = 0; i < lastCol; ++i) {
if(!canvas[row][col].char) { const col = row[i];
canvas[row][col].char = ' '; if(col.sgr) {
if(!canvas[row][col].sgr) { lastSgr = col.sgr;
// :TODO: fix duplicate SGR's in a row here - we just need one per sequence
canvas[row][col].sgr = ANSI.reset();
} }
output += `${col.sgr || ''}${col.char || ' '}`;
} }
col += 1; if(i < row.length) {
output += `${ANSI.blackBG()}${row.slice(i).map( () => ' ').join('')}${lastSgr}`;
} }
// :TODO: end *all* with CRLF - general usage will be width : 79 - prob update defaults if(options.startCol + options.cols < options.termWidth || options.forceLineTerm) {
output += '\r\n';
if(col <= options.width) {
canvas[row][col] = canvas[row][col] || {};
canvas[row][col].char = '\r\n';
canvas[row][col].sgr = ANSI.reset();
// :TODO: don't splice, just reset + fill with ' ' till end
for(let fillCol = col; fillCol <= options.width; ++fillCol) {
canvas[row][fillCol].char = ' ';
} }
});
//canvas[row] = canvas[row].splice(0, col + 1); return cb(null, output);
//canvas[row][options.width - 1].char = '\r\n';
} else {
canvas[row] = canvas[row].splice(0, options.width + 1);
}
}
let out = '';
for(let row = 0; row < options.height; ++row) {
out += canvas[row].map( col => {
let c = col.sgr || '';
c += col.char;
return c;
}).join('');
}
// :TODO: finalize: @ any non-char cell, reset sgr & set to ' '
// :TODO: finalize: after sgr established, omit anything > supplied dimens
return cb(out);
}); });
parser.parse(input); parser.parse(input);
} }
// create2dArray = (rows, columns) => [...Array(rows).keys()].map(i => Array(columns).fill({}))
/* /*
const fs = require('graceful-fs'); const fs = require('graceful-fs');
let data = fs.readFileSync('/home/nuskooler/Downloads/art3.ans'); //let data = fs.readFileSync('/home/nuskooler/Downloads/art3.ans');
//let data = fs.readFileSync('/home/nuskooler/dev/enigma-bbs/mods/themes/nu-xibalba/MATRIX1.ANS');
let data = fs.readFileSync('/home/nuskooler/Downloads/ansi_diz_test/file_id.diz.2.ans');
data = iconv.decode(data, 'cp437'); data = iconv.decode(data, 'cp437');
createCleanAnsi(data, { width : 79, height : 25 }, (out) => { prepAnsi(data, { cols : 45, rows : 25 }, (err, out) => {
out = iconv.encode(out, 'cp437'); out = iconv.encode(out, 'cp437');
fs.writeFileSync('/home/nuskooler/Downloads/art4.ans', out); fs.writeFileSync('/home/nuskooler/Downloads/art4.ans', out);
}); });

View File

@ -8,7 +8,6 @@ const ansi = require('../core/ansi_term.js');
const theme = require('../core/theme.js'); const theme = require('../core/theme.js');
const FileEntry = require('../core/file_entry.js'); const FileEntry = require('../core/file_entry.js');
const stringFormat = require('../core/string_format.js'); const stringFormat = require('../core/string_format.js');
const createCleanAnsi = require('../core/string_util.js').createCleanAnsi;
const FileArea = require('../core/file_base_area.js'); const FileArea = require('../core/file_base_area.js');
const Errors = require('../core/enig_error.js').Errors; const Errors = require('../core/enig_error.js').Errors;
const ErrNotEnabled = require('../core/enig_error.js').ErrorReasons.NotEnabled; const ErrNotEnabled = require('../core/enig_error.js').ErrorReasons.NotEnabled;
@ -19,8 +18,6 @@ const FileAreaWeb = require('../core/file_area_web.js');
const FileBaseFilters = require('../core/file_base_filter.js'); const FileBaseFilters = require('../core/file_base_filter.js');
const resolveMimeType = require('../core/mime_util.js').resolveMimeType; const resolveMimeType = require('../core/mime_util.js').resolveMimeType;
const cleanControlCodes = require('../core/string_util.js').cleanControlCodes;
// deps // deps
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
@ -378,17 +375,15 @@ exports.getModule = class FileAreaList extends MenuModule {
if(_.isString(self.currentFileEntry.desc)) { if(_.isString(self.currentFileEntry.desc)) {
const descView = self.viewControllers.browse.getView(MciViewIds.browse.desc); const descView = self.viewControllers.browse.getView(MciViewIds.browse.desc);
if(descView) { if(descView) {
createCleanAnsi( descView.setAnsi(
self.currentFileEntry.desc, self.currentFileEntry.desc,
{ height : self.client.termHeight, width : descView.dimens.width }, {
cleanDesc => { prepped : false,
// :TODO: use cleanDesc -- need to finish createCleanAnsi() !! forceLineTerm : true
//descView.setText(cleanDesc); },
descView.setText( self.currentFileEntry.desc ); () => {
self.updateQueueIndicator(); self.updateQueueIndicator();
self.populateCustomLabels('browse', MciViewIds.browse.customRangeStart); self.populateCustomLabels('browse', MciViewIds.browse.customRangeStart);
return callback(null); return callback(null);
} }
); );