如何使用readline建议使用制表符完成的文件?

时间:2013-04-17 19:31:10

标签: node.js readline tab-completion

在Bash shell中,我可以使用tab-completion来使用建议文件和目录名称。如何使用nodejs和readline实现此目的?

示例:

  • /<Tab>应该建议/root//bin/
  • /et<Tab>应填写/etc/
  • 假设当前目录中存在此类文件,
  • fo<Tab>应填写foobar

我在考虑使用globbing(模式search_term.replace(/[?*]/g, "\\$&") + "*"),但可能有一个我忽略的库吗?

这是我目前使用glob的方法,它在使用//<Tab>时被破坏,因为它返回规范化名称并且可能还有其他一些奇怪之处:<​​/ p>

function command_completion(line) {
    var hits;
    // likely broken, one does not simply escape a glob char
    var pat = line.replace(/[?*]/g, "\\$&") + "*";
    // depends: glob >= 3.0
    var glob = require("glob").sync;
    hits = glob(pat, {
        silent: true,
        nobrace: true,
        noglobstar: true,
        noext: true,
        nocomment: true,
        nonegate: true
    });

    return [hits, line];
}

var readline = require("readline");
rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    completer: command_completion
});
rl.prompt();

2 个答案:

答案 0 :(得分:0)

也许你可以看一下readdir:https://www.npmjs.com/package/readdir

只需阅读用户制作选项卡的目录,然后将用户输入与目录中每个文件的开头进行比较,如果文件名匹配,则将其显示给用户。类似的东西:

var readDir = require('readdir');

function strncmp(str1, str2, lgth) {
  var s1 = (str1 + '')
    .substr(0, lgth);
  var s2 = (str2 + '')
    .substr(0, lgth);

  return ((s1 == s2) ? 0 : ((s1 > s2) ? 1 : -1));
}

var userInput = // get user input;
var path = // get the path;
readDir.read(path, [*], function(err, files) {
    for (var i = 0; i < files.length; i++)
        if (strncmp(files[i], userInput, userInput.length) == 0)
            console.log(files[i]);
});

答案 1 :(得分:0)

这是一个可行的解决方案,但有一些怪癖:

  • 它不支持相对路径
  • 当尝试通过按两次Tab键显示建议时,它将在建议列表中显示完整路径。
  • 与'\'相比,它更喜欢使用'/',但在Windows上可以使用'\'分隔符
  • 它仅支持目录和文件。 (没有设备,管道,套接字,软链接)

代码:

const { promises: fsPromises } = require("fs"); 
const { parse, sep } = require("path");

function fileSystemCompleter(line, callback) {
  let { dir, base } = parse(line);
  fsPromises.readdir(dir, { withFileTypes: true })
    .then((dirEntries) => {
      // for an exact match that is a directory, read the contents of the directory
      if (dirEntries.find((entry) => entry.name === base && entry.isDirectory())) {
        dir = dir === "/" || dir === sep ? `${dir}${base}` : `${dir}/${base}`;
        return fsPromises.readdir(dir, { withFileTypes: true })
      }
      return dirEntries.filter((entry) => entry.name.startsWith(base));
    })
    .then((matchingEntries) => {
      if (dir === sep || dir === "/") {
        dir = "";
      }
      const hits = matchingEntries
        .filter((entry) => entry.isFile() || entry.isDirectory())
        .map((entry) => `${dir}/${entry.name}${entry.isDirectory() && !entry.name.endsWith("/") ? "/" : ""}`);
      callback(null, [hits, line]);
    })
    .catch(() => (callback(null, [[], line])));
}