Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix REPL '_' assignment, support '_error' #2845

Merged
merged 2 commits into from Aug 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 39 additions & 8 deletions js/repl.ts
Expand Up @@ -25,7 +25,8 @@ function replError(...args: unknown[]): void {
}

const helpMsg = [
"_ Print last execution output",
"_ Get last evaluation result",
"_error Get last thrown error",
"exit Exit the REPL",
"help Print this help message"
].join("\n");
Expand Down Expand Up @@ -70,22 +71,25 @@ function isRecoverableError(e: Error): boolean {
return recoverableErrorMessages.includes(e.message);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Value = any;

let lastEvalResult: Value = undefined;
let lastThrownError: Value = undefined;

// Evaluate code.
// Returns true if code is consumed (no error/irrecoverable error).
// Also attempts setting window._ to last valid execute output.
// Returns false if error is recoverable
function evaluate(code: string): boolean {
const [result, errInfo] = core.evalContext(code);
if (!errInfo) {
// Try setting `window._` to the returned result
try {
window._ = result;
} catch {} // Silently fail on error.
lastEvalResult = result;
replLog(result);
} else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) {
// Recoverable compiler error
return false; // don't consume code.
} else {
lastThrownError = errInfo.thrown;
if (errInfo.isNativeError) {
const formattedError = formatError(
core.errorToJSON(errInfo.thrown as Error)
Expand Down Expand Up @@ -113,8 +117,35 @@ export async function replLoop(): Promise<void> {
exit(exitCode);
};

// Make _ a valid property on window to avoid confusing error.
window._ = undefined;
// Configure window._ to give the last evaluation result.
Object.defineProperty(window, "_", {
configurable: true,
get: (): Value => lastEvalResult,
set: (value: Value): Value => {
Object.defineProperty(window, "_", {
value: value,
writable: true,
enumerable: true,
configurable: true
});
console.log("Last evaluation result is no longer saved to _.");
}
});

// Configure window._error to give the last thrown error.
Object.defineProperty(window, "_error", {
configurable: true,
get: (): Value => lastThrownError,
set: (value: Value): Value => {
Object.defineProperty(window, "_error", {
value: value,
writable: true,
enumerable: true,
configurable: true
});
console.log("Last thrown error is no longer saved to _error.");
}
});

while (true) {
let code = "";
Expand Down
25 changes: 23 additions & 2 deletions tools/repl_test.py
Expand Up @@ -58,7 +58,8 @@ def test_exit_command(self):
def test_help_command(self):
out, err, code = self.input("help")
expectedOut = '\n'.join([
"_ Print last execution output",
"_ Get last evaluation result",
"_error Get last thrown error",
"exit Exit the REPL",
"help Print this help message",
"",
Expand Down Expand Up @@ -151,12 +152,32 @@ def test_missing_deno_dir(self):
self.assertTrue(err.startswith("Unable to save REPL history:"))
self.assertEqual(code, 0)

def test_save_last_output(self):
def test_save_last_eval(self):
out, err, code = self.input("1", "_")
self.assertEqual(out, '1\n1\n')
self.assertEqual(err, '')
self.assertEqual(code, 0)

def test_save_last_thrown(self):
out, err, code = self.input("throw 1", "_error")
self.assertEqual(out, '1\n')
self.assertEqual(err, 'Thrown: 1\n')
self.assertEqual(code, 0)

def test_assign_underscore(self):
out, err, code = self.input("_ = 1", "2", "_")
self.assertEqual(
out, 'Last evaluation result is no longer saved to _.\n1\n2\n1\n')
self.assertEqual(err, '')
self.assertEqual(code, 0)

def test_assign_underscore_error(self):
out, err, code = self.input("_error = 1", "throw 2", "_error")
self.assertEqual(
out, 'Last thrown error is no longer saved to _error.\n1\n1\n')
self.assertEqual(err, 'Thrown: 2\n')
self.assertEqual(code, 0)


if __name__ == "__main__":
run_tests()