From 73bd987b1af876c752fad4b9a53b3ee71097de51 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 14 Apr 2018 11:57:23 -0700 Subject: [PATCH] Add caseSensitivity to config->{completion,workspaceSymbol} --- CMakeLists.txt | 15 +++++----- src/clang_indexer.cc | 3 -- src/config.h | 9 +++++- src/file_consumer.cc | 15 ++++------ src/file_consumer.h | 2 -- src/fuzzy_match.cc | 22 ++++++++------ src/fuzzy_match.h | 3 +- src/indexer.h | 7 ----- src/lsp.cc | 2 +- src/messages/text_document_completion.cc | 2 +- src/messages/workspace_symbol.cc | 2 +- src/query.cc | 37 ++++++++++++------------ 12 files changed, 57 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0594e2ba..36085bd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(DefaultCMakeBuildType) # Required libclang version set(LIBCLANG_VERSION 6.0.0 CACHE STRING "libclang version") -set(LIBCLANG_DOWNLOAD_LOCATION ${CMAKE_BINARY_DIR} +set(LIBCLANG_DOWNLOAD_LOCATION ${CMAKE_BINARY_DIR} CACHE STRING "Downloaded libclang location") option(SYSTEM_LIBCLANG "Use system installation of libclang instead of \ downloading libclang" OFF) @@ -33,7 +33,8 @@ if(NOT CYGWIN) set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL Windows) +# CMake sets MSVC for both MSVC and Clang(Windows) +if(MSVC) # Common MSVC/Clang(Windows) options target_compile_options(ccls PRIVATE /nologo @@ -87,7 +88,7 @@ endif() ### Libraries # See cmake/FindClang.cmake -find_package(Clang ${CLANG_VERSION} REQUIRED) +find_package(Clang ${LIBCLANG_VERSION} REQUIRED) target_link_libraries(ccls PRIVATE Clang::Clang) # Enable threading support @@ -97,7 +98,7 @@ target_link_libraries(ccls PRIVATE Threads::Threads) if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) target_link_libraries(ccls PRIVATE -lc++experimental) -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) +elseif(MSVC) else() target_link_libraries(ccls PRIVATE -lstdc++fs) endif() @@ -161,8 +162,8 @@ endif() if(NOT SYSTEM_LIBCLANG AND ${CMAKE_SYSTEM_NAME} STREQUAL Windows) add_custom_command(TARGET ccls POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${DOWNLOADED_CLANG_DIR}/bin/libclang.dll + COMMAND ${CMAKE_COMMAND} -E copy + ${DOWNLOADED_CLANG_DIR}/bin/libclang.dll $ COMMENT "Copying libclang.dll to build directory ...") endif() @@ -190,7 +191,7 @@ else() support clang-format 6.0.0") endif() - add_custom_target(format + add_custom_target(format COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red --bold ${Clang_FORMAT_ERROR}) endif() diff --git a/src/clang_indexer.cc b/src/clang_indexer.cc index ee96345c..d0405321 100644 --- a/src/clang_indexer.cc +++ b/src/clang_indexer.cc @@ -15,9 +15,6 @@ #include #include -// TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on -// |Foobar| in DISALLOW_COPY(Foobar) - #if CINDEX_VERSION >= 47 #define CINDEX_HAVE_PRETTY 1 #endif diff --git a/src/config.h b/src/config.h index a0b7c55e..d4cfe163 100644 --- a/src/config.h +++ b/src/config.h @@ -89,6 +89,11 @@ struct Config { } codeLens; struct Completion { + // 0: case-insensitive + // 1: case-folded, i.e. insensitive if no input character is uppercase. + // 2: case-sensitive + int caseSensitivity = 2; + // Some completion UI, such as Emacs' completion-at-point and company-lsp, // display completion item label and detail side by side. // This does not look right, when you see things like: @@ -209,6 +214,7 @@ struct Config { } index; struct WorkspaceSymbol { + int caseSensitivity = 1; // Maximum workspace search results. int maxNum = 1000; // If true, workspace search results will be dynamically rescored/reordered @@ -227,6 +233,7 @@ struct Config { MAKE_REFLECT_STRUCT(Config::ClientCapability, snippetSupport); MAKE_REFLECT_STRUCT(Config::CodeLens, localVariables); MAKE_REFLECT_STRUCT(Config::Completion, + caseSensitivity, detailedLabel, filterAndSort, includeBlacklist, @@ -248,7 +255,7 @@ MAKE_REFLECT_STRUCT(Config::Index, onDidChange, threads, whitelist); -MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, maxNum, sort); +MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum); MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand, diff --git a/src/file_consumer.cc b/src/file_consumer.cc index 9d1d276a..a85c666f 100644 --- a/src/file_consumer.cc +++ b/src/file_consumer.cc @@ -81,7 +81,11 @@ IndexFile* FileConsumer::TryConsumeFile( CXFileUniqueID file_id; if (clang_getFileUniqueID(file, &file_id) != 0) { - EmitError(file); + std::string file_name = FileName(file); + if (!file_name.empty()) { + LOG_S(ERROR) << "Could not get unique file id for " << file_name + << " when parsing " << parse_file_; + } return nullptr; } @@ -126,12 +130,3 @@ std::vector> FileConsumer::TakeLocalState() { } return result; } - -void FileConsumer::EmitError(CXFile file) const { - std::string file_name = ToString(clang_getFileName(file)); - // TODO: Investigate this more, why can we get an empty file name? - if (!file_name.empty()) { - LOG_S(ERROR) << "Could not get unique file id for " << file_name - << " when parsing " << parse_file_; - } -} diff --git a/src/file_consumer.h b/src/file_consumer.h index de7162c3..520aa5ed 100644 --- a/src/file_consumer.h +++ b/src/file_consumer.h @@ -68,8 +68,6 @@ struct FileConsumer { std::vector> TakeLocalState(); private: - void EmitError(CXFile file) const; - std::unordered_map> local_; FileConsumerSharedState* shared_; std::string parse_file_; diff --git a/src/fuzzy_match.cc b/src/fuzzy_match.cc index 4989475b..a579a3fd 100644 --- a/src/fuzzy_match.cc +++ b/src/fuzzy_match.cc @@ -78,8 +78,11 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) { return s; } -FuzzyMatcher::FuzzyMatcher(std::string_view pattern) { +FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { CalculateRoles(pattern, pat_role, &pat_set); + if (sensitivity == 1) + sensitivity = pat_set & 1 << Upper ? 2 : 0; + case_sensitivity = sensitivity; size_t n = 0; for (size_t i = 0; i < pattern.size(); i++) if (pattern[i] != ' ') { @@ -112,12 +115,13 @@ int FuzzyMatcher::Match(std::string_view text) { cur[j][1] + MissScore(j, true)); // For the first char of pattern, apply extra restriction to filter bad // candidates (e.g. |int| in |PRINT|) - if (low_pat[i] == low_text[j] && - (i || text_role[j] != Tail || pat[i] == text[j])) { - cur[j + 1][1] = std::max(pre[j][0] + MatchScore(i, j, false), - pre[j][1] + MatchScore(i, j, true)); - } else - cur[j + 1][1] = kMinScore * 2; + cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j] + : low_pat[i] == low_text[j] && + (i || text_role[j] != Tail || + pat[i] == text[j])) + ? std::max(pre[j][0] + MatchScore(i, j, false), + pre[j][1] + MatchScore(i, j, true)) + : cur[j + 1][1] = kMinScore * 2; } } @@ -131,7 +135,7 @@ int FuzzyMatcher::Match(std::string_view text) { TEST_SUITE("fuzzy_match") { bool Ranks(std::string_view pat, std::vector texts) { - FuzzyMatcher fuzzy(pat); + FuzzyMatcher fuzzy(pat, 0); std::vector scores; for (auto text : texts) scores.push_back(fuzzy.Match(text)); @@ -150,7 +154,7 @@ TEST_SUITE("fuzzy_match") { } TEST_CASE("test") { - FuzzyMatcher fuzzy(""); + FuzzyMatcher fuzzy("", 0); CHECK(fuzzy.Match("") == 0); CHECK(fuzzy.Match("aaa") < 0); diff --git a/src/fuzzy_match.h b/src/fuzzy_match.h index 958f35cd..9dd132ef 100644 --- a/src/fuzzy_match.h +++ b/src/fuzzy_match.h @@ -12,10 +12,11 @@ class FuzzyMatcher { // overflow. constexpr static int kMinScore = INT_MIN / 4; - FuzzyMatcher(std::string_view pattern); + FuzzyMatcher(std::string_view pattern, int case_sensitivity); int Match(std::string_view text); private: + int case_sensitivity; std::string pat; std::string_view text; int pat_set, text_set; diff --git a/src/indexer.h b/src/indexer.h index 6af15b82..43035a6b 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -188,9 +188,6 @@ struct TypeDef : NameMixin> { types == o.types && funcs == o.funcs && vars == o.vars && kind == o.kind && hover == o.hover && comments == o.comments; } - bool operator!=(const TypeDef& o) const { - return !(*this == o); - } }; template void Reflect(TVisitor& visitor, TypeDef& value) { @@ -269,9 +266,6 @@ struct FuncDef : NameMixin> { kind == o.kind && storage == o.storage && hover == o.hover && comments == o.comments; } - bool operator!=(const FuncDef& o) const { - return !(*this == o); - } }; template @@ -359,7 +353,6 @@ struct VarDef : NameMixin> { extent == o.extent && type == o.type && kind == o.kind && storage == o.storage && hover == o.hover && comments == o.comments; } - bool operator!=(const VarDef& o) const { return !(*this == o); } }; template diff --git a/src/lsp.cc b/src/lsp.cc index e40f2d71..717cbb53 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -49,7 +49,7 @@ std::optional ReadJsonRpcContentFrom( return opt_c && *opt_c == expected; }; if (!expect_char('\r') || !expect_char('\n')) { - LOG_S(INFO) << "Unexpected token (expected \r\n sequence)"; + LOG_S(INFO) << "Unexpected token (expected \\r\\n sequence)"; return std::nullopt; } diff --git a/src/messages/text_document_completion.cc b/src/messages/text_document_completion.cc index 3f70405b..e67d6083 100644 --- a/src/messages/text_document_completion.cc +++ b/src/messages/text_document_completion.cc @@ -214,7 +214,7 @@ void FilterAndSortCompletionResponse( } // Fuzzy match and remove awful candidates. - FuzzyMatcher fuzzy(complete_text); + FuzzyMatcher fuzzy(complete_text, g_config->completion.caseSensitivity); for (auto& item : items) { item.score_ = CaseFoldingSubsequenceMatch(complete_text, *item.filterText).first diff --git a/src/messages/workspace_symbol.cc b/src/messages/workspace_symbol.cc index 173a53d9..e138d4a7 100644 --- a/src/messages/workspace_symbol.cc +++ b/src/messages/workspace_symbol.cc @@ -129,7 +129,7 @@ struct Handler_WorkspaceSymbol : BaseMessageHandler { int longest = 0; for (int i : result_indices) longest = std::max(longest, int(db->GetSymbolName(i, true).size())); - FuzzyMatcher fuzzy(query); + FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity); std::vector> permutation(result_indices.size()); for (int i = 0; i < int(result_indices.size()); i++) { permutation[i] = { diff --git a/src/query.cc b/src/query.cc index e6bd14e9..093d2b40 100644 --- a/src/query.cc +++ b/src/query.cc @@ -625,15 +625,15 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, /*onFound:*/ [this, &previous_id_map, ¤t_id_map](IndexType* previous, IndexType* current) { - std::optional previous_remapped_def = + std::optional prev_remapped = ToQuery(previous_id_map, previous->def); - std::optional current_remapped_def = + std::optional current_remapped = ToQuery(current_id_map, current->def); - if (current_remapped_def && - previous_remapped_def != current_remapped_def && - !current_remapped_def->detailed_name.empty()) { + if (current_remapped && + !(prev_remapped == current_remapped) && + !current_remapped->detailed_name.empty()) { types_def_update.push_back(QueryType::DefUpdate( - current->usr, std::move(*current_remapped_def))); + current->usr, std::move(*current_remapped))); } PROCESS_UPDATE_DIFF(QueryTypeId, types_declarations, declarations, Use); @@ -686,15 +686,15 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, /*onFound:*/ [this, &previous_id_map, ¤t_id_map](IndexFunc* previous, IndexFunc* current) { - std::optional previous_remapped_def = + std::optional prev_remapped = ToQuery(previous_id_map, previous->def); - std::optional current_remapped_def = + std::optional current_remapped = ToQuery(current_id_map, current->def); - if (current_remapped_def && - previous_remapped_def != current_remapped_def && - !current_remapped_def->detailed_name.empty()) { + if (current_remapped && + !(prev_remapped == current_remapped) && + !current_remapped->detailed_name.empty()) { funcs_def_update.push_back(QueryFunc::DefUpdate( - current->usr, std::move(*current_remapped_def))); + current->usr, std::move(*current_remapped))); } PROCESS_UPDATE_DIFF(QueryFuncId, funcs_declarations, declarations, Use); @@ -736,15 +736,14 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, /*onFound:*/ [this, &previous_id_map, ¤t_id_map](IndexVar* previous, IndexVar* current) { - std::optional previous_remapped_def = + std::optional prev_remapped = ToQuery(previous_id_map, previous->def); - std::optional current_remapped_def = + std::optional current_remapped = ToQuery(current_id_map, current->def); - if (current_remapped_def && - previous_remapped_def != current_remapped_def && - !current_remapped_def->detailed_name.empty()) - vars_def_update.push_back(QueryVar::DefUpdate( - current->usr, std::move(*current_remapped_def))); + if (current_remapped && !(prev_remapped == current_remapped) && + !current_remapped->detailed_name.empty()) + vars_def_update.push_back( + QueryVar::DefUpdate(current->usr, std::move(*current_remapped))); PROCESS_UPDATE_DIFF(QueryVarId, vars_declarations, declarations, Use); PROCESS_UPDATE_DIFF(QueryVarId, vars_uses, uses, Use);