Better :: lexing and fix ci

This commit is contained in:
Fangrui Song 2018-02-22 15:49:16 -08:00
parent 61b5ef4fa0
commit d4c49730fd
5 changed files with 56 additions and 63 deletions

View File

@ -3,6 +3,7 @@
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include <algorithm> #include <algorithm>
#include <iostream>
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent // VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
// text documents. // text documents.
@ -197,35 +198,31 @@ void LexFunctionDeclaration(const std::string& buffer_content,
*insert_text = result; *insert_text = result;
} }
std::string LexWordAroundPos(lsPosition position, const std::string& content) { std::string_view LexIdentifierAroundPos(lsPosition position,
int index = GetOffsetForPosition(position, content); std::string_view content) {
int start = GetOffsetForPosition(position, content);
int end = start + 1;
char c;
int start = index; // We search for :: before the cursor but not after to get the qualifier.
int end = index + 1; for (; start > 0; start--) {
c = content[start - 1];
// We search for : before the cursor but not after to get the qualifier. if (isalnum(c) || c == '_')
while (start > 0) { ;
char c = content[start - 1]; else if (c == ':' && start > 1 && content[start - 2] == ':')
if (isalnum(c) || c == '_' || c == ':') { start--;
--start; else
} else {
break; break;
}
} }
while (end < (int)content.size()) { for (; end < (int)content.size(); end++)
char c = content[end]; if (c = content[end], !(isalnum(c) || c == '_'))
if (isalnum(c) || c == '_') {
++end;
} else {
break; break;
}
}
return content.substr(start, end - start); return content.substr(start, end - start);
} }
bool SubsequenceMatch(std::string_view search, std::string_view content) { bool SubsequenceMatchIgnoreCase(std::string_view search, std::string_view content) {
size_t j = 0; size_t j = 0;
for (size_t i = 0; i < search.size(); i++) { for (size_t i = 0; i < search.size(); i++) {
char search_char = tolower(search[i]); char search_char = tolower(search[i]);
@ -280,33 +277,29 @@ TEST_SUITE("Offset") {
TEST_SUITE("Substring") { TEST_SUITE("Substring") {
TEST_CASE("match") { TEST_CASE("match") {
// Sanity.
REQUIRE(SubsequenceMatch("a", "aa"));
REQUIRE(SubsequenceMatch("aa", "aa"));
// Empty string matches anything. // Empty string matches anything.
REQUIRE(SubsequenceMatch("", "")); REQUIRE(SubsequenceMatchIgnoreCase("", ""));
REQUIRE(SubsequenceMatch("", "aa")); REQUIRE(SubsequenceMatchIgnoreCase("", "aa"));
// Match in start/middle/end. // Match in start/middle/end.
REQUIRE(SubsequenceMatch("a", "abbbb")); REQUIRE(SubsequenceMatchIgnoreCase("a", "abbbb"));
REQUIRE(SubsequenceMatch("a", "bbabb")); REQUIRE(SubsequenceMatchIgnoreCase("a", "bbabb"));
REQUIRE(SubsequenceMatch("a", "bbbba")); REQUIRE(SubsequenceMatchIgnoreCase("a", "bbbba"));
REQUIRE(SubsequenceMatch("aa", "aabbb")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "aabbb"));
REQUIRE(SubsequenceMatch("aa", "bbaab")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "bbaab"));
REQUIRE(SubsequenceMatch("aa", "bbbaa")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "bbbaa"));
// Capitalization. // Capitalization.
REQUIRE(SubsequenceMatch("aa", "aA")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "aA"));
REQUIRE(SubsequenceMatch("aa", "Aa")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "Aa"));
REQUIRE(SubsequenceMatch("aa", "AA")); REQUIRE(SubsequenceMatchIgnoreCase("aa", "AA"));
// Token skipping. // Token skipping.
REQUIRE(SubsequenceMatch("ad", "abcd")); REQUIRE(SubsequenceMatchIgnoreCase("ad", "abcd"));
REQUIRE(SubsequenceMatch("ad", "ABCD")); REQUIRE(SubsequenceMatchIgnoreCase("ad", "ABCD"));
// Ordering. // Ordering.
REQUIRE(!SubsequenceMatch("ad", "dcba")); REQUIRE(!SubsequenceMatchIgnoreCase("ad", "dcba"));
} }
TEST_CASE("skip") { TEST_CASE("skip") {
@ -420,34 +413,33 @@ TEST_SUITE("LexFunctionDeclaration") {
TEST_SUITE("LexWordAroundPos") { TEST_SUITE("LexWordAroundPos") {
TEST_CASE("edges") { TEST_CASE("edges") {
std::string content = "Foobar"; std::string content = "Foobar";
REQUIRE(LexWordAroundPos(CharPos(content, 'F'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'F'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'o'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'o'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'b'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'b'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'a'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'a'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'r'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'r'), content) == "Foobar");
} }
TEST_CASE("simple") { TEST_CASE("simple") {
std::string content = " Foobar "; std::string content = " Foobar ";
REQUIRE(LexWordAroundPos(CharPos(content, 'F'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'F'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'o'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'o'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'b'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'b'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'a'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'a'), content) == "Foobar");
REQUIRE(LexWordAroundPos(CharPos(content, 'r'), content) == "Foobar"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'r'), content) == "Foobar");
} }
TEST_CASE("underscores and numbers") { TEST_CASE("underscores, numbers and ::") {
std::string content = " _my_t5ype7 "; std::string content = " file:ns::_my_t5ype7 ";
REQUIRE(LexWordAroundPos(CharPos(content, '_'), content) == "_my_t5ype7"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'f'), content) == "file");
REQUIRE(LexWordAroundPos(CharPos(content, '5'), content) == "_my_t5ype7"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 's'), content) == "ns");
REQUIRE(LexWordAroundPos(CharPos(content, 'e'), content) == "_my_t5ype7"); REQUIRE(LexIdentifierAroundPos(CharPos(content, 'y'), content) == "ns::_my_t5ype7");
REQUIRE(LexWordAroundPos(CharPos(content, '7'), content) == "_my_t5ype7");
} }
TEST_CASE("dot, dash, colon are skipped") { TEST_CASE("dot, dash, colon are skipped") {
std::string content = "1. 2- 3:"; std::string content = "1. 2- 3:";
REQUIRE(LexWordAroundPos(CharPos(content, '1'), content) == "1"); REQUIRE(LexIdentifierAroundPos(CharPos(content, '1'), content) == "1");
REQUIRE(LexWordAroundPos(CharPos(content, '2'), content) == "2"); REQUIRE(LexIdentifierAroundPos(CharPos(content, '2'), content) == "2");
REQUIRE(LexWordAroundPos(CharPos(content, '3'), content) == "3"); REQUIRE(LexIdentifierAroundPos(CharPos(content, '3'), content) == "3");
} }
} }

