Complex include completion.

This commit is contained in:
scturtle 2018-02-03 22:36:07 +08:00
parent e16753d261
commit 15dd552610
3 changed files with 59 additions and 47 deletions

View File

@ -45,38 +45,42 @@ lsPosition CharPos(std::string_view search,
return result; return result;
} }
std::tuple<bool, std::string, std::string> ShouldRunIncludeCompletion( ParseIncludeLineResult ParseIncludeLine(const std::string& line) {
const std::string& line) { static const std::regex pattern(
size_t start = 0; "(\\s*)" // [1]: spaces before '#'
while (start < line.size() && isspace(line[start])) "#" //
++start; "(\\s*)" // [2]: spaces after '#'
// Must start with '#'. "([^\\s\"<]*)" // [3]: "include"
if (start >= line.size() || line[start] != '#') "(\\s*)" // [4]: spaces before quote
return std::make_tuple(false, "", ""); "([\"<])?" // [5]: the first quote char
++start; "([^\\s\">]*)" // [6]: path of file
// Ingore "include" and following spaces. "[\">]?" //
if (line.compare(start, 7, "include") == 0) { "(.*)"); // [7]: suffix after quote char
start += 7; std::smatch match;
while (start < line.size() && isspace(line[start])) bool ok = std::regex_match(line, match, pattern);
++start; std::string text = match[3].str() + match[6].str();
return {ok, text, match};
} }
// Determine the surrounding characters.
std::string surround;
if (line[start] == '"') {
surround = "\"\"";
++start;
} else if (line[start] == '<') {
surround = "<>";
++start;
} else
surround = "<>";
// Fix the prefix for completion.
size_t end = start;
while (end < line.size() && line[end] != '\"' && line[end] != '>')
++end;
std::string prefix = line.substr(start, end - start);
return std::make_tuple(true, surround, prefix); void DecorateIncludePaths(const std::smatch& match,
std::vector<lsCompletionItem>* items) {
char quote0, quote1;
if (match[5].compare("\"") == 0)
quote0 = quote1 = '"';
else
quote0 = '<', quote1 = '>';
std::string spaces_between_include_and_quote =
match[3].compare("include") == 0 ? match[4].str() : " ";
std::string prefix = match[1].str() + '#' + match[2].str() + "include" +
spaces_between_include_and_quote + quote0;
std::string suffix = std::string(1, quote1) + match[7].str();
for (lsCompletionItem& item : *items) {
item.textEdit->newText = prefix + item.textEdit->newText + suffix;
item.label = prefix + item.label.substr(7) + suffix;
}
} }
// TODO: eliminate |line_number| param. // TODO: eliminate |line_number| param.
@ -229,6 +233,8 @@ bool SubsequenceMatch(std::string_view search, std::string_view content) {
return true; return true;
} }
// Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found.
std::tuple<bool, int> SubsequenceCountSkip(std::string_view search, std::tuple<bool, int> SubsequenceCountSkip(std::string_view search,
std::string_view content) { std::string_view content) {
bool hasUppercaseLetter = std::any_of(search.begin(), search.end(), isupper); bool hasUppercaseLetter = std::any_of(search.begin(), search.end(), isupper);

View File

@ -4,6 +4,7 @@
#include <string_view.h> #include <string_view.h>
#include <regex>
#include <string> #include <string>
#include <tuple> #include <tuple>
@ -14,8 +15,17 @@ lsPosition CharPos(std::string_view search,
char character, char character,
int character_offset = 0); int character_offset = 0);
std::tuple<bool, std::string, std::string> ShouldRunIncludeCompletion( struct ParseIncludeLineResult
const std::string& line); {
bool ok;
std::string text; // include the "include" part
std::smatch match;
};
ParseIncludeLineResult ParseIncludeLine(const std::string& line);
void DecorateIncludePaths(const std::smatch& match,
std::vector<lsCompletionItem>* items);
// TODO: eliminate |line_number| param. // TODO: eliminate |line_number| param.
optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line); optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line);

View File

@ -233,11 +233,9 @@ struct TextDocumentCompletionHandler : MessageHandler {
&existing_completion); &existing_completion);
} }
bool yes; ParseIncludeLineResult result = ParseIncludeLine(buffer_line);
std::string surround, prefix;
std::tie(yes, surround, prefix) = ShouldRunIncludeCompletion(buffer_line);
if (yes) { if (result.ok) {
Out_TextDocumentComplete out; Out_TextDocumentComplete out;
out.id = request->id; out.id = request->id;
@ -252,22 +250,20 @@ struct TextDocumentCompletionHandler : MessageHandler {
lock.unlock(); lock.unlock();
} }
FilterAndSortCompletionResponse(&out, prefix, // Needed by |FilterAndSortCompletionResponse|.
config->completion.filterAndSort); // Will be removed in |DecorateIncludePaths|.
for (lsCompletionItem& item : out.result.items)
item.label = "include" + item.label;
FilterAndSortCompletionResponse(&out, result.text,
config->completion.filterAndSort);
DecorateIncludePaths(result.match, &out.result.items);
auto decorator = [&](std::string& text) {
std::string result = "#include ";
result += surround[0] + text + surround[1];
text = result;
};
LOG_S(INFO) << "DEBUG prefix " << prefix;
for (lsCompletionItem& item : out.result.items) { for (lsCompletionItem& item : out.result.items) {
item.textEdit->range.start.line = request->params.position.line; item.textEdit->range.start.line = request->params.position.line;
item.textEdit->range.start.character = 0; item.textEdit->range.start.character = 0;
item.textEdit->range.end.line = request->params.position.line; item.textEdit->range.end.line = request->params.position.line;
item.textEdit->range.end.character = (int)buffer_line.size(); item.textEdit->range.end.character = (int)buffer_line.size();
decorator(item.textEdit->newText);
decorator(item.label);
} }
QueueManager::WriteStdout(IpcId::TextDocumentCompletion, out); QueueManager::WriteStdout(IpcId::TextDocumentCompletion, out);