Move insert mode completion functions into BufferCompleter
This commit is contained in:
parent
6b66a3dfe5
commit
3fe0b8e719
|
@ -497,122 +497,6 @@ struct BufferCompletion
|
||||||
bool is_valid() const { return not candidates.empty(); }
|
bool is_valid() const { return not candidates.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
static BufferCompletion complete_word(const Buffer& buffer,
|
|
||||||
BufferCoord cursor_pos,
|
|
||||||
bool other_buffers)
|
|
||||||
{
|
|
||||||
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 ex = R"(\<\Q)" + String{begin, end} + R"(\E\w+\>)";
|
|
||||||
Regex re(ex.begin(), ex.end());
|
|
||||||
using RegexIt = boost::regex_iterator<BufferIterator>;
|
|
||||||
|
|
||||||
std::unordered_set<String> matches;
|
|
||||||
for (RegexIt it(buffer.begin(), buffer.end(), re), re_end; it != re_end; ++it)
|
|
||||||
{
|
|
||||||
auto& match = (*it)[0];
|
|
||||||
if (match.first <= pos and pos < match.second)
|
|
||||||
continue;
|
|
||||||
matches.insert(String{match.first, match.second});
|
|
||||||
}
|
|
||||||
if (other_buffers)
|
|
||||||
{
|
|
||||||
for (const auto& buf : BufferManager::instance())
|
|
||||||
{
|
|
||||||
if (buf.get() == &buffer)
|
|
||||||
continue;
|
|
||||||
for (RegexIt it(buf->begin(), buf->end(), re), re_end; it != re_end; ++it)
|
|
||||||
{
|
|
||||||
auto& match = (*it)[0];
|
|
||||||
matches.insert(String{match.first, match.second});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CandidateList result;
|
|
||||||
std::copy(make_move_iterator(matches.begin()),
|
|
||||||
make_move_iterator(matches.end()),
|
|
||||||
inserter(result, result.begin()));
|
|
||||||
std::sort(result.begin(), result.end());
|
|
||||||
return { begin.coord(), end.coord(), std::move(result), buffer.timestamp() };
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_filename(char c)
|
|
||||||
{
|
|
||||||
return isalnum(c) or c == '/' or c == '.' or c == '_' or c == '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
static BufferCompletion complete_filename(const Buffer& buffer,
|
|
||||||
BufferCoord cursor_pos,
|
|
||||||
memoryview<String> path)
|
|
||||||
{
|
|
||||||
auto pos = buffer.iterator_at(cursor_pos);
|
|
||||||
auto begin = pos;
|
|
||||||
|
|
||||||
while (begin != buffer.begin() and is_filename(*(begin-1)))
|
|
||||||
--begin;
|
|
||||||
|
|
||||||
if (begin == pos)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
String prefix{begin, pos};
|
|
||||||
std::vector<String> res;
|
|
||||||
for (auto dir : path)
|
|
||||||
{
|
|
||||||
if (not dir.empty() and dir.back() != '/')
|
|
||||||
dir += '/';
|
|
||||||
for (auto& filename : 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() };
|
|
||||||
}
|
|
||||||
|
|
||||||
static BufferCompletion complete_opt(const Buffer& buffer,
|
|
||||||
BufferCoord cursor_pos,
|
|
||||||
OptionManager& options)
|
|
||||||
{
|
|
||||||
using StringList = std::vector<String>;
|
|
||||||
const StringList& opt = options["completions"].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 {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class BufferCompleter : public OptionManagerWatcher_AutoRegister
|
class BufferCompleter : public OptionManagerWatcher_AutoRegister
|
||||||
{
|
{
|
||||||
|
@ -710,6 +594,118 @@ public:
|
||||||
m_context.ui().menu_hide();
|
m_context.ui().menu_hide();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
using StringList = std::vector<String>;
|
||||||
|
|
||||||
|
template<bool other_buffers>
|
||||||
|
BufferCompletion 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 ex = R"(\<\Q)" + String{begin, end} + R"(\E\w+\>)";
|
||||||
|
Regex re(ex.begin(), ex.end());
|
||||||
|
using RegexIt = boost::regex_iterator<BufferIterator>;
|
||||||
|
|
||||||
|
std::unordered_set<String> matches;
|
||||||
|
for (RegexIt it(buffer.begin(), buffer.end(), re), re_end; it != re_end; ++it)
|
||||||
|
{
|
||||||
|
auto& match = (*it)[0];
|
||||||
|
if (match.first <= pos and pos < match.second)
|
||||||
|
continue;
|
||||||
|
matches.insert(String{match.first, match.second});
|
||||||
|
}
|
||||||
|
if (other_buffers)
|
||||||
|
{
|
||||||
|
for (const auto& buf : BufferManager::instance())
|
||||||
|
{
|
||||||
|
if (buf.get() == &buffer)
|
||||||
|
continue;
|
||||||
|
for (RegexIt it(buf->begin(), buf->end(), re), re_end; it != re_end; ++it)
|
||||||
|
{
|
||||||
|
auto& match = (*it)[0];
|
||||||
|
matches.insert(String{match.first, match.second});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CandidateList result;
|
||||||
|
std::copy(make_move_iterator(matches.begin()),
|
||||||
|
make_move_iterator(matches.end()),
|
||||||
|
inserter(result, result.begin()));
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
return { begin.coord(), end.coord(), std::move(result), buffer.timestamp() };
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferCompletion 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};
|
||||||
|
StringList res;
|
||||||
|
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() };
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferCompletion complete_opt(const Buffer& buffer, BufferCoord cursor_pos)
|
||||||
|
{
|
||||||
|
const StringList& opt = options()["completions"].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 {};
|
||||||
|
}
|
||||||
|
|
||||||
void on_option_changed(const Option& opt) override
|
void on_option_changed(const Option& opt) override
|
||||||
{
|
{
|
||||||
if (opt.name() == "completions")
|
if (opt.name() == "completions")
|
||||||
|
@ -734,17 +730,16 @@ private:
|
||||||
if (not m_completions.is_valid())
|
if (not m_completions.is_valid())
|
||||||
{
|
{
|
||||||
auto& buffer = m_context.buffer();
|
auto& buffer = m_context.buffer();
|
||||||
auto& completers = options()["completers"].get<std::vector<String>>();
|
auto& completers = options()["completers"].get<StringList>();
|
||||||
BufferCoord cursor_pos = m_context.editor().main_selection().last();
|
BufferCoord cursor_pos = m_context.editor().main_selection().last();
|
||||||
if (contains(completers, "option"))
|
if (contains(completers, "option"))
|
||||||
m_completions = complete_opt(buffer, cursor_pos, options());
|
m_completions = complete_opt(buffer, cursor_pos);
|
||||||
if (not m_completions.is_valid() and
|
if (not m_completions.is_valid() and contains(completers, "word=buffer"))
|
||||||
(contains(completers, "word=buffer") or contains(completers, "word=all")))
|
m_completions = complete_word<false>(buffer, cursor_pos);
|
||||||
m_completions = complete_word(buffer, cursor_pos,
|
if (not m_completions.is_valid() and contains(completers, "word=all"))
|
||||||
contains(completers, "word=all"));
|
m_completions = complete_word<true>(buffer, cursor_pos);
|
||||||
if (not m_completions.is_valid() and contains(completers, "filename"))
|
if (not m_completions.is_valid() and contains(completers, "filename"))
|
||||||
m_completions = complete_filename(buffer, cursor_pos,
|
m_completions = complete_filename(buffer, cursor_pos);
|
||||||
options()["path"].get<std::vector<String>>());
|
|
||||||
if (not m_completions.is_valid())
|
if (not m_completions.is_valid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user