Skip to content

Commit

Permalink
repl: integrate experimental repl
Browse files Browse the repository at this point in the history
  • Loading branch information
RedYetiDev committed Apr 19, 2024
1 parent 3790d52 commit c09f639
Show file tree
Hide file tree
Showing 20 changed files with 3,049 additions and 1,897 deletions.
67 changes: 67 additions & 0 deletions doc/api/experimental_repl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Experimental REPL

<!--introduced_in=REPLACEME-->

> Stability: 1 - Early Development
<!-- source_link=lib/internal/repl/experimental/index.js -->

Before diving into the details of the experimental REPL, it's essential to
understand its current development stage. Being an experimental feature, its
stability cannot be assured. It's accessible solely through the command-line
argument `--experimental-repl`.

## Overview

The experimental REPL seeks to enhance the existing REPL experience by
introducing innovative features such as real-time Syntax Highlighting, with
plans for annotations in future iterations. It's implemented asynchronously,
utilizing an ECMAScript class for its functionality.

## Key Features

1. **Syntax Highlighting**: Real-time syntax highlighting for improved
readability and a more enriched coding environment.

2. **Annotations** (Upcoming): Annotations will allow users to preview the
signature of a function.

## Usage

To interact with the experimental REPL, initialize it using the command-line
argument `--experimental-repl`. However, exercise caution as this feature is
still experimental and might demonstrate unexpected behavior.

## Runtime

Upon launching the REPL, expect the prompt to follow this format:

```console
In [1]: 1+1
Out[1]: 2
```

Retrieve the output of a specific line using the command `_` followed by the
line number:

```console
In [1]: Math.max(10, 100)
Out[1]: 100

In [2]: _1
Out[2]: 100
```

For accessing the output of the last three lines, use `_`, `__`, and `___`
respectively.

### Known Limitations

Since this REPL is experimental, several limitations are known:

1. Annotations are under development.
2. Cursor location isn't synchronized after multiple-line text.

