Accept "cd dir/" again instead of using a subdirectory
Commit69053d962
(Use menu behavior when completing change-directory, 2022-07-19) made ":cd dir/" actually run ":cd dir/first-subdir", which can be surprising. This is usually irrelevant because you rarely type the trailing slash. However it does happen after correcting an error with `<backspace>` and friends. For for example, :cd d<tab>/f<backspace> results in :cd dir/ We should probably fix user expectations here. Do this by adding "dir/" as valid completion. This requires us to allow empty candidates in "RankedMatch" but there's no harm in that. This means we need to filter out empty completions from shell-script-candidates elsewhere. Alternative fix: we could revert69053d962
. This would remove the convenient menu behavior but that wouldn't be a huge deal. Fixes #4775
This commit is contained in:
parent
91d45a100a
commit
59b8b99577
|
@ -241,7 +241,8 @@ struct ShellScriptCompleter
|
||||||
ShellManager::Flags::WaitForStdout,
|
ShellManager::Flags::WaitForStdout,
|
||||||
shell_context).first;
|
shell_context).first;
|
||||||
CandidateList candidates;
|
CandidateList candidates;
|
||||||
for (auto&& candidate : output | split<StringView>('\n'))
|
for (auto&& candidate : output | split<StringView>('\n')
|
||||||
|
| filter([](auto s) { return not s.empty(); }))
|
||||||
candidates.push_back(candidate.str());
|
candidates.push_back(candidate.str());
|
||||||
|
|
||||||
return {0_byte, pos_in_token, std::move(candidates), m_flags};
|
return {0_byte, pos_in_token, std::move(candidates), m_flags};
|
||||||
|
@ -274,7 +275,8 @@ struct ShellCandidatesCompleter
|
||||||
ShellManager::Flags::WaitForStdout,
|
ShellManager::Flags::WaitForStdout,
|
||||||
shell_context).first;
|
shell_context).first;
|
||||||
m_candidates.clear();
|
m_candidates.clear();
|
||||||
for (auto c : output | split<StringView>('\n'))
|
for (auto c : output | split<StringView>('\n')
|
||||||
|
| filter([](auto s) { return not s.empty(); }))
|
||||||
m_candidates.emplace_back(c.str(), used_letters(c));
|
m_candidates.emplace_back(c.str(), used_letters(c));
|
||||||
m_token = token_to_complete;
|
m_token = token_to_complete;
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,6 +518,14 @@ CandidateList complete_filename(StringView prefix, const Regex& ignored_regex,
|
||||||
if (RankedMatch match{file, fileprefix})
|
if (RankedMatch match{file, fileprefix})
|
||||||
matches.push_back(match);
|
matches.push_back(match);
|
||||||
}
|
}
|
||||||
|
// Hack: when completing directories, also echo back the query if it
|
||||||
|
// is a valid directory. This enables menu completion to select the
|
||||||
|
// directory instead of a child.
|
||||||
|
if (only_dirs and not dirname.empty() and dirname.back() == '/' and fileprefix.empty()
|
||||||
|
and /* exists on disk */ not files.empty())
|
||||||
|
{
|
||||||
|
matches.push_back(RankedMatch{fileprefix, fileprefix});
|
||||||
|
}
|
||||||
std::sort(matches.begin(), matches.end());
|
std::sort(matches.begin(), matches.end());
|
||||||
const bool expand = (flags & FilenameFlags::Expand);
|
const bool expand = (flags & FilenameFlags::Expand);
|
||||||
return candidates(matches, expand ? parsed_dirname : dirname);
|
return candidates(matches, expand ? parsed_dirname : dirname);
|
||||||
|
|
|
@ -112,12 +112,13 @@ static Optional<SubseqRes> subsequence_match_smart_case(StringView str, StringVi
|
||||||
template<typename TestFunc>
|
template<typename TestFunc>
|
||||||
RankedMatch::RankedMatch(StringView candidate, StringView query, TestFunc func)
|
RankedMatch::RankedMatch(StringView candidate, StringView query, TestFunc func)
|
||||||
{
|
{
|
||||||
if (candidate.empty() or query.length() > candidate.length())
|
if (query.length() > candidate.length())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (query.empty())
|
if (query.empty())
|
||||||
{
|
{
|
||||||
m_candidate = candidate;
|
m_candidate = candidate;
|
||||||
|
m_matches = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +130,7 @@ RankedMatch::RankedMatch(StringView candidate, StringView query, TestFunc func)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_candidate = candidate;
|
m_candidate = candidate;
|
||||||
|
m_matches = true;
|
||||||
m_max_index = res->max_index;
|
m_max_index = res->max_index;
|
||||||
|
|
||||||
if (res->single_word)
|
if (res->single_word)
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct RankedMatch
|
||||||
bool operator<(const RankedMatch& other) const;
|
bool operator<(const RankedMatch& other) const;
|
||||||
bool operator==(const RankedMatch& other) const { return m_candidate == other.m_candidate; }
|
bool operator==(const RankedMatch& other) const { return m_candidate == other.m_candidate; }
|
||||||
|
|
||||||
explicit operator bool() const { return not m_candidate.empty(); }
|
explicit operator bool() const { return m_matches; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename TestFunc>
|
template<typename TestFunc>
|
||||||
|
@ -48,6 +48,7 @@ private:
|
||||||
friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
|
friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
|
||||||
|
|
||||||
StringView m_candidate{};
|
StringView m_candidate{};
|
||||||
|
bool m_matches = false;
|
||||||
Flags m_flags = Flags::None;
|
Flags m_flags = Flags::None;
|
||||||
int m_word_boundary_match_count = 0;
|
int m_word_boundary_match_count = 0;
|
||||||
int m_max_index = 0;
|
int m_max_index = 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user