Insert mode completion execute completers in order, and supports multiple option

The 'completions' option is gone, just add option=completion_option_name
in the completers list.
This commit is contained in:
Maxime Coste 2014-03-15 03:14:23 +00:00
parent 19f5eb65e8
commit 36b016226c
5 changed files with 72 additions and 33 deletions

View File

@ -523,15 +523,18 @@ Some options are built in Kakoune, and can be used to control it's behaviour:
* +filetype+ _str_: arbitrary string defining the type of the file
filetype dependant actions should hook on this option changing for
activation/deactivation.
* +completions+ _str-list_: option used for external completion, the
first string should follow the format
_<line>.<column>[+<length>]@<timestamp>_ to define where the completion
apply in the buffer, and the other strings are the candidates.
* +path+ _str-list_: directories to search for gf command.
* +completers+ _str-list_: completion systems to use for insert mode
completion. Support +option+ which use the +completions+ option, and
+word=all+ or +word=buffer+ which complete using words in all buffers
(+word=all+) or only the current one (+word=buffer+)
completion. given completers are tried in order until one generate some
completion candidates. Existing completers are:
- +word=all+ or +word=buffer+ which complete using words in all buffers
(+word=all+) or only the current one (+word=buffer+)
- +filename+ which tries to detect when a filename is being entered and
provides completion based on local filesystem.
- +option=<opt-name>+ where <opt-name> is a _str-list_ option. The first
element of the list should follow the format:
_<line>.<column>[+<length>]@<timestamp>_ to define where the completion
apply in the buffer, and the other strings are the candidates.
* +autoreload+ _yesnoask_: auto reload the buffers when an external
modification is detected.

View File