If you'd like to propose a solution, feel free to open a
[pull request](https://github.com/nodejs/node/pulls). If you've identified an
issue not listed above, open an [issue](https://github.com/nodejs/node/issues).
1 change: 1 addition & 0 deletions doc/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* [DNS](dns.md)
* [Domain](domain.md)
* [Errors](errors.md)
* [Experimental REPL](experimental_repl.md)
* [Events](events.md)
* [File system](fs.md)
* [Globals](globals.md)
Expand Down
5 changes: 4 additions & 1 deletion doc/api/repl.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# REPL

> This file documents the **stable REPL**. There is a seperate document for
> the [experimental REPL](./experimental_repl.md)
<!--introduced_in=v0.10.0-->

> Stability: 2 - Stable
<!-- source_link=lib/repl.js -->
<!-- source_link=lib/internal/repl/stable/index.js -->

The `node:repl` module provides a Read-Eval-Print-Loop (REPL) implementation
that is available both as a standalone program or includible in other
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ Enable the experimental permission model.
.It Fl -experimental-policy
Use the specified file as a security policy.
.
.It Fl -experimental-repl
Use the experimental REPL.
.
.It Fl -experimental-shadow-realm
Use this flag to enable ShadowRealm support.
.
Expand Down
3 changes: 0 additions & 3 deletions lib/internal/main/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ if (process.env.NODE_REPL_EXTERNAL_MODULE) {
}

require('internal/modules/run_main').runEntryPointWithESMLoader(() => {
console.log(`Welcome to Node.js ${process.version}.\n` +
'Type ".help" for more information.');

const cliRepl = require('internal/repl');
cliRepl.createInternalRepl(process.env, (err, repl) => {
if (err) {
Expand Down
32 changes: 23 additions & 9 deletions lib/internal/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@ const {
NumberParseInt,
} = primordials;

const REPL = require('repl');
const { kStandaloneREPL } = require('internal/repl/utils');
const StableREPL = require('internal/repl/stable/index');
const ExperimentalREPL = require('internal/repl/experimental/index');
const { kStandaloneREPL } = require('internal/repl/stable/utils');
const {
getOptionValue,
} = require('internal/options');

const useExperimentalRepl = getOptionValue('--experimental-repl') || false;

module.exports = { __proto__: useExperimentalRepl ? ExperimentalREPL : StableREPL };
module.exports.createInternalRepl = useExperimentalRepl ? createExperimentalRepl : createStableRepl;

module.exports = { __proto__: REPL };
module.exports.createInternalRepl = createRepl;
function createStableRepl(env, opts, cb) {
process.stdout.write(`Welcome to Node.js ${process.version}.\n` +
'Type ".help" for more information.\n');

function createRepl(env, opts, cb) {
if (typeof opts === 'function') {
cb = opts;
opts = null;
Expand All @@ -31,13 +40,13 @@ function createRepl(env, opts, cb) {

if (env.NODE_REPL_MODE) {
opts.replMode = {
'strict': REPL.REPL_MODE_STRICT,
'sloppy': REPL.REPL_MODE_SLOPPY,
'strict': StableREPL.REPL_MODE_STRICT,
'sloppy': StableREPL.REPL_MODE_SLOPPY,
}[env.NODE_REPL_MODE.toLowerCase().trim()];
}

if (opts.replMode === undefined) {
opts.replMode = REPL.REPL_MODE_SLOPPY;
opts.replMode = StableREPL.REPL_MODE_SLOPPY;
}

const historySize = Number(env.NODE_REPL_HISTORY_SIZE);
Expand All @@ -47,7 +56,12 @@ function createRepl(env, opts, cb) {
opts.historySize = 1000;
}

const repl = REPL.start(opts);
const repl = StableREPL.start(opts);
const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY;
repl.setupHistory(term ? env.NODE_REPL_HISTORY : '', cb);
}

async function createExperimentalRepl() {
const repl = new ExperimentalREPL();
await repl.start();
}
82 changes: 82 additions & 0 deletions lib/internal/repl/experimental/deps/emphasize.js

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions lib/internal/repl/experimental/highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const emphasize = require('internal/repl/experimental/deps/emphasize');
const util = require('util');

function makeStyled(color) {
return (s) => `\x1b[${color[0]}m${s}\x1b[${color[1]}m`;
}

const sheet = {
'comment': makeStyled(util.inspect.colors.grey),
'quote': makeStyled(util.inspect.colors.grey),

'keyword': makeStyled(util.inspect.colors.green),
'addition': makeStyled(util.inspect.colors.green),

'number': makeStyled(util.inspect.colors.yellow),
'string': makeStyled(util.inspect.colors.green),
'meta meta-string': makeStyled(util.inspect.colors.cyan),
'literal': makeStyled(util.inspect.colors.cyan),
'doctag': makeStyled(util.inspect.colors.cyan),
'regexp': makeStyled(util.inspect.colors.cyan),

'attribute': undefined,
'attr': undefined,
'variable': makeStyled(util.inspect.colors.yellow),
'template-variable': makeStyled(util.inspect.colors.yellow),
'class title': makeStyled(util.inspect.colors.yellow),
'type': makeStyled(util.inspect.colors.yellow),

'symbol': makeStyled(util.inspect.colors.magenta),
'bullet': makeStyled(util.inspect.colors.magenta),
'subst': makeStyled(util.inspect.colors.magenta),
'meta': makeStyled(util.inspect.colors.magenta),
'meta keyword': makeStyled(util.inspect.colors.magenta),
'link': makeStyled(util.inspect.colors.magenta),

'built_in': makeStyled(util.inspect.colors.cyan),
'deletion': makeStyled(util.inspect.colors.red),

'emphasis': makeStyled(util.inspect.colors.italic),
'strong': makeStyled(util.inspect.colors.bold),
'formula': makeStyled(util.inspect.colors.inverse),
};

module.exports = (s) =>
emphasize.highlight('js', s, sheet).value;
51 changes: 51 additions & 0 deletions lib/internal/repl/experimental/history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

const fs = require('internal/fs/promises');
const permission = require('internal/process/permission');
const path = require('path');
const os = require('os');

const {
StringPrototypeSplit,
} = primordials;

module.exports = async () => {
let handle;

try {
const historyPath = path.join(os.homedir(), '.node_repl_history');
if (permission.isEnabled() && permission.has('fs.write', historyPath) === false) {
process.stdout.write('\nAccess to FileSystemWrite is restricted.\n' +
'REPL session history will not be persisted.\n');
return {
__proto__: null,
history: [],
writeHistory: () => false,
};
}

handle = await fs.open(historyPath, 'a+', 0o0600);
const data = await handle.readFile({ encoding: 'utf8' });
const history = StringPrototypeSplit(data, os.EOL, 1000);
const writeHistory = async (d) => {
if (!handle) {
return false;
}
try {
await handle.truncate(0);
await handle.writeFile(d.join(os.EOL));
return true;
} catch {
handle.close().catch(() => undefined);
handle = null;
return false;
}
};
return { __proto__: null, history, writeHistory };
} catch {
if (handle) {
handle.close().catch(() => undefined);
}
return { __proto__: null, history: [], writeHistory: () => false };
}
};
Loading

0 comments on commit c09f639

Please sign in to comment.