Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use "reverse interactive search" in NodeJS REPL?

Tags:

node.js

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.

like image 499
ironsand Avatar asked Oct 12 '17 16:10

ironsand


People also ask

How do I use REPL in node?

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.

What are REPL commands in node JS?

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.

Which command is used to run REPL session in node?

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.

How do I get out of node REPL?

To exit from the REPL terminal, press Ctrl + C twice or write . exit and press Enter.


2 Answers

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.

like image 92
v1bri Avatar answered Oct 14 '22 15:10

v1bri


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();
});
like image 23
Palisand Avatar answered Oct 14 '22 15:10

Palisand