Как предложить файлы с завершением вкладки, используя readline?
В оболочке Bash я могу использовать табуляцию, чтобы использовать предлагаемые имена файлов и каталогов. Как я могу добиться этого с помощью nodejs и readline?
Примеры:
/<Tab>
должен предложить/root/
,/bin/
, так далее./et<Tab>
следует завершить/etc/
,fo<Tab>
следует завершитьfoobar
при условии, что такой файл существует в текущем каталоге.
Я думал об использовании Globbing (шаблон search_term.replace(/[?*]/g, "\\$&") + "*"
), но есть ли библиотека, которую я пропустил?
Это мой нынешний подход с использованием glob, он нарушается при использовании //<Tab>
поскольку он возвращает канонизированное имя и, возможно, имеет некоторые другие странности:
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();
1 ответ
Вот рабочее решение с несколькими причудами:
- Он не поддерживает относительные пути
- При попытке отобразить предложения, дважды нажав вкладку, отображается полный путь в списке предложений.
- Он предпочитает '/' вместо '\', но допускает разделители '\' в окнах.
- Он поддерживает только каталоги и файлы. (без устройств, труб, розеток, софт линков)
Код:
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])));
}
Может быть, вы могли бы взглянуть на 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]);
});