I want to use reverse interactive search
in NodeJS
REPL by Ctrl + r
like in bash
or irb
.
Ctrl + r
didn't trigger the interactive search. Is there a way to use the function in Nodejs REPL?
I'm using MacOS Sierra and the version of NodeJS is v8.5.0
.
Starting the REPL is simple - just run node on the command line without a filename. It then drops you into a simple prompt ('>') where you can type any JavaScript command you wish. As in most shells, you can press the up and down arrow keys to scroll through your command history and modify previous commands.
The Node. js Read-Eval-Print-Loop (REPL) is an interactive shell that processes Node. js expressions. The shell reads JavaScript code the user enters, evaluates the result of interpreting the line of code, prints the result to the user, and loops until the user signals to quit.
Run REPL from JavaScript file We can import the REPL in a JavaScript file using repl . repl. start(); Run the file in the command line.
To exit from the REPL terminal, press Ctrl + C twice or write . exit and press Enter.
This question was answered in a recent I pity the foo() blog post...
Can reverse-search in commands history be used inside Node’s REPL?
Currently it seems like its not possible. The Node REPL does allow to persist the history into a file and later load it, but doesn’t allow to reverse-search it.
So it appears that reverse history search is not natively supported by the REPL.
However you can install the rlwrap
utility and run it on top of the Node REPL to provide similar functionality. The REPL documentation site has some basic instructions to get you up and running. I was a little curious what was going on behind the scenes so a little more Googling turned up this section from Learning Node: Moving to the Server Side which goes into more detail on the tradeoffs associated with using rlwrap
. For example...
As helpful as rlwrap is, we still end up with undefined every time we type in an expression that doesn't return a value. However, we can adjust this, and other functionality, just by creating our own custom REPL, discussed next.
I see that bounty is ending soon... can I have it? What if I told you:
Skip to The Code section if you are familiar with history search and would like to use it as an alternative.
Are you familiar with zsh's history search? It is almost like reverse search, except for the fact that you can begin searching after entering something on the command line and pressing the up
arrow key. I find it to be a quicker alternative than reverse search and use it more often. The downside (which is minor in my opinion) is that you cannot update your original search query after searching unless you return to it by "going back down". For example:
history (top-down)
foo
bar
foo-ish
fubar
...
on your prompt:
> fo
press up
> foo
press up
> foo-ish
press down
> foo
press down
> fo
NOTE: As soon as you alter a search result and hit up
again, the altered text becomes your new query.
The Code
const PROMPT = '> ';
// history search
let input = '';
let searchInput = input;
let searchResults = [];
let searchResultIndex = 0;
process.stdin.on('keypress', (_, key) => {
// update 'input' on changes, excluding history
if (input !== server.line && !['up', 'down'].includes(key.name)) {
input = server.line;
}
// search is initiated if a user presses the 'up' key after having inputted
if (input !== '' && (
key.name === 'up' ||
(key.name === 'down' && searchResultIndex > 0)
)) {
// reset on fresh search or if user changed input during search
if (searchInput !== input) {
searchResultIndex = 0;
// first search result is always user's input
searchResults = [input, ...server.history.filter(item => item.includes(input))];
searchInput = input;
}
const increment = key.name === 'up' ? 1 : -1;
searchResultIndex = Math.min(searchResultIndex + increment, searchResults.length - 1);
server.historyIndex = -1;
server.line = '';
process.stdout.clearLine();
process.stdout.cursorTo(0);
const result = searchResults[searchResultIndex];
process.stdout.write(PROMPT + result);
server.cursor = result.length;
server.line = result;
}
});
BONUS - To implement persistent history:
const HISTSIZE = 100;
const server = repl.start({
prompt: PROMPT,
historySize: HISTSIZE,
removeHistoryDuplicates: true,
});
// load history or create file
historyFile = path.join(path.dirname(require.main.filename),
'.repl_history');
try {
fs.statSync(historyFile);
fs.readFileSync(historyFile, 'utf8')
.split('\n')
.filter(line => line.trim())
.map(line => server.history.push(line));
}
catch (e) {
console.log(e);
fs.closeSync(fs.openSync(historyFile, 'w'));
}
server.on('exit', () => {
// save history
fs.writeFileSync(historyFile, server.history.slice(0, HISTSIZE).join('\n'));
console.log('Thank you. Come again.');
process.exit();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With