8

Within the Bash shell, I can use tab-completion to use suggest file and directory names. How can I achieve this with nodejs and readline?

Examples:

  • /<Tab> should suggest /root/, /bin/, etc.
  • /et<Tab> should complete to /etc/.
  • fo<Tab> should complete to foobar assuming such a file exists in the current directory.

I was thinking of using globbing (pattern search_term.replace(/[?*]/g, "\\$&") + "*"), but is there maybe a library that I have overlooked?

This is my current approach using glob, it is broken when using //<Tab> as it returns the canonicalized name and has possibly some other oddities:

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();
4

2 回答 2

2

这是一个有一些怪癖的工作解决方案:

  • 它不支持相对路径
  • 当尝试通过按两次 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])));
}
于 2020-11-04T19:05:35.697 回答
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]);
});
于 2015-07-15T15:07:30.663 回答