【问题标题】:How to modify cursor line in Node.js Readline?如何修改 Node.js Readline 中的光标线?
【发布时间】:2020-06-11 11:25:44
【问题描述】:

我在 Node.js 中有我的语言的 REPL,当用户手动输入文本并按 Enter 时,我会自动缩进行,这样他就可以缩进文本。问题是当他复制/粘贴有缩进的文本时,它有双重缩进。

问题是我在换行之前添加了缩进。所以我解决这个问题的想法是在用户按下回车并且他已经有空格之后如何删除我的空格(重写行)。但我不知道该怎么做。是否可以使用一些 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(spaces);在用户键入他的行后的多行命令中,我需要检查是否

我认为我需要修改 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';

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

【问题讨论】:

    标签: javascript node.js readline


    【解决方案1】:

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

    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';
            ...
    

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

    【讨论】:

      【解决方案2】:

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

      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

      【讨论】:

      • 谢谢,您的第一个解决方案几乎可以工作,只有当我复制末尾没有空白换行符的粘贴文本时它才有效。关于你的模块它有历史吗?在我的代码中,我从 Node.js 复制代码以将历史记录写入文件。
      • 不幸的是,您的库根本不起作用,onInput 没有执行,我无法粘贴语法高亮的代码。
      • 看来你的第一个解决方案也不起作用,我在添加input.on('data', 后没有得到提示,使用它时它会阻止用户输入,甚至不会让我的 REPL运行。
      • 末尾有空白换行符(\r\n\n) 行为类似于 enter,readline 将强制终端创建一个新行。至于 repll 问题,onInput 根据我的测试工作正常(粘贴语法高亮文本工作),确保你做对了。如果仍有问题,您可以通过code 报告问题
      • 尝试第三种解决方案,它解决了:>只有当我复制粘贴文本末尾没有空白换行符时它才有效
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多