ranked match: prefer input order over alphabetical order for user-specified completions
When using either of set-option g completers option=my_option prompt -shell-script-candidates ... While the search text is empty, the completions will be sorted alphabetically. This is bad because it means the most important entries are not listed first, making them harder to select or even spot. Let's apply input order before resorting to sorting alphabetically. In theory there is a more elegant solution: sort candidates (except if they're user input) before passing them to RankedMatch, and then always use stable sort. However that doesn't work because we use a heap which doesn't support stable sort. Closes #1709, #4813
This commit is contained in:
parent
d6215dc25d
commit
658c3385a9
|
@ -25,7 +25,7 @@ define-command -params ..1 \
|
||||||
cut -f 1 "$tags" | grep -v '^!' | uniq > "$namecache"
|
cut -f 1 "$tags" | grep -v '^!' | uniq > "$namecache"
|
||||||
fi
|
fi
|
||||||
cat "$namecache"
|
cat "$namecache"
|
||||||
done} \
|
done | sort } \
|
||||||
-docstring %{
|
-docstring %{
|
||||||
ctags-search [<symbol>]: jump to a symbol's definition
|
ctags-search [<symbol>]: jump to a symbol's definition
|
||||||
If no symbol is passed then the current selection is used as symbol name
|
If no symbol is passed then the current selection is used as symbol name
|
||||||
|
|
|
@ -189,7 +189,7 @@ complete-command doc shell-script-candidates %{
|
||||||
/^\[\[[^\]]+\]\]/ { sub(/^\[\[/, ""); sub(/\]\].*/, ""); print }
|
/^\[\[[^\]]+\]\]/ { sub(/^\[\[/, ""); sub(/\]\].*/, ""); print }
|
||||||
' < $page | tr '[A-Z ]' '[a-z-]'
|
' < $page | tr '[A-Z ]' '[a-z-]'
|
||||||
fi;;
|
fi;;
|
||||||
esac
|
esac | sort
|
||||||
}
|
}
|
||||||
|
|
||||||
alias global help doc
|
alias global help doc
|
||||||
|
|
|
@ -62,27 +62,49 @@ define-command -params 1.. \
|
||||||
Available commands:
|
Available commands:
|
||||||
add
|
add
|
||||||
apply (alias for "patch git apply")
|
apply (alias for "patch git apply")
|
||||||
rm
|
|
||||||
reset
|
|
||||||
blame
|
blame
|
||||||
commit
|
|
||||||
checkout
|
checkout
|
||||||
|
commit
|
||||||
diff
|
diff
|
||||||
|
edit
|
||||||
|
grep
|
||||||
hide-blame
|
hide-blame
|
||||||
hide-diff
|
hide-diff
|
||||||
init
|
init
|
||||||
log
|
log
|
||||||
next-hunk
|
next-hunk
|
||||||
prev-hunk
|
prev-hunk
|
||||||
|
reset
|
||||||
|
rm
|
||||||
show
|
show
|
||||||
show-branch
|
show-branch
|
||||||
show-diff
|
show-diff
|
||||||
status
|
status
|
||||||
update-diff
|
update-diff
|
||||||
grep
|
|
||||||
} -shell-script-candidates %{
|
} -shell-script-candidates %{
|
||||||
if [ $kak_token_to_complete -eq 0 ]; then
|
if [ $kak_token_to_complete -eq 0 ]; then
|
||||||
printf "add\napply\nrm\nreset\nblame\ncommit\ncheckout\ndiff\nhide-blame\nhide-diff\nlog\nnext-hunk\nprev-hunk\nshow\nshow-branch\nshow-diff\ninit\nstatus\nupdate-diff\ngrep\nedit\n"
|
printf %s\\n \
|
||||||
|
apply \
|
||||||
|
blame \
|
||||||
|
checkout \
|
||||||
|
commit \
|
||||||
|
diff \
|
||||||
|
edit \
|
||||||
|
grep \
|
||||||
|
hide-blame \
|
||||||
|
hide-diff \
|
||||||
|
init \
|
||||||
|
log \
|
||||||
|
next-hunk \
|
||||||
|
prev-hunk \
|
||||||
|
reset \
|
||||||
|
rm \
|
||||||
|
show \
|
||||||
|
show-branch \
|
||||||
|
show-diff \
|
||||||
|
status \
|
||||||
|
update-diff \
|
||||||
|
;
|
||||||
else
|
else
|
||||||
case "$1" in
|
case "$1" in
|
||||||
commit) printf -- "--amend\n--no-edit\n--all\n--reset-author\n--fixup\n--squash\n"; git ls-files -m ;;
|
commit) printf -- "--amend\n--no-edit\n--all\n--reset-author\n--fixup\n--squash\n"; git ls-files -m ;;
|
||||||
|
|
|
@ -63,7 +63,10 @@ define-command -hidden -params ..3 man-impl %{ evaluate-commands %sh{
|
||||||
|
|
||||||
define-command -params ..1 \
|
define-command -params ..1 \
|
||||||
-shell-script-candidates %{
|
-shell-script-candidates %{
|
||||||
find /usr/share/man/ $(printf %s "${MANPATH}" | sed 's/:/ /') -name '*.[1-8]*' | sed 's,^.*/\(.*\)\.\([1-8][a-zA-Z]*\).*$,\1(\2),'
|
find /usr/share/man/ $(printf %s "${MANPATH}" |
|
||||||
|
sed 's/:/ /') -name '*.[1-8]*' |
|
||||||
|
sed 's,^.*/\(.*\)\.\([1-8][a-zA-Z]*\).*$,\1(\2),' |
|
||||||
|
sort
|
||||||
} \
|
} \
|
||||||
-docstring %{
|
-docstring %{
|
||||||
man [<page>]: manpage viewer wrapper
|
man [<page>]: manpage viewer wrapper
|
||||||
|
|
|
@ -337,10 +337,13 @@ private:
|
||||||
{
|
{
|
||||||
UsedLetters query_letters = used_letters(query);
|
UsedLetters query_letters = used_letters(query);
|
||||||
Vector<RankedMatch> matches;
|
Vector<RankedMatch> matches;
|
||||||
for (auto&& candidate : m_candidates)
|
for (auto&& [i, candidate] : m_candidates | enumerate())
|
||||||
{
|
{
|
||||||
if (RankedMatch m{candidate.first, candidate.second, query, query_letters})
|
if (RankedMatch m{candidate.first, candidate.second, query, query_letters})
|
||||||
|
{
|
||||||
|
m.set_input_sequence_number(i);
|
||||||
matches.push_back(m);
|
matches.push_back(m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t max_count = 100;
|
constexpr size_t max_count = 100;
|
||||||
|
|
|
@ -298,10 +298,11 @@ InsertCompletion complete_option(const SelectionList& sels,
|
||||||
StringView query = buffer.substr(coord, cursor_pos);
|
StringView query = buffer.substr(coord, cursor_pos);
|
||||||
Vector<RankedMatchAndInfo> matches;
|
Vector<RankedMatchAndInfo> matches;
|
||||||
|
|
||||||
for (auto& candidate : opt.list)
|
for (auto&& [i, candidate] : opt.list | enumerate())
|
||||||
{
|
{
|
||||||
if (RankedMatchAndInfo match{std::get<0>(candidate), query})
|
if (RankedMatchAndInfo match{std::get<0>(candidate), query})
|
||||||
{
|
{
|
||||||
|
match.set_input_sequence_number(i);
|
||||||
match.on_select = std::get<1>(candidate);
|
match.on_select = std::get<1>(candidate);
|
||||||
auto& menu = std::get<2>(candidate);
|
auto& menu = std::get<2>(candidate);
|
||||||
match.menu_entry = not menu.empty() ?
|
match.menu_entry = not menu.empty() ?
|
||||||
|
|
|
@ -208,6 +208,9 @@ bool RankedMatch::operator<(const RankedMatch& other) const
|
||||||
if (m_max_index != other.m_max_index)
|
if (m_max_index != other.m_max_index)
|
||||||
return m_max_index < other.m_max_index;
|
return m_max_index < other.m_max_index;
|
||||||
|
|
||||||
|
if (m_input_sequence_number != other.m_input_sequence_number)
|
||||||
|
return m_input_sequence_number < other.m_input_sequence_number;
|
||||||
|
|
||||||
// Reorder codepoints to improve matching behaviour
|
// Reorder codepoints to improve matching behaviour
|
||||||
auto order = [](Codepoint cp) { return cp == '/' ? 0 : cp; };
|
auto order = [](Codepoint cp) { return cp == '/' ? 0 : cp; };
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct RankedMatch
|
||||||
|
|
||||||
explicit operator bool() const { return m_matches; }
|
explicit operator bool() const { return m_matches; }
|
||||||
|
|
||||||
|
void set_input_sequence_number(size_t i) { m_input_sequence_number = i; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename TestFunc>
|
template<typename TestFunc>
|
||||||
RankedMatch(StringView candidate, StringView query, TestFunc test);
|
RankedMatch(StringView candidate, StringView query, TestFunc test);
|
||||||
|
@ -54,6 +56,7 @@ private:
|
||||||
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;
|
||||||
|
size_t m_input_sequence_number = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user