summaryrefslogtreecommitdiff
path: root/src/input_handler.cc
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2014-04-28 21:54:00 +0100
committerMaxime Coste <frrrwww@gmail.com>2014-04-28 21:54:00 +0100
commitf68394668174492caa599442e9f7bd344a84d9d8 (patch)
tree6e267fb3c4805c1631e1b1b368af3f7430356f5e /src/input_handler.cc
parent512bfa0c65f8e089700881a4e6cbfb1d2dc8cb58 (diff)
Extract insert completion code to insert_completer.{cc,hh}
Diffstat (limited to 'src/input_handler.cc')
-rw-r--r--src/input_handler.cc380
1 files changed, 11 insertions, 369 deletions
diff --git a/src/input_handler.cc b/src/input_handler.cc
index 560d2d11..b0f3bcc5 100644
--- a/src/input_handler.cc
+++ b/src/input_handler.cc
@@ -1,18 +1,16 @@
#include "input_handler.hh"
-#include "window.hh"
-#include "utf8.hh"
-#include "user_interface.hh"
#include "buffer_manager.hh"
-#include "register_manager.hh"
-#include "normal.hh"
-#include "event_manager.hh"
+#include "buffer_utils.hh"
#include "client.hh"
#include "color_registry.hh"
-#include "file.hh"
-#include "word_db.hh"
-#include "buffer_utils.hh"
-#include "debug.hh"
+#include "event_manager.hh"
+#include "insert_completer.hh"
+#include "normal.hh"
+#include "register_manager.hh"
+#include "user_interface.hh"
+#include "utf8.hh"
+#include "window.hh"
#include <unordered_map>
@@ -616,359 +614,6 @@ private:
KeyCallback m_callback;
};
-struct InsertCompletion
-{
- BufferCoord begin;
- BufferCoord end;
- CandidateList candidates;
- size_t timestamp;
-
- bool is_valid() const { return not candidates.empty(); }
-};
-
-class InsertCompleter : public OptionManagerWatcher_AutoRegister
-{
-public:
- InsertCompleter(const Context& context)
- : OptionManagerWatcher_AutoRegister(context.options()), m_context(context)
- {}
- InsertCompleter(const InsertCompleter&) = delete;
- InsertCompleter& operator=(const InsertCompleter&) = delete;
-
- void select(int offset)
- {
- if (not setup_ifn())
- return;
-
- auto& buffer = m_context.buffer();
- m_current_candidate = (m_current_candidate + offset) % (int)m_matching_candidates.size();
- if (m_current_candidate < 0)
- m_current_candidate += m_matching_candidates.size();
- const String& candidate = m_matching_candidates[m_current_candidate];
- const auto& cursor_pos = m_context.selections().main().cursor();
- const auto prefix_len = buffer.distance(m_completions.begin, cursor_pos);
- const auto suffix_len = std::max(0_byte, buffer.distance(cursor_pos, m_completions.end));
- const auto buffer_len = buffer.byte_count();
-
- auto ref = buffer.string(m_completions.begin, m_completions.end);
- for (auto& sel : m_context.selections())
- {
- auto offset = buffer.offset(sel.cursor());
- auto pos = buffer.iterator_at(sel.cursor());
- if (offset >= prefix_len and offset + suffix_len < buffer_len and
- std::equal(ref.begin(), ref.end(), pos - prefix_len))
- {
- pos = buffer.erase(pos - prefix_len, pos + suffix_len);
- buffer.insert(pos, candidate);
- }
- }
- m_completions.end = cursor_pos;
- m_completions.begin = buffer.advance(m_completions.end, -candidate.length());
- m_completions.timestamp = buffer.timestamp();
- if (m_context.has_ui())
- m_context.ui().menu_select(m_current_candidate);
-
- // when we select a match, remove non displayed matches from the candidates
- // which are considered as invalid with the new completion timestamp
- m_completions.candidates.clear();
- std::copy(m_matching_candidates.begin(), m_matching_candidates.end()-1,
- std::back_inserter(m_completions.candidates));
- }
-
- void update()
- {
- if (m_completions.is_valid())
- {
- ByteCount longest_completion = 0;
- for (auto& candidate : m_completions.candidates)
- longest_completion = std::max(longest_completion, candidate.length());
-
- BufferCoord cursor = m_context.selections().main().cursor();
- BufferCoord compl_beg = m_completions.begin;
- if (cursor.line == compl_beg.line and
- is_in_range(cursor.column - compl_beg.column,
- ByteCount{0}, longest_completion-1))
- {
- String prefix = m_context.buffer().string(compl_beg, cursor);
-
- if (m_context.buffer().timestamp() == m_completions.timestamp)
- m_matching_candidates = m_completions.candidates;
- else
- {
- m_matching_candidates.clear();
- for (auto& candidate : m_completions.candidates)
- {
- if (candidate.substr(0, prefix.length()) == prefix)
- m_matching_candidates.push_back(candidate);
- }
- }
- if (not m_matching_candidates.empty())
- {
- m_current_candidate = m_matching_candidates.size();
- m_completions.end = cursor;
- menu_show();
- m_matching_candidates.push_back(prefix);
- return;
- }
- }
- }
- reset();
- setup_ifn();
- }
-
- void reset()
- {
- m_completions = InsertCompletion{};
- if (m_context.has_ui())
- m_context.ui().menu_hide();
- }
-
- template<typename CompleteFunc>
- bool try_complete(CompleteFunc complete_func)
- {
- auto& buffer = m_context.buffer();
- BufferCoord cursor_pos = m_context.selections().main().cursor();
- 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;
-
- kak_assert(cursor_pos >= m_completions.begin);
- m_matching_candidates = m_completions.candidates;
- m_current_candidate = m_matching_candidates.size();
- menu_show();
- m_matching_candidates.push_back(buffer.string(m_completions.begin, m_completions.end));
- return true;
- }
- using StringList = std::vector<String>;
-
- static WordDB& get_word_db(const Buffer& buffer)
- {
- static const ValueId word_db_id = ValueId::get_free_id();
- Value& cache_val = buffer.values()[word_db_id];
- if (not cache_val)
- cache_val = Value(WordDB{buffer});
- return cache_val.as<WordDB>();
- }
-
- template<bool other_buffers>
- InsertCompletion complete_word(const Buffer& buffer, BufferCoord cursor_pos)
- {
- auto pos = buffer.iterator_at(cursor_pos);
- if (pos == buffer.begin() or not is_word(*utf8::previous(pos)))
- return {};
-
- auto end = buffer.iterator_at(cursor_pos);
- auto begin = end-1;
- while (begin != buffer.begin() and is_word(*begin))
- --begin;
- if (not is_word(*begin))
- ++begin;
-
- String prefix{begin, end};
-
- while (end != buffer.end() and is_word(*end))
- ++end;
-
- String current_word{begin, end};
-
- auto& word_db = get_word_db(buffer);
- std::unordered_set<String> matches;
- auto bufmatches = word_db.find_prefix(prefix);
- matches.insert(bufmatches.begin(), bufmatches.end());
-
- if (word_db.get_word_occurences(current_word) <= 1)
- matches.erase(current_word);
-
- if (other_buffers)
- {
- for (const auto& buf : BufferManager::instance())
- {
- if (buf.get() == &buffer)
- continue;
- bufmatches = get_word_db(*buf).find_prefix(prefix);
- matches.insert(bufmatches.begin(), bufmatches.end());
- }
- }
- matches.erase(prefix);
- CandidateList result;
- std::copy(matches.begin(), matches.end(),
- inserter(result, result.begin()));
- std::sort(result.begin(), result.end());
- return { begin.coord(), end.coord(), std::move(result), buffer.timestamp() };
- }
-
- template<bool require_slash>
- InsertCompletion complete_filename(const Buffer& buffer, BufferCoord cursor_pos)
- {
- auto pos = buffer.iterator_at(cursor_pos);
- auto begin = pos;
-
- auto is_filename = [](char c)
- {
- return isalnum(c) or c == '/' or c == '.' or c == '_' or c == '-';
- };
- while (begin != buffer.begin() and is_filename(*(begin-1)))
- --begin;
-
- if (begin == pos)
- return {};
-
- String prefix{begin, pos};
- if (require_slash and not contains(prefix, '/'))
- return {};
-
- StringList res;
- if (prefix.front() == '/')
- res = Kakoune::complete_filename(prefix, Regex{});
- else
- {
- for (auto dir : options()["path"].get<StringList>())
- {
- if (not dir.empty() and dir.back() != '/')
- dir += '/';
- for (auto& filename : Kakoune::complete_filename(dir + prefix, Regex{}))
- res.push_back(filename.substr(dir.length()));
- }
- }
- if (res.empty())
- return {};
- return { begin.coord(), pos.coord(), std::move(res), buffer.timestamp() };
- }
-
- InsertCompletion complete_option(const Buffer& buffer, BufferCoord cursor_pos, const String& option_name)
- {
- const StringList& opt = options()[option_name].get<StringList>();;
- if (opt.empty())
- return {};
-
- auto& desc = opt[0];
- static const Regex re(R"((\d+)\.(\d+)(?:\+(\d+))?@(\d+))");
- boost::smatch match;
- if (boost::regex_match(desc.begin(), desc.end(), match, re))
- {
- BufferCoord coord{ str_to_int(match[1].str()) - 1, str_to_int(match[2].str()) - 1 };
- if (not buffer.is_valid(coord))
- return {};
- auto end = coord;
- if (match[3].matched)
- {
- ByteCount len = str_to_int(match[3].str());
- end = buffer.advance(coord, len);
- }
- size_t timestamp = (size_t)str_to_int(match[4].str());
-
- ByteCount longest_completion = 0;
- for (auto it = opt.begin() + 1; it != opt.end(); ++it)
- longest_completion = std::max(longest_completion, it->length());
-
- if (timestamp == buffer.timestamp() and
- cursor_pos.line == coord.line and cursor_pos.column >= coord.column and
- buffer.distance(coord, cursor_pos) < longest_completion)
- return { coord, end, { opt.begin() + 1, opt.end() }, timestamp };
- }
- return {};
- }
-
- InsertCompletion complete_line(const Buffer& buffer, BufferCoord cursor_pos)
- {
- String prefix = buffer[cursor_pos.line].substr(0_byte, cursor_pos.column);
- StringList res;
- for (LineCount l = 0_line; l < buffer.line_count(); ++l)
- {
- if (l == cursor_pos.line)
- continue;
- ByteCount len = buffer[l].length();
- if (len > cursor_pos.column and std::equal(prefix.begin(), prefix.end(), buffer[l].begin()))
- res.push_back(buffer[l].substr(0_byte, len-1));
- }
- if (res.empty())
- return {};
- std::sort(res.begin(), res.end());
- res.erase(std::unique(res.begin(), res.end()), res.end());
- return { cursor_pos.line, cursor_pos, std::move(res), buffer.timestamp() };
- }
-
-private:
- void on_option_changed(const Option& opt) override
- {
- 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();
- }
- }
-
- void menu_show()
- {
- if (not m_context.has_ui())
- return;
- DisplayCoord menu_pos = m_context.window().display_position(m_completions.begin);
-
- const CharCount tabstop = m_context.options()["tabstop"].get<int>();
- const CharCount column = get_column(m_context.buffer(), tabstop,
- m_completions.begin);
- std::vector<String> menu_entries;
- for (auto& candidate : m_matching_candidates)
- menu_entries.push_back(expand_tabs(candidate, tabstop, column));
-
- m_context.ui().menu_show(menu_entries, menu_pos,
- get_color("MenuForeground"),
- get_color("MenuBackground"),
- MenuStyle::Inline);
- m_context.ui().menu_select(m_current_candidate);
- }
-
- bool setup_ifn()
- {
- using namespace std::placeholders;
- if (not m_completions.is_valid())
- {
- auto& completers = options()["completers"].get<StringList>();
- for (auto& completer : completers)
- {
- if (completer == "filename" and
- try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
- { return complete_filename<true>(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;
- }
-
- const Context& m_context;
- InsertCompletion m_completions;
- CandidateList m_matching_candidates;
- int m_current_candidate = -1;
-};
-
class Insert : public InputMode
{
public:
@@ -1003,14 +648,11 @@ public:
if (m_mode == Mode::Complete)
{
if (key.key == 'f')
- m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
- { return m_completer.complete_filename<false>(buffer, cursor_pos); });
+ m_completer.explicit_file_complete();
if (key.key == 'w')
- m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
- { return m_completer.complete_word<true>(buffer, cursor_pos); });
+ m_completer.explicit_word_complete();
if (key.key == 'l')
- m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos)
- { return m_completer.complete_line(buffer, cursor_pos); });
+ m_completer.explicit_line_complete();
m_mode = Mode::Default;
return;
}