Skip to content

Commit

Permalink
Merge pull request #1036 from Agoric/mfig/repl-console
Browse files Browse the repository at this point in the history
Implement `console` endowment for the REPL
  • Loading branch information
michaelfig authored May 1, 2020
2 parents 0a774a5 + 4aaf56d commit e778b72
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 31 deletions.
9 changes: 7 additions & 2 deletions packages/cosmic-swingset/lib/ag-solo/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,17 @@
padding-bottom: 12px;
}

.history>div> :first-child {
.history>.msg-line {
background: antiquewhite;
font-style: oblique;
}

.history>div>.command-line :first-child, .history>div>.history-line :first-child {
text-align: right;
color: #424248;
}

.history>div> :nth-child(2) {
.history>div>.command-line :nth-child(2), .history>div>.history-line :nth-child(2) {
text-align: left;
padding-left: 8px;
/* Overflow must be set (to auto or hidden) to force the word break to apply.
Expand Down
60 changes: 45 additions & 15 deletions packages/cosmic-swingset/lib/ag-solo/html/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,53 @@ function run() {

const commands = [];

function addHistoryRow(h, histnum, kind, value) {
function linesToHTML(lines) {
return lines
.split('\n')
.map(l =>
l
.replace('&', '&')
.replace('<', '&lt;')
.replace(/\t/g, ' ')
.replace(/\s/g, '&nbsp;'),
)
.join('<br />');
}

function addHistoryRow(h, histnum, kind, value, msgs) {
if (histnum >= 0) {
const row = document.createElement('div');
row.className = `${kind}-line`;
const label = document.createElement('div');
label.textContent = `${kind}[${histnum}]`;
const content = document.createElement('div');
content.id = `${kind}-${histnum}`;
content.textContent = `${value}`;
row.appendChild(label);
row.appendChild(content);
h.append(row);
}
// Write out any messages attached to this line.
const row = document.createElement('div');
row.className = `${kind}-line`;
const label = document.createElement('div');
label.textContent = `${kind}[${histnum}]`;
const content = document.createElement('div');
content.id = `${kind}-${histnum}`;
content.textContent = `${value}`;
row.appendChild(label);
row.appendChild(content);
row.className = 'msg-line';
const m = document.createElement('div');
m.id = `msg-${kind}-${histnum}`;
if (msgs) {
m.innerHTML = linesToHTML(`${msgs}`);
}
row.appendChild(document.createElement('div'));
row.appendChild(m);
h.append(row);
}

function addHistoryEntry(histnum, command, result) {
function addHistoryEntry(histnum, command, result, consoles) {
const h = document.getElementById('history');
addHistoryRow(h, histnum, 'command', command);
addHistoryRow(h, histnum, 'history', result);
addHistoryRow(h, histnum, 'command', command, consoles.command || '');
addHistoryRow(h, histnum, 'history', result, consoles.display || '');
commands[histnum] = command;
}

function updateHistory(histnum, command, result) {
function updateHistory(histnum, command, result, consoles = {}) {
const h = document.getElementById('history');
const isScrolledToBottom =
h.scrollHeight - h.clientHeight <= h.scrollTop + 1;
Expand All @@ -74,10 +100,14 @@ function run() {
const c = document.getElementById(`command-${histnum}`);
if (c) {
const h1 = document.getElementById(`history-${histnum}`);
const m1 = document.getElementById(`msg-command-${histnum}`);
const m2 = document.getElementById(`msg-history-${histnum}`);
c.textContent = `${command}`;
m1.innerHTML = linesToHTML(`${consoles.command}`);
h1.textContent = `${result}`;
m2.innerHTML = linesToHTML(`${consoles.display}`);
} else {
addHistoryEntry(histnum, command, result);
addHistoryEntry(histnum, command, result, consoles);
}
if (isScrolledToBottom) {
setTimeout(() => (h.scrollTop = h.scrollHeight), 0);
Expand All @@ -97,7 +127,7 @@ function run() {
// we receive commands to update result boxes
if (obj.type === 'updateHistory') {
// these args come from calls to vat-http.js updateHistorySlot()
updateHistory(obj.histnum, obj.command, obj.display);
updateHistory(obj.histnum, obj.command, obj.display, obj.consoles);
} else {
console.log(`unknown WS type in:`, obj);
}
Expand Down
95 changes: 81 additions & 14 deletions packages/cosmic-swingset/lib/ag-solo/vats/repl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeEvaluators } from '@agoric/evaluate';
import harden from '@agoric/harden';
import { isPromise } from '@agoric/produce-promise';
import { makeConsole } from '@agoric/swingset-vat/src/makeConsole';

// A REPL-specific JSON stringify.
export function stringify(
Expand Down Expand Up @@ -30,55 +31,113 @@ export function stringify(
already.add(value);

let ret = '';
const spcs = spaces === undefined ? '' : `\n${' '.repeat(spaces)}`;
const nextSpaces = spaces === undefined ? undefined : spaces + 2;
if (getInterfaceOf && getInterfaceOf(value) !== undefined) {
ret += `${value}`;
} else if (Array.isArray(value)) {
ret += `[`;

let sep = '';
for (let i = 0; i < value.length; i += 1) {
ret += sep + stringify(value[i], spaces, getInterfaceOf, already);
ret += `${sep}${spcs}${stringify(
value[i],
nextSpaces,
getInterfaceOf,
already,
)}`;
sep = ',';
}
if (sep !== '') {
ret += spcs;
}
ret += ']';
return ret;
}

ret += '{';
let sep = '';
for (const key of Object.keys(value)) {
ret += `${sep}${JSON.stringify(key, undefined, spaces)}:${stringify(
value[key],
spaces,
getInterfaceOf,
already,
)}`;
ret += `${sep}${spcs}${JSON.stringify(
key,
undefined,
nextSpaces,
)}:${stringify(value[key], nextSpaces, getInterfaceOf, already)}`;
sep = ',';
}
if (sep !== '') {
ret += spcs;
}
ret += '}';
return ret;
}

export function getReplHandler(E, homeObjects, send, vatPowers) {
const commands = {};
const history = {};
const display = {};
let highestHistory = -1;
const commands = {
[highestHistory]: '',
};
const history = {
[highestHistory]: '',
};
const display = {
[highestHistory]: '',
};
const replHandles = new Set();
let consoleOffset = highestHistory * 2 + 1;
const consoleRegions = {
[consoleOffset - 1]: [],
[consoleOffset]: [],
};

// Create a message much like a console.log would.
function joinMsg(args) {
let ret = '';
let sep = '';
for (const a of args) {
let s;
if (typeof a === 'string') {
s = a;
} else {
s = stringify(a, 2, vatPowers.getInterfaceOf);
}
ret += `${sep}${s}`;
sep = ' ';
}
return ret;
}

function updateHistorySlot(histnum) {
// console.debug(`sendBroadcast ${histnum}`);
console.warn(`sendBroadcast ${histnum}`, histnum, consoleOffset);
send(
{
type: 'updateHistory',
histnum,
command: commands[histnum],
display: display[histnum],
consoles: {
command: consoleRegions[histnum * 2].join('\n'),
display: consoleRegions[histnum * 2 + 1].join('\n'),
},
},
[...replHandles.keys()],
);
}

function writeToConsole(...args) {
consoleRegions[consoleOffset].push(joinMsg(args));
updateHistorySlot(Math.floor(consoleOffset / 2));
}

const replConsole = makeConsole({
debug: writeToConsole,
log: writeToConsole,
info: writeToConsole,
warn: writeToConsole,
error: writeToConsole,
});

replConsole.log(`Welcome to Agoric!`);
const { evaluateProgram } = makeEvaluators({ sloppyGlobals: true });

const handler = {
Expand All @@ -88,7 +147,7 @@ export function getReplHandler(E, homeObjects, send, vatPowers) {

rebroadcastHistory() {
// console.debug(`rebroadcastHistory`, highestHistory);
for (let histnum = 0; histnum <= highestHistory; histnum += 1) {
for (let histnum = -1; histnum <= highestHistory; histnum += 1) {
updateHistorySlot(histnum);
}
return true;
Expand All @@ -102,18 +161,23 @@ export function getReplHandler(E, homeObjects, send, vatPowers) {
`histnum ${histnum} is not larger than highestHistory ${highestHistory}`,
);
}

highestHistory = histnum;

commands[histnum] = body;

// Need this concatenation to bypass direct eval test in realms-shim.
// eslint-disable-next-line no-useless-concat
consoleOffset = histnum * 2;
consoleRegions[consoleOffset] = [];
consoleRegions[consoleOffset + 1] = [];
// eslint-disable-next-line no-useless-concat
display[histnum] = `working on eval` + `(${body})`;
updateHistorySlot(histnum);

const endowments = {
...vatPowers,
console,
console: replConsole,
E,
commands,
history,
Expand All @@ -128,7 +192,10 @@ export function getReplHandler(E, homeObjects, send, vatPowers) {
} catch (e) {
console.log(`error in eval`, e);
history[histnum] = e;
display[histnum] = `exception: ${e}`;
display[histnum] = joinMsg('exception:', e);
} finally {
// Advance to the region after the display.
consoleOffset += 1;
}

if (isPromise(r)) {
Expand Down

0 comments on commit e778b72

Please sign in to comment.