Add complete_command for completing commands in PATH
use it for pipe completion
This commit is contained in:
parent
a0d4a44dd5
commit
bcba5287ad
124
src/file.cc
124
src/file.cc
|
@ -251,13 +251,54 @@ String find_file(const String& filename, memoryview<String> paths)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Filter>
|
||||||
|
std::vector<String> list_files(const String& prefix,
|
||||||
|
const String& dirname,
|
||||||
|
Filter filter)
|
||||||
|
{
|
||||||
|
kak_assert(dirname.empty() or dirname.back() == '/');
|
||||||
|
DIR* dir = opendir(dirname.empty() ? "./" : dirname.c_str());
|
||||||
|
auto closeDir = on_scope_end([=]{ closedir(dir); });
|
||||||
|
|
||||||
|
std::vector<String> result;
|
||||||
|
if (not dir)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
std::vector<String> subseq_result;
|
||||||
|
while (dirent* entry = readdir(dir))
|
||||||
|
{
|
||||||
|
if (not filter(*entry))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String filename = entry->d_name;
|
||||||
|
if (filename.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool match_prefix = prefix_match(filename, prefix);
|
||||||
|
const bool match_subseq = subsequence_match(filename, prefix);
|
||||||
|
if (match_prefix or match_subseq)
|
||||||
|
{
|
||||||
|
if (entry->d_type == DT_DIR)
|
||||||
|
filename += '/';
|
||||||
|
if (prefix.length() != 0 or filename[0] != '.')
|
||||||
|
{
|
||||||
|
if (match_prefix)
|
||||||
|
result.push_back(filename);
|
||||||
|
if (match_subseq)
|
||||||
|
subseq_result.push_back(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto& real_result = result.empty() ? subseq_result : result;
|
||||||
|
return real_result;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<String> complete_filename(const String& prefix,
|
std::vector<String> complete_filename(const String& prefix,
|
||||||
const Regex& ignored_regex,
|
const Regex& ignored_regex,
|
||||||
ByteCount cursor_pos)
|
ByteCount cursor_pos)
|
||||||
{
|
{
|
||||||
String real_prefix = parse_filename(prefix.substr(0, cursor_pos));
|
String real_prefix = parse_filename(prefix.substr(0, cursor_pos));
|
||||||
String dirname = "./";
|
String dirname;
|
||||||
String dirprefix;
|
|
||||||
String fileprefix = real_prefix;
|
String fileprefix = real_prefix;
|
||||||
|
|
||||||
ByteCount dir_end = -1;
|
ByteCount dir_end = -1;
|
||||||
|
@ -269,49 +310,68 @@ std::vector<String> complete_filename(const String& prefix,
|
||||||
if (dir_end != -1)
|
if (dir_end != -1)
|
||||||
{
|
{
|
||||||
dirname = real_prefix.substr(0, dir_end + 1);
|
dirname = real_prefix.substr(0, dir_end + 1);
|
||||||
dirprefix = dirname;
|
|
||||||
fileprefix = real_prefix.substr(dir_end + 1);
|
fileprefix = real_prefix.substr(dir_end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
DIR* dir = opendir(dirname.c_str());
|
|
||||||
auto closeDir = on_scope_end([=]{ closedir(dir); });
|
|
||||||
|
|
||||||
std::vector<String> result;
|
|
||||||
if (not dir)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
const bool check_ignored_regex = not ignored_regex.empty() and
|
const bool check_ignored_regex = not ignored_regex.empty() and
|
||||||
not boost::regex_match(fileprefix.c_str(), ignored_regex);
|
not boost::regex_match(fileprefix.c_str(), ignored_regex);
|
||||||
|
|
||||||
std::vector<String> subseq_result;
|
auto filter = [&](const dirent& entry)
|
||||||
while (dirent* entry = readdir(dir))
|
|
||||||
{
|
{
|
||||||
String filename = entry->d_name;
|
return not check_ignored_regex or
|
||||||
if (filename.empty())
|
not boost::regex_match(entry.d_name, ignored_regex);
|
||||||
continue;
|
};
|
||||||
|
std::vector<String> res = list_files(fileprefix, dirname, filter);
|
||||||
|
for (auto& file : res)
|
||||||
|
file = escape(dirname + file);
|
||||||
|
std::sort(res.begin(), res.end());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (check_ignored_regex and boost::regex_match(filename.c_str(), ignored_regex))
|
std::vector<String> complete_command(const String& prefix, ByteCount cursor_pos)
|
||||||
continue;
|
{
|
||||||
|
String real_prefix = parse_filename(prefix.substr(0, cursor_pos));
|
||||||
|
String dirname;
|
||||||
|
String fileprefix = real_prefix;
|
||||||
|
|
||||||
const bool match_prefix = prefix_match(filename, fileprefix);
|
ByteCount dir_end = -1;
|
||||||
const bool match_subseq = subsequence_match(filename, fileprefix);
|
for (ByteCount i = 0; i < real_prefix.length(); ++i)
|
||||||
if (match_prefix or match_subseq)
|
|
||||||
{
|
{
|
||||||
String name = dirprefix + filename;
|
if (real_prefix[i] == '/')
|
||||||
if (entry->d_type == DT_DIR)
|
dir_end = i;
|
||||||
name += '/';
|
}
|
||||||
if (fileprefix.length() != 0 or filename[0] != '.')
|
|
||||||
|
std::vector<String> path;
|
||||||
|
if (dir_end != -1)
|
||||||
{
|
{
|
||||||
if (match_prefix)
|
path.emplace_back(real_prefix.substr(0, dir_end + 1));
|
||||||
result.push_back(escape(name));
|
fileprefix = real_prefix.substr(dir_end + 1);
|
||||||
if (match_subseq)
|
|
||||||
subseq_result.push_back(escape(name));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
path = split(getenv("PATH"), ':');
|
||||||
|
|
||||||
|
std::vector<String> res;
|
||||||
|
for (auto dirname : path)
|
||||||
|
{
|
||||||
|
if (not dirname.empty() and dirname.back() != '/')
|
||||||
|
dirname += '/';
|
||||||
|
|
||||||
|
auto filter = [&](const dirent& entry) {
|
||||||
|
struct stat st;
|
||||||
|
if (stat((dirname + entry.d_name).c_str(), &st))
|
||||||
|
return false;
|
||||||
|
bool executable = (st.st_mode & S_IXUSR)
|
||||||
|
| (st.st_mode & S_IXGRP)
|
||||||
|
| (st.st_mode & S_IXOTH);
|
||||||
|
return S_ISREG(st.st_mode) and executable;
|
||||||
|
};
|
||||||
|
auto completion = list_files(prefix, dirname, filter);
|
||||||
|
std::move(completion.begin(), completion.end(), std::back_inserter(res));
|
||||||
}
|
}
|
||||||
}
|
std::sort(res.begin(), res.end());
|
||||||
auto& real_result = result.empty() ? subseq_result : result;
|
auto it = std::unique(res.begin(), res.end());
|
||||||
std::sort(real_result.begin(), real_result.end());
|
res.erase(it, res.end());
|
||||||
return real_result;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t get_fs_timestamp(const String& filename)
|
time_t get_fs_timestamp(const String& filename)
|
||||||
|
|
|
@ -38,6 +38,9 @@ time_t get_fs_timestamp(const String& filename);
|
||||||
std::vector<String> complete_filename(const String& prefix,
|
std::vector<String> complete_filename(const String& prefix,
|
||||||
const Regex& ignore_regex,
|
const Regex& ignore_regex,
|
||||||
ByteCount cursor_pos = -1);
|
ByteCount cursor_pos = -1);
|
||||||
|
|
||||||
|
std::vector<String> complete_command(const String& prefix,
|
||||||
|
ByteCount cursor_pos = -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // file_hh_INCLUDED
|
#endif // file_hh_INCLUDED
|
||||||
|
|
|
@ -420,7 +420,37 @@ void command(Context& context, int)
|
||||||
|
|
||||||
void pipe(Context& context, int)
|
void pipe(Context& context, int)
|
||||||
{
|
{
|
||||||
context.input_handler().prompt("pipe:", get_color("Prompt"), complete_nothing,
|
auto completer = [](const Context& context, CompletionFlags flags,
|
||||||
|
const String& prefix, ByteCount cursor_pos)
|
||||||
|
{
|
||||||
|
ByteCount word_start = 0;
|
||||||
|
ByteCount word_end = 0;
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
const ByteCount len = prefix.length();
|
||||||
|
for (ByteCount pos = 0; pos < cursor_pos;)
|
||||||
|
{
|
||||||
|
if (pos != 0)
|
||||||
|
first = false;
|
||||||
|
while (pos != len and is_blank(prefix[pos]))
|
||||||
|
++pos;
|
||||||
|
word_start = pos;
|
||||||
|
while (pos != len and not is_blank(prefix[pos]))
|
||||||
|
++pos;
|
||||||
|
word_end = pos;
|
||||||
|
}
|
||||||
|
Completions completions{word_start, word_end};
|
||||||
|
if (first)
|
||||||
|
completions.candidates = complete_command(prefix.substr(word_start, word_end),
|
||||||
|
cursor_pos - word_start);
|
||||||
|
else
|
||||||
|
completions.candidates = complete_filename(prefix.substr(word_start, word_end),
|
||||||
|
context.options()["ignored_files"].get<Regex>(),
|
||||||
|
cursor_pos - word_start);
|
||||||
|
return completions;
|
||||||
|
};
|
||||||
|
|
||||||
|
context.input_handler().prompt("pipe:", get_color("Prompt"), completer,
|
||||||
[](const String& cmdline, PromptEvent event, Context& context)
|
[](const String& cmdline, PromptEvent event, Context& context)
|
||||||
{
|
{
|
||||||
if (event != PromptEvent::Validate)
|
if (event != PromptEvent::Validate)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user