如何修改 Node.js Readline 中的光标行?

How to modify cursor line in Node.js Readline?

我在 Node.js 中有我的语言的 REPL,当用户手动输入文本并按回车键时,我会自动缩进行,以便他缩进文本。问题是,当他 copy/paste 有缩进的文本时,它有双缩进。

问题是我在换行之前添加了缩进。所以我解决这个问题的想法是在用户按下回车键并且他已经有空格后如何删除我的空格(重写该行)。但我不知道该怎么做。是否可以使用一些 ANSI 代码。

我的完整 REPL 如下所示:

if (process.stdin.isTTY) {
    console.log(banner);
}
var prompt = 'lips> ';
var continuePrompt = '... ';
var terminal = !!process.stdin.isTTY && !(process.env.EMACS || process.env.INSIDE_EMACS);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: prompt,
    terminal
});
if (process.stdin.isTTY) {
    rl.prompt();
}
var code = '';
var multiline = false;
var resolve;
var newline;
// we use promise loop to fix issue when copy paste list of S-Expression
var prev_eval = Promise.resolve();
boostrap(interp).then(function() {
    rl.on('line', function(line) {
        code += line.replace(/^\s+/, '') + '\n';
        try {
            if (balanced_parenthesis(code)) {
                rl.pause();
                prev_eval = prev_eval.then(function() {
                    var result = run(code, interp);
                    code = '';
                    return result;
                }).then(function(result) {
                    if (process.stdin.isTTY) {
                        print(result);
                        if (newline) {
                            // readline don't work with not endend lines
                            // it ignore those so we end then ourselfs
                            process.stdout.write("\n");
                            newline = false;
                        }
                        if (multiline) {
                            rl.setPrompt(prompt);
                            multiline = false;
                        }
                        rl.prompt();
                    }
                    rl.resume();
                }).catch(function() {
                    if (process.stdin.isTTY) {
                        if (multiline) {
                            rl.setPrompt(prompt);
                            multiline = false;
                        }
                        rl.prompt();
                    }
                });
            } else {
                multiline = true;
                var ind = indent(code, 2, prompt.length - continuePrompt.length);
                rl.setPrompt(continuePrompt);
                rl.prompt();
                var spaces = new Array(ind + 1).join(' ');
                if (terminal) {
                    rl.write(spaces);
                } else {
                    process.stdout.write(spaces);
                    code += spaces;
                }
            }
        } catch (e) {
            console.error(e.message);
            code = '';
            rl.prompt();
        }
    });
});

我正在使用 rl.write(空格);在用户键入他的行后的多行命令中,我需要检查是否

我想我需要的是修改 Node.js Readline 中有光标的行。

我试过:

    rl.on('line', function(line) {
        if (line.match(/^\s/)) {
            rl.write(null, { ctrl: true, name: 'u' });
            rl.write((multiline ? continuePrompt : prompt) + line);
        }
        line = line.replace(/^\s+/, '');
        code += line + '\n';

    rl.on('line', function(line) {
        var have_spaces = line.match(/^\s/);
        line = line.replace(/^\s+/, '');
        if (have_spaces) {
            rl.pause();
            process.stdout.write('\x1b[1K\x1b[0K' + line);
            rl.resume();
        }
        code += line + '\n';

但是在我按下回车键后,两者都只是在下一行回显,而不是有光标所在的行。如何修改有光标的行?我对能做到这一点的图书馆很满意,但我找不到任何有用的东西。

在 readline 模块中,每当您按下 enter 时,readline 都会清除当前行,然后发出行事件:readline.js source code,这意味着您无法在行事件触发时修改前一行的输出。这听起来令人失望,但我们有一个解决方法:

  1. 你可以试试这个基本版本:
const readline = require('readline')
const input = process.stdin
const rl = readline.createInterface({
  input: input,
  output: process.stdout,
})

rl.prompt()
input.setEncoding('utf8')

input.on('data', (input) => {
  const REGEXP = /^\s+/
  let haveSpaces = null
  if (input.length > 1) {
    input = input.replace(REGEXP, (match) => {
      if (match) {
        haveSpaces = true
        return ''
      }
    })
    if (haveSpaces) {
      rl.write(null, { ctrl: true, name: 'u' })
      rl.write(input)
    }
  }
})

通过监听输入流的data事件,判断用户是否粘贴了带缩进的字符串,然后修改输入

  1. 或使用 repll 使其更容易互动:
const { replLive, onInput } = require('repll')
const repll = replLive(['repll› '], 'A meaningful placeholder')

onInput((input) => {
  const REGEXP = /^\s+/
  let haveSpaces = null
  if (input.length > 1) {
    input = input.replace(REGEXP, (match) => {
      if (match) {
        haveSpaces = true
        return ''
      }
    })
    if (haveSpaces) {
      repll.write(null, { ctrl: true, name: 'u' })
      repll.write(input)
      repll.refresh('Your pasted text have indents! We already fixed that')
    }
  }
})

我有一个模块 (repll) 可以处理这些场景,具有生动的输出和占位符以及许多其他交互功能。

  1. 对于语法高度,请参阅 repl.it 上的演示(因为我不希望这个答案太长而无法阅读)。记住 运行 它在 shell 中(不是 console)

这是我得到的,为了准确回答标题中的问题,您可以使用按键事件修改输入:

rl._writeToOutput = function _writeToOutput(string) {
    rl.output.write(scheme(string));
};
process.stdin.on('keypress', (c, k) => {
    setTimeout(function() {
        rl._refreshLine(); // force refresh colors
    }, 0);
});

这会突出显示该行并在每次按键时刷新该行以强制显示颜色。

对于缩进,我已经使用此代码和 ANSI 转义码解决了我的问题。

var terminal = !!process.stdin.isTTY && !(process.env.EMACS || process.env.INSIDE_EMACS);

bootstrap(interp).then(function() {
    rl.on('line', function(line) {
        code += line;
        const lines = code.split('\n');
        const cols = process.stdout.columns;
        // fix formatting for previous lines that was echo
        // ReadLine will not handle those
        if (terminal && lines.length > 2) {
            var count = 0;
            // correction when line wrapps in original line
            // that will be overwritten
            lines.map(line => {
                if (line.length > cols) {
                    count += Math.ceil(line.length / cols) - 1;
                }
            });
            var f = new Formatter(code);
            code = f.format();
            const stdout = scheme(code).split('\n').map((line, i) => {
                var prefix;
                if (i === 0) {
                    prefix = unify_prompt(prompt, continuePrompt);
                } else {
                    prefix = unify_prompt(continuePrompt, prompt);
                }
                return '\x1b[K' + prefix + line;
            }).join('\n');
            let num = lines.length + count;
            const format = `\x1b[${num}F${stdout}\n`;
            process.stdout.write(format);
        }
        code += '\n';
        ...

当行长超过终端宽度时,此代码只有一个问题,行的更新被破坏,并且当您复制粘贴文本时,最后一行没有正确缩进(它有双缩进),直到您按回车键。