@ -1,5 +1,6 @@
decl -hidden str clang_filename
decl str clang_options
decl str-list clang_completions
def clang-complete %{
%sh{
@ -23,12 +24,13 @@ def clang-complete %{
for cmp in ${output}; do
completions="${completions}:${cmp}"
done
echo "eval -client $kak_client %[ echo completed; set buffer completions '${completions}' ]" | kak -p ${kak_session}
echo "eval -client $kak_client %[ echo completed; set buffer clang_completions '${completions}' ]" | kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null &
}
}
def clang-enable-autocomplete %{
set window completers %sh{ echo "'option=clang_completions:${kak_opt_completers}'" }
hook window -id clang-autocomplete InsertIdle .* %{ try %{
exec -draft <a-h><a-k>(\.|->|::).$<ret>
echo 'completing...'
@ -36,4 +38,7 @@ def clang-enable-autocomplete %{
} }
}
def clang-disable-autocomplete %{ rmhooks window clang-autocomplete }
def clang-disable-autocomplete %{
set window completers %sh{ echo "'${kak_opt_completers}'" | sed -e 's/option=clang_completions://g' }
rmhooks window clang-autocomplete
}

View File

@ -31,7 +31,7 @@ def tag-complete %{ eval -draft %{
%sh{ (
compl=$(readtags -p "$kak_selection" | cut -f 1 | sort | uniq | sed -e 's/:/\\:/g' | sed -e 's/\n/:/g' )
compl="${kak_cursor_line}.${kak_cursor_column}+${#kak_selection}@${kak_timestamp}:${compl}"
echo "set buffer=$kak_bufname completions '${compl}'" | kak -p ${kak_session}
echo "set buffer=$kak_bufname ctags_completions '${compl}'" | kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null & }
}}

View File

@ -11,6 +11,7 @@
#include "color_registry.hh"
#include "file.hh"
#include "word_db.hh"
#include "debug.hh"
#include <unordered_map>
@ -711,12 +712,20 @@ public:
m_context.ui().menu_hide();
}
template<BufferCompletion (BufferCompleter::*complete_func)(const Buffer&, BufferCoord)>
bool try_complete()
template<typename CompleteFunc>
bool try_complete(CompleteFunc complete_func)
{
auto& buffer = m_context.buffer();
BufferCoord cursor_pos = m_context.selections().main().cursor();
m_completions = (this->*complete_func)(buffer, cursor_pos);
try
{
m_completions = complete_func(buffer, cursor_pos);
}
catch (runtime_error& e)
{
write_debug("error while trying to run completer: "_str + e.what());
return false;
}
if (not m_completions.is_valid())
return false;
@ -812,9 +821,9 @@ public:
return { begin.coord(), pos.coord(), std::move(res), buffer.timestamp() };
}
BufferCompletion complete_option(const Buffer& buffer, BufferCoord cursor_pos)
BufferCompletion complete_option(const Buffer& buffer, BufferCoord cursor_pos, const String& option_name)
{
const StringList& opt = options()["completions"].get<StringList>();
const StringList& opt = options()[option_name].get<StringList>();;
if (opt.empty())
return {};
@ -868,7 +877,14 @@ public:
private:
void on_option_changed(const Option& opt) override
{
if (opt.name() == "completions")
auto& completers = options()["completers"].get<StringList>();
StringList option_names;
for (auto& completer : completers)
{
if (completer.substr(0_byte, 7_byte) == "option=")
option_names.emplace_back(completer.substr(7_byte));
}
if (contains(option_names, opt.name()))
{
reset();
setup_ifn();
@ -889,18 +905,29 @@ private:
bool setup_ifn()
{
using namespace std::placeholders;
if (not m_completions.is_valid())
{
auto& completers = options()["completers"].get<StringList>();
if (contains(completers, "option") and try_complete<&BufferCompleter::complete_option>())
return true;
if (contains(completers, "word=buffer") and try_complete<&BufferCompleter::complete_word<false>>())
return true;
if (contains(completers, "word=all") and try_complete<&BufferCompleter::complete_word<true>>())
return true;
if (contains(completers, "filename") and try_complete<&BufferCompleter::complete_filename>())
return true;
for (auto& completer : completers)
{
if (completer == "filename" and
try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return complete_filename(buffer, cursor_pos); }))
return true;
if (completer.substr(0_byte, 7_byte) == "option=" and
try_complete([&, this](const Buffer& buffer, BufferCoord cursor_pos)
{ return complete_option(buffer, cursor_pos, completer.substr(7_byte)); }))
return true;
if (completer == "word=buffer" and
try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return complete_word<false>(buffer, cursor_pos); }))
return true;
if (completer == "word=all" and
try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return complete_word<true>(buffer, cursor_pos); }))
return true;
}
return false;
}
return true;
@ -946,13 +973,14 @@ public:
if (m_mode == Mode::Complete)
{
if (key.key == 'f')
m_completer.try_complete<&BufferCompleter::complete_filename>();
m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return m_completer.complete_filename(buffer, cursor_pos); });
if (key.key == 'w')
m_completer.try_complete<&BufferCompleter::complete_word<true>>();
if (key.key == 'o')
m_completer.try_complete<&BufferCompleter::complete_option>();
m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return m_completer.complete_word<true>(buffer, cursor_pos); });
if (key.key == 'l')
m_completer.try_complete<&BufferCompleter::complete_line>();
m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
{ return m_completer.complete_line(buffer, cursor_pos); });
m_mode = Mode::Default;
return;
}

View File

@ -133,15 +133,18 @@ GlobalOptions::GlobalOptions()
declare_option<bool>("aligntab", false);
declare_option<Regex>("ignored_files", Regex{R"(^(\..*|.*\.(o|so|a))$)"});
declare_option<String>("filetype", "");
declare_option<std::vector<String>>("completions", {});
declare_option<std::vector<String>>("path", { "./", "/usr/include" });
declare_option<std::vector<String>>("completers", {"option", "filename", "word=buffer"},
declare_option<std::vector<String>>("completers", {"filename", "word=buffer"},
Option::Flags::None,
[](const std::vector<String>& s) {
static const auto values = {"option", "word=buffer", "word=all", "filename" };
static const auto values = {"word=buffer", "word=all", "filename" };
for (auto& v : s)
{
if (v.substr(0_byte, 7_byte) == "option=")
continue;
if (not contains(values, v))
throw runtime_error(v + " is not a recognised value for completers");
}
});
declare_option<YesNoAsk>("autoreload", Ask);
}