View File

@ -35,10 +35,11 @@ void LexFunctionDeclaration(const std::string& buffer_content,
std::string* insert_text, std::string* insert_text,
int* newlines_after_name); int* newlines_after_name);
std::string LexWordAroundPos(lsPosition position, const std::string& content); std::string_view LexIdentifierAroundPos(lsPosition position,
std::string_view content);
// Case-insensitive subsequence matching. // Case-insensitive subsequence matching.
bool SubsequenceMatch(std::string_view search, std::string_view content); bool SubsequenceMatchIgnoreCase(std::string_view search, std::string_view content);
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);

View File

@ -447,8 +447,8 @@ struct TextDocumentCodeActionHandler
// For error diagnostics, provide an action to resolve an include. // For error diagnostics, provide an action to resolve an include.
// TODO: find a way to index diagnostic contents so line numbers // TODO: find a way to index diagnostic contents so line numbers
// don't get mismatched when actively editing a file. // don't get mismatched when actively editing a file.
std::string include_query = std::string_view include_query =
LexWordAroundPos(diag.range.start, working_file->buffer_content); LexIdentifierAroundPos(diag.range.start, working_file->buffer_content);
if (diag.severity == lsDiagnosticSeverity::Error && if (diag.severity == lsDiagnosticSeverity::Error &&
!include_query.empty()) { !include_query.empty()) {
const size_t kMaxResults = 20; const size_t kMaxResults = 20;

View File

@ -126,7 +126,7 @@ struct TextDocumentDefinitionHandler
if (!has_symbol) { if (!has_symbol) {
lsPosition position = request->params.position; lsPosition position = request->params.position;
const std::string& buffer = working_file->buffer_content; const std::string& buffer = working_file->buffer_content;
std::string query = LexWordAroundPos(position, buffer); std::string_view query = LexIdentifierAroundPos(position, buffer);
bool has_scope = query.find(':') != std::string::npos; bool has_scope = query.find(':') != std::string::npos;
// For symbols whose short/detailed names contain |query| as a // For symbols whose short/detailed names contain |query| as a

View File

@ -107,7 +107,7 @@ struct WorkspaceSymbolHandler : BaseMessageHandler<Ipc_WorkspaceSymbol> {
for (int i = 0; i < (int)db->symbols.size(); ++i) { for (int i = 0; i < (int)db->symbols.size(); ++i) {
std::string_view detailed_name = db->GetSymbolDetailedName(i); std::string_view detailed_name = db->GetSymbolDetailedName(i);
if (SubsequenceMatch(query_without_space, detailed_name)) { if (SubsequenceMatchIgnoreCase(query_without_space, detailed_name)) {
// Do not show the same entry twice. // Do not show the same entry twice.
if (!inserted_results.insert(std::string(detailed_name)).second) if (!inserted_results.insert(std::string(detailed_name)).second)
continue; continue;