mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 17:11:59 +00:00
Better :: lexing and fix ci
This commit is contained in:
parent
61b5ef4fa0
commit
d4c49730fd
106
src/lex_utils.cc
106
src/lex_utils.cc
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user