Only offer directories when completing :cd arguments

Refactor code in file.cc, avoid many double stat when searching
commands

Fixes #646
This commit is contained in:
Maxime Coste 2016-04-07 22:47:41 +01:00
parent a8b2834d56
commit 3eca90f2b1
3 changed files with 20 additions and 22 deletions

View File

@ -1826,7 +1826,15 @@ const CommandDesc change_working_directory_cmd = {
single_name_param, single_name_param,
CommandFlags::None, CommandFlags::None,
CommandHelper{}, CommandHelper{},
filename_completer, PerArgumentCommandCompleter{{
[](const Context& context, CompletionFlags flags,
const String& prefix, ByteCount cursor_pos) -> Completions {
return { 0_byte, cursor_pos,
complete_filename(prefix,
context.options()["ignored_files"].get<Regex>(),
cursor_pos, true) };
}
}},
[](const ParametersParser& parser, Context&, const ShellContext&) [](const ParametersParser& parser, Context&, const ShellContext&)
{ {
if (chdir(parse_filename(parser[0]).c_str()) != 0) if (chdir(parse_filename(parser[0]).c_str()) != 0)

View File

@ -348,13 +348,13 @@ Vector<String> list_files(StringView dirname, Filter filter)
while (dirent* entry = readdir(dir)) while (dirent* entry = readdir(dir))
{ {
StringView filename = entry->d_name; StringView filename = entry->d_name;
if (filename.empty() or not filter(*entry)) if (filename.empty())
continue; continue;
struct stat st; struct stat st;
auto fmt_str = (dirname.empty() or dirname.back() == '/') ? "{}{}" : "{}/{}"; auto fmt_str = (dirname.empty() or dirname.back() == '/') ? "{}{}" : "{}/{}";
format_to(buffer, fmt_str, dirname, filename); format_to(buffer, fmt_str, dirname, filename);
if (stat(buffer, &st) != 0) if (stat(buffer, &st) != 0 or not filter(*entry, st))
continue; continue;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
@ -366,7 +366,7 @@ Vector<String> list_files(StringView dirname, Filter filter)
Vector<String> list_files(StringView directory) Vector<String> list_files(StringView directory)
{ {
return list_files(directory, [](const dirent& entry) { return list_files(directory, [](const dirent& entry, const struct stat&) {
return StringView{entry.d_name}.substr(0_byte, 1_byte) != "."; return StringView{entry.d_name}.substr(0_byte, 1_byte) != ".";
}); });
} }
@ -380,9 +380,8 @@ static CandidateList candidates(ConstArrayView<RankedMatch> matches, StringView
return res; return res;
} }
CandidateList complete_filename(StringView prefix, CandidateList complete_filename(StringView prefix, const Regex& ignored_regex,
const Regex& ignored_regex, ByteCount cursor_pos, bool only_dir)
ByteCount cursor_pos)
{ {
String real_prefix = parse_filename(prefix.substr(0, cursor_pos)); String real_prefix = parse_filename(prefix.substr(0, cursor_pos));
StringView dirname, fileprefix; StringView dirname, fileprefix;
@ -392,10 +391,11 @@ CandidateList complete_filename(StringView prefix,
not regex_match(fileprefix.begin(), fileprefix.end(), ignored_regex); not regex_match(fileprefix.begin(), fileprefix.end(), ignored_regex);
const bool include_hidden = fileprefix.substr(0_byte, 1_byte) == "."; const bool include_hidden = fileprefix.substr(0_byte, 1_byte) == ".";
auto filter = [&ignored_regex, check_ignored_regex, include_hidden](const dirent& entry) auto filter = [&ignored_regex, check_ignored_regex, include_hidden, only_dir](const dirent& entry, struct stat& st)
{ {
return (include_hidden or StringView{entry.d_name}.substr(0_byte, 1_byte) != ".") and return (include_hidden or StringView{entry.d_name}.substr(0_byte, 1_byte) != ".") and
(not check_ignored_regex or not regex_match(entry.d_name, ignored_regex)); (not check_ignored_regex or not regex_match(entry.d_name, ignored_regex)) and
(not only_dir or S_ISDIR(st.st_mode));
}; };
auto files = list_files(dirname, filter); auto files = list_files(dirname, filter);
Vector<RankedMatch> matches; Vector<RankedMatch> matches;
@ -416,13 +416,8 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
if (not dirname.empty()) if (not dirname.empty())
{ {
auto filter = [&dirname](const dirent& entry) auto filter = [&dirname](const dirent& entry, const struct stat& st)
{ {
char buffer[PATH_MAX+1];
format_to(buffer, "{}{}", dirname, entry.d_name);
struct stat st;
if (stat(buffer, &st) != 0)
return false;
bool executable = (st.st_mode & S_IXUSR) bool executable = (st.st_mode & S_IXUSR)
| (st.st_mode & S_IXGRP) | (st.st_mode & S_IXGRP)
| (st.st_mode & S_IXOTH); | (st.st_mode & S_IXOTH);
@ -460,12 +455,7 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
auto& cache = command_cache[dirname]; auto& cache = command_cache[dirname];
if (memcmp(&cache.mtim, &st.st_mtim, sizeof(TimeSpec)) != 0) if (memcmp(&cache.mtim, &st.st_mtim, sizeof(TimeSpec)) != 0)
{ {
auto filter = [&dirname](const dirent& entry) { auto filter = [&dirname](const dirent& entry, const struct stat& st) {
struct stat st;
char buffer[PATH_MAX+1];
format_to(buffer, "{}/{}", dirname, entry.d_name);
if (stat(buffer, &st))
return false;
bool executable = (st.st_mode & S_IXUSR) bool executable = (st.st_mode & S_IXUSR)
| (st.st_mode & S_IXGRP) | (st.st_mode & S_IXGRP)
| (st.st_mode & S_IXOTH); | (st.st_mode & S_IXOTH);

View File

@ -83,7 +83,7 @@ constexpr bool operator!=(const timespec& lhs, const timespec& rhs)
} }
CandidateList complete_filename(StringView prefix, const Regex& ignore_regex, CandidateList complete_filename(StringView prefix, const Regex& ignore_regex,
ByteCount cursor_pos = -1); ByteCount cursor_pos = -1, bool only_dir = false);
CandidateList complete_command(StringView prefix, ByteCount cursor_pos = -1); CandidateList complete_command(StringView prefix, ByteCount cursor_pos = -1);