From 67eb1e6b23af2f4b11f88a862400734f3a53e2ed Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 28 Oct 2018 10:49:31 -0700 Subject: [PATCH] Refactor message handler and namespace ccls --- CMakeLists.txt | 9 +- src/clang_complete.cc | 3 +- src/clang_complete.hh | 7 +- src/clang_tu.cc | 2 + src/clang_tu.hh | 2 + src/config.cc | 2 +- src/config.h | 4 +- src/filesystem.cc | 2 +- src/fuzzy_match.cc | 4 +- src/fuzzy_match.h | 2 + src/hierarchy.hh | 4 +- src/include_complete.cc | 7 +- src/include_complete.h | 11 +- src/indexer.cc | 9 +- src/indexer.h | 39 +- src/lsp.cc | 139 +------ src/{lsp.h => lsp.hh} | 100 +---- src/lsp_completion.h | 122 ------ src/main.cc | 6 +- src/match.cc | 7 +- src/match.h | 4 +- src/maybe.h | 2 + src/message_handler.cc | 202 ++++++++-- src/message_handler.h | 71 ---- src/message_handler.hh | 265 +++++++++++++ src/messages/ccls_call.cc | 170 ++++----- src/messages/ccls_info.cc | 90 ++--- src/messages/ccls_inheritance.cc | 174 ++++----- src/messages/ccls_member.cc | 219 +++++------ src/messages/ccls_navigate.cc | 161 ++++---- src/messages/ccls_reload.cc | 90 ++--- src/messages/ccls_vars.cc | 95 ++--- src/messages/exit.cc | 19 - src/messages/initialize.cc | 249 ++++++------ src/messages/shutdown.cc | 25 -- src/messages/textDocument_code.cc | 234 ++++++++++++ src/messages/textDocument_codeAction.cc | 71 ---- src/messages/textDocument_codeLens.cc | 219 ----------- src/messages/textDocument_completion.cc | 285 ++++++-------- src/messages/textDocument_definition.cc | 295 +++++++------- src/messages/textDocument_did.cc | 180 +++------ src/messages/textDocument_document.cc | 403 +++++++++----------- src/messages/textDocument_foldingRange.cc | 74 ++-- src/messages/textDocument_formatting.cc | 165 +++----- src/messages/textDocument_hover.cc | 105 ++--- src/messages/textDocument_references.cc | 206 +++++----- src/messages/textDocument_rename.cc | 69 +--- src/messages/textDocument_signatureHelp.cc | 144 +++---- src/messages/textDocument_typeDefinition.cc | 72 ---- src/messages/workspace.cc | 189 +++++++++ src/messages/workspace_did.cc | 163 -------- src/messages/workspace_symbol.cc | 127 ------ src/pipeline.cc | 128 ++++--- src/pipeline.hh | 5 +- src/position.cc | 4 +- src/position.h | 33 +- src/project.cc | 5 +- src/{project.h => project.hh} | 4 +- src/query.cc | 5 +- src/query.h | 20 +- src/query_utils.cc | 2 + src/query_utils.h | 2 + src/serializer.cc | 5 +- src/{serializer.h => serializer.hh} | 6 +- src/serializers/binary.h | 4 +- src/serializers/json.h | 4 +- src/test.cc | 4 +- src/test.h | 2 + src/threaded_queue.h | 12 +- src/utils.cc | 2 + src/utils.h | 4 +- src/working_files.cc | 6 +- src/working_files.h | 6 +- 73 files changed, 2455 insertions(+), 3126 deletions(-) rename src/{lsp.h => lsp.hh} (74%) delete mode 100644 src/lsp_completion.h delete mode 100644 src/message_handler.h create mode 100644 src/message_handler.hh delete mode 100644 src/messages/exit.cc delete mode 100644 src/messages/shutdown.cc create mode 100644 src/messages/textDocument_code.cc delete mode 100644 src/messages/textDocument_codeAction.cc delete mode 100644 src/messages/textDocument_codeLens.cc delete mode 100644 src/messages/textDocument_typeDefinition.cc create mode 100644 src/messages/workspace.cc delete mode 100644 src/messages/workspace_did.cc delete mode 100644 src/messages/workspace_symbol.cc rename src/{project.h => project.hh} (97%) rename src/{serializer.h => serializer.hh} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c6bdbcd..abe0e04b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,11 +212,8 @@ target_sources(ccls PRIVATE src/messages/ccls_navigate.cc src/messages/ccls_reload.cc src/messages/ccls_vars.cc - src/messages/exit.cc src/messages/initialize.cc - src/messages/shutdown.cc - src/messages/textDocument_codeAction.cc - src/messages/textDocument_codeLens.cc + src/messages/textDocument_code.cc src/messages/textDocument_completion.cc src/messages/textDocument_definition.cc src/messages/textDocument_did.cc @@ -227,7 +224,5 @@ target_sources(ccls PRIVATE src/messages/textDocument_references.cc src/messages/textDocument_rename.cc src/messages/textDocument_signatureHelp.cc - src/messages/textDocument_typeDefinition.cc - src/messages/workspace_did.cc - src/messages/workspace_symbol.cc + src/messages/workspace.cc ) diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 499877ef..c8419b5f 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -563,8 +563,6 @@ std::shared_ptr CompletionSession::GetPreamble() { return preamble; } -} // namespace ccls - CompletionManager::CompletionManager(Project *project, WorkingFiles *working_files, OnDiagnostic on_diagnostic, @@ -674,3 +672,4 @@ void CompletionManager::FlushAllSessions() { preloads.Clear(); sessions.Clear(); } +} // namespace ccls diff --git a/src/clang_complete.hh b/src/clang_complete.hh index 0d50a7f8..21ce4876 100644 --- a/src/clang_complete.hh +++ b/src/clang_complete.hh @@ -5,9 +5,8 @@ #include "clang_tu.hh" #include "lru_cache.h" -#include "lsp.h" -#include "lsp_completion.h" -#include "project.h" +#include "lsp.hh" +#include "project.hh" #include "threaded_queue.h" #include "working_files.h" @@ -61,7 +60,6 @@ struct CompletionSession std::shared_ptr GetPreamble(); }; -} struct CompletionManager { using OnDiagnostic = std::functionpath == path && this->position == position; } }; +} // namespace ccls diff --git a/src/clang_tu.cc b/src/clang_tu.cc index e53d1ea4..2a85e694 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -12,6 +12,7 @@ using namespace clang; +namespace ccls { std::string PathFromFileEntry(const FileEntry &file) { StringRef Name = file.tryGetRealPathName(); if (Name.empty()) @@ -241,3 +242,4 @@ const char *ClangBuiltinTypeName(int kind) { return ""; } } +} // namespace ccls diff --git a/src/clang_tu.hh b/src/clang_tu.hh index 3b405a09..29cda7f9 100644 --- a/src/clang_tu.hh +++ b/src/clang_tu.hh @@ -17,6 +17,7 @@ namespace vfs = clang::vfs; } #endif +namespace ccls { std::string PathFromFileEntry(const clang::FileEntry &file); Range FromCharSourceRange(const clang::SourceManager &SM, @@ -42,3 +43,4 @@ BuildCompilerInvocation(std::vector args, llvm::IntrusiveRefCntPtr VFS); const char *ClangBuiltinTypeName(int); +} // namespace ccls diff --git a/src/config.cc b/src/config.cc index 9ea383f3..5ba8f5c4 100644 --- a/src/config.cc +++ b/src/config.cc @@ -3,9 +3,9 @@ #include "config.h" +namespace ccls { Config *g_config; -namespace ccls { void DoPathMapping(std::string &arg) { for (const std::string &mapping : g_config->clang.pathMappings) { auto sep = mapping.find('>'); diff --git a/src/config.h b/src/config.h index 10391fe1..57a06b6c 100644 --- a/src/config.h +++ b/src/config.h @@ -3,10 +3,11 @@ #pragma once -#include "serializer.h" +#include "serializer.hh" #include +namespace ccls { /* The language client plugin needs to send initialization options in the `initialize` request to the ccls language server. @@ -269,6 +270,5 @@ MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand, extern Config *g_config; -namespace ccls { void DoPathMapping(std::string &arg); } diff --git a/src/filesystem.cc b/src/filesystem.cc index 2507db0c..ff797b0b 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -11,7 +11,7 @@ using namespace llvm; void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, const std::function &handler) { - EnsureEndsInSlash(folder); + ccls::EnsureEndsInSlash(folder); sys::fs::file_status Status; if (sys::fs::status(folder, Status, true)) return; diff --git a/src/fuzzy_match.cc b/src/fuzzy_match.cc index 08f446b4..7c44c452 100644 --- a/src/fuzzy_match.cc +++ b/src/fuzzy_match.cc @@ -8,10 +8,11 @@ #include #include +namespace ccls { +namespace { enum CharClass { Other, Lower, Upper }; enum CharRole { None, Tail, Head }; -namespace { CharClass GetCharClass(int c) { if (islower(c)) return Lower; @@ -133,6 +134,7 @@ int FuzzyMatcher::Match(std::string_view text) { ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j)); return ret; } +} // namespace ccls #if 0 TEST_SUITE("fuzzy_match") { diff --git a/src/fuzzy_match.h b/src/fuzzy_match.h index c8de1d69..e4d6ead1 100644 --- a/src/fuzzy_match.h +++ b/src/fuzzy_match.h @@ -7,6 +7,7 @@ #include #include +namespace ccls { class FuzzyMatcher { public: constexpr static int kMaxPat = 100; @@ -30,3 +31,4 @@ private: int MatchScore(int i, int j, bool last); int MissScore(int j, bool last); }; +} // namespace ccls diff --git a/src/hierarchy.hh b/src/hierarchy.hh index 3fd7d341..477d59a3 100644 --- a/src/hierarchy.hh +++ b/src/hierarchy.hh @@ -3,11 +3,12 @@ #pragma once -#include "lsp.h" +#include "lsp.hh" #include #include +namespace ccls { template std::vector FlattenHierarchy(const std::optional &root) { if (!root) @@ -28,3 +29,4 @@ std::vector FlattenHierarchy(const std::optional &root) { ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); return ret; } +} // namespace ccls diff --git a/src/include_complete.cc b/src/include_complete.cc index b88d9ce1..71749012 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -6,15 +6,18 @@ #include "filesystem.hh" #include "match.h" #include "platform.h" -#include "project.h" +#include "project.hh" #include #include #include + +#include using namespace llvm; #include +namespace ccls { namespace { struct CompletionCandidate { @@ -83,7 +86,6 @@ lsCompletionItem BuildCompletionItem(const std::string &path, item.priority_ = 0; return item; } - } // namespace IncludeComplete::IncludeComplete(Project *project) @@ -198,3 +200,4 @@ IncludeComplete::FindCompletionItemForAbsolutePath( return std::nullopt; return completion_items[it->second]; } +} // namespace ccls diff --git a/src/include_complete.h b/src/include_complete.h index 4681d497..d82fc326 100644 --- a/src/include_complete.h +++ b/src/include_complete.h @@ -3,12 +3,12 @@ #pragma once -#include "lsp_completion.h" +#include "message_handler.hh" #include #include -#include +namespace ccls { struct GroupMatch; struct Project; @@ -26,18 +26,18 @@ struct IncludeComplete { void InsertIncludesFromDirectory(std::string directory, bool use_angle_brackets); - std::optional + std::optional FindCompletionItemForAbsolutePath(const std::string &absolute_path); // Insert item to |completion_items|. // Update |absolute_path_to_completion_item| and |inserted_paths|. void InsertCompletionItem(const std::string &absolute_path, - lsCompletionItem &&item); + ccls::lsCompletionItem &&item); // Guards |completion_items| when |is_scanning| is true. std::mutex completion_items_mutex; std::atomic is_scanning; - std::vector completion_items; + std::vector completion_items; // Absolute file path to the completion item in |completion_items|. // Keep the one with shortest include path. @@ -50,3 +50,4 @@ struct IncludeComplete { Project *project_; std::unique_ptr match_; }; +} // namespace ccls diff --git a/src/indexer.cc b/src/indexer.cc index de83bdc2..7bca21d6 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -9,7 +9,7 @@ #include "match.h" #include "pipeline.hh" #include "platform.h" -#include "serializer.h" +#include "serializer.hh" #include #include @@ -26,9 +26,9 @@ #include #include -using namespace ccls; using namespace clang; +namespace ccls { namespace { constexpr int kInitializerMaxLines = 3; @@ -1195,7 +1195,7 @@ template void Uniquify(std::vector &a) { a.resize(n); } -namespace ccls::idx { +namespace idx { void Init() { multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, g_config->index.multiVersionBlacklist); @@ -1334,7 +1334,7 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs, return result; } -} // namespace ccls::idx +} // namespace idx void Reflect(Reader &vis, SymbolRef &v) { if (vis.Format() == SerializeFormat::Json) { @@ -1422,3 +1422,4 @@ void Reflect(Writer &vis, DeclRef &v) { Reflect(vis, v.extent); } } +} // namespace ccls diff --git a/src/indexer.h b/src/indexer.h index ae96fe28..7f353563 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -3,10 +3,10 @@ #pragma once -#include "lsp.h" +#include "lsp.hh" #include "maybe.h" #include "position.h" -#include "serializer.h" +#include "serializer.hh" #include "utils.h" #include @@ -19,6 +19,17 @@ #include #include +namespace std { +template <> struct hash { + std::size_t operator()(llvm::sys::fs::UniqueID ID) const { + size_t ret = ID.getDevice(); + ccls::hash_combine(ret, ID.getFile()); + return ret; + } +}; +} // namespace std + +namespace ccls { using Usr = uint64_t; enum class LanguageId; @@ -48,7 +59,6 @@ struct SymbolRef { bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); } bool Valid() const { return range.Valid(); } }; -MAKE_HASHABLE(SymbolRef, t.range, t.usr, t.kind, t.role); struct ExtentRef : SymbolRef { Range extent; @@ -57,7 +67,6 @@ struct ExtentRef : SymbolRef { } bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); } }; -MAKE_HASHABLE(ExtentRef, t.range, t.usr, t.kind, t.role, t.extent); struct Ref { Range range; @@ -81,12 +90,10 @@ struct Use : Ref { return range == o.range && file_id == o.file_id; } }; -MAKE_HASHABLE(Use, t.range, t.file_id) struct DeclRef : Use { Range extent; }; -MAKE_HASHABLE(DeclRef, t.range, t.file_id) void Reflect(Reader &visitor, SymbolRef &value); void Reflect(Writer &visitor, SymbolRef &value); @@ -233,16 +240,6 @@ struct IndexInclude { const char *resolved_path; }; -namespace std { -template <> struct hash { - std::size_t operator()(llvm::sys::fs::UniqueID ID) const { - size_t ret = ID.getDevice(); - hash_combine(ret, ID.getFile()); - return ret; - } -}; -} // namespace std - struct IndexFile { // For both JSON and MessagePack cache files. static const int kMajorVersion; @@ -296,7 +293,7 @@ struct CompletionManager; struct WorkingFiles; struct VFS; -namespace ccls::idx { +namespace idx { void Init(); std::vector> Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, @@ -304,4 +301,10 @@ Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, const std::vector &args, const std::vector> &remapped, bool &ok); -} // namespace ccls::idx +} // namespace idx +} // namespace ccls + +MAKE_HASHABLE(ccls::SymbolRef, t.range, t.usr, t.kind, t.role); +MAKE_HASHABLE(ccls::ExtentRef, t.range, t.usr, t.kind, t.role, t.extent); +MAKE_HASHABLE(ccls::Use, t.range, t.file_id) +MAKE_HASHABLE(ccls::DeclRef, t.range, t.file_id) diff --git a/src/lsp.cc b/src/lsp.cc index 6a6e70f0..50e9d5de 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -1,18 +1,15 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "lsp.h" +#include "lsp.hh" #include "log.hh" #include "serializers/json.h" -#include - #include #include -MethodType kMethodType_Exit = "exit"; - +namespace ccls { void Reflect(Reader &visitor, lsRequestId &value) { if (visitor.IsInt64()) { value.type = lsRequestId::kInt; @@ -44,10 +41,6 @@ void Reflect(Writer &visitor, lsRequestId &value) { } } -InMessage::~InMessage() {} - -MessageRegistry *MessageRegistry::instance_ = nullptr; - lsTextDocumentIdentifier lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const { lsTextDocumentIdentifier result; @@ -55,120 +48,6 @@ lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const { return result; } -// Reads a JsonRpc message. |read| returns the next input character. -std::optional -ReadJsonRpcContentFrom(std::function()> read) { - // Read the content length. It is terminated by the "\r\n" sequence. - int exit_seq = 0; - std::string stringified_content_length; - while (true) { - std::optional opt_c = read(); - if (!opt_c) { - LOG_S(INFO) << "No more input when reading content length header"; - return std::nullopt; - } - char c = *opt_c; - - if (exit_seq == 0 && c == '\r') - ++exit_seq; - if (exit_seq == 1 && c == '\n') - break; - - stringified_content_length += c; - } - const char *kContentLengthStart = "Content-Length: "; - assert(StartsWith(stringified_content_length, kContentLengthStart)); - int content_length = - atoi(stringified_content_length.c_str() + strlen(kContentLengthStart)); - - // There is always a "\r\n" sequence before the actual content. - auto expect_char = [&](char expected) { - std::optional opt_c = read(); - return opt_c && *opt_c == expected; - }; - if (!expect_char('\r') || !expect_char('\n')) { - LOG_S(INFO) << "Unexpected token (expected \\r\\n sequence)"; - return std::nullopt; - } - - // Read content. - std::string content; - content.reserve(content_length); - for (int i = 0; i < content_length; ++i) { - std::optional c = read(); - if (!c) { - LOG_S(INFO) << "No more input when reading content body"; - return std::nullopt; - } - content += *c; - } - - return content; -} - -std::optional ReadCharFromStdinBlocking() { - // We do not use std::cin because it does not read bytes once stuck in - // cin.bad(). We can call cin.clear() but C++ iostream has other annoyance - // like std::{cin,cout} is tied by default, which causes undesired cout flush - // for cin operations. - int c = getchar(); - if (c >= 0) - return c; - return std::nullopt; -} - -std::optional -MessageRegistry::ReadMessageFromStdin(std::unique_ptr *message) { - std::optional content = - ReadJsonRpcContentFrom(&ReadCharFromStdinBlocking); - if (!content) { - LOG_S(ERROR) << "Failed to read JsonRpc input; exiting"; - exit(1); - } - - rapidjson::Document document; - document.Parse(content->c_str(), content->length()); - assert(!document.HasParseError()); - - JsonReader json_reader{&document}; - return Parse(json_reader, message); -} - -std::optional -MessageRegistry::Parse(Reader &visitor, std::unique_ptr *message) { - if (!visitor.HasMember("jsonrpc") || - std::string(visitor["jsonrpc"]->GetString()) != "2.0") { - LOG_S(FATAL) << "Bad or missing jsonrpc version"; - exit(1); - } - - std::string method; - ReflectMember(visitor, "method", method); - - if (allocators.find(method) == allocators.end()) - return std::string("Unable to find registered handler for method '") + - method + "'"; - - Allocator &allocator = allocators[method]; - try { - allocator(visitor, message); - return std::nullopt; - } catch (std::invalid_argument &e) { - // *message is partially deserialized but some field (e.g. |id|) are likely - // available. - return std::string("Fail to parse '") + method + "' " + - static_cast(visitor).GetPath() + ", expected " + - e.what(); - } -} - -MessageRegistry *MessageRegistry::instance() { - if (!instance_) - instance_ = new MessageRegistry(); - - return instance_; -} - lsDocumentUri lsDocumentUri::FromPath(const std::string &path) { lsDocumentUri result; result.SetPath(path); @@ -260,16 +139,4 @@ std::string lsPosition::ToString() const { bool lsTextEdit::operator==(const lsTextEdit &that) { return range == that.range && newText == that.newText; } - -void Reflect(Writer &visitor, lsMarkedString &value) { - // If there is a language, emit a `{language:string, value:string}` object. If - // not, emit a string. - if (value.language) { - REFLECT_MEMBER_START(); - REFLECT_MEMBER(language); - REFLECT_MEMBER(value); - REFLECT_MEMBER_END(); - } else { - Reflect(visitor, value.value); - } -} +} // namespace ccls diff --git a/src/lsp.h b/src/lsp.hh similarity index 74% rename from src/lsp.h rename to src/lsp.hh index 9013c89b..9b8731ee 100644 --- a/src/lsp.h +++ b/src/lsp.hh @@ -4,15 +4,15 @@ #pragma once #include "config.h" -#include "serializer.h" +#include "serializer.hh" #include "utils.h" +#include + #include #include -using MethodType = const char *; -extern MethodType kMethodType_Exit; - +namespace ccls { struct lsRequestId { // The client can send the request id as an int or a string. We should output // the same format we received. @@ -27,47 +27,10 @@ void Reflect(Reader &visitor, lsRequestId &value); void Reflect(Writer &visitor, lsRequestId &value); struct InMessage { - virtual ~InMessage(); - - virtual MethodType GetMethodType() const = 0; - virtual lsRequestId GetRequestId() const { return {}; } -}; - -struct NotificationMessage : InMessage {}; - -struct RequestMessage : public InMessage { lsRequestId id; - lsRequestId GetRequestId() const override { return id; } -}; - -#define REGISTER_IN_MESSAGE(type) \ - static MessageRegistryRegister type##message_handler_instance_; - -struct MessageRegistry { - static MessageRegistry *instance_; - static MessageRegistry *instance(); - - using Allocator = - std::function *)>; - std::unordered_map allocators; - - std::optional - ReadMessageFromStdin(std::unique_ptr *message); - std::optional Parse(Reader &visitor, - std::unique_ptr *message); -}; - -template struct MessageRegistryRegister { - MessageRegistryRegister() { - T dummy; - std::string method_name = dummy.GetMethodType(); - MessageRegistry::instance()->allocators[method_name] = - [](Reader &visitor, std::unique_ptr *message) { - *message = std::make_unique(); - // Reflect may throw and *message will be partially deserialized. - Reflect(visitor, static_cast(**message)); - }; - } + std::string method; + std::unique_ptr message; + std::unique_ptr document; }; enum class lsErrorCodes { @@ -214,7 +177,6 @@ struct lsSymbolInformation { lsLocation location; std::optional containerName; }; -MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName); struct lsTextDocumentIdentifier { lsDocumentUri uri; @@ -230,15 +192,6 @@ struct lsVersionedTextDocumentIdentifier { }; MAKE_REFLECT_STRUCT(lsVersionedTextDocumentIdentifier, uri, version); -struct lsTextDocumentPositionParams { - // The text document. - lsTextDocumentIdentifier textDocument; - - // The position inside the text document. - lsPosition position; -}; -MAKE_REFLECT_STRUCT(lsTextDocumentPositionParams, textDocument, position); - struct lsTextEdit { // The range of the text document to be manipulated. To insert // text into a document create a range where start === end. @@ -289,26 +242,7 @@ struct lsWorkspaceEdit { }; MAKE_REFLECT_STRUCT(lsWorkspaceEdit, documentChanges); -// MarkedString can be used to render human readable text. It is either a -// markdown string or a code-block that provides a language and a code snippet. -// The language identifier is sematically equal to the std::optional language -// identifier in fenced code blocks in GitHub issues. See -// https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting -// -// The pair of a language and a value is an equivalent to markdown: -// ```${language} -// ${value} -// ``` -// -// Note that markdown strings will be sanitized - that means html will be -// escaped. -struct lsMarkedString { - std::optional language; - std::string value; -}; -void Reflect(Writer &visitor, lsMarkedString &value); - -struct lsTextDocumentContentChangeEvent { +struct TextDocumentContentChangeEvent { // The range of the document that changed. std::optional range; // The length of the range that got replaced. @@ -316,24 +250,21 @@ struct lsTextDocumentContentChangeEvent { // The new text of the range/document. std::string text; }; -MAKE_REFLECT_STRUCT(lsTextDocumentContentChangeEvent, range, rangeLength, text); -struct lsTextDocumentDidChangeParams { +struct TextDocumentDidChangeParam { lsVersionedTextDocumentIdentifier textDocument; - std::vector contentChanges; + std::vector contentChanges; }; -MAKE_REFLECT_STRUCT(lsTextDocumentDidChangeParams, textDocument, - contentChanges); -struct lsWorkspaceFolder { +struct WorkspaceFolder { lsDocumentUri uri; std::string name; }; -MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name); +MAKE_REFLECT_STRUCT(WorkspaceFolder, uri, name); // Show a message to the user. -enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 }; -MAKE_REFLECT_TYPE_PROXY(lsMessageType) +enum class MessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 }; +MAKE_REFLECT_TYPE_PROXY(MessageType) enum class lsDiagnosticSeverity { // Reports an error. @@ -380,7 +311,7 @@ struct lsPublishDiagnosticsParams { MAKE_REFLECT_STRUCT(lsPublishDiagnosticsParams, uri, diagnostics); struct lsShowMessageParams { - lsMessageType type = lsMessageType::Error; + MessageType type = MessageType::Error; std::string message; }; MAKE_REFLECT_STRUCT(lsShowMessageParams, type, message); @@ -418,3 +349,4 @@ inline uint16_t operator&(Role lhs, Role rhs) { inline Role operator|(Role lhs, Role rhs) { return Role(uint16_t(lhs) | uint16_t(rhs)); } +} // namespace ccls diff --git a/src/lsp_completion.h b/src/lsp_completion.h deleted file mode 100644 index d0a4f4da..00000000 --- a/src/lsp_completion.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once -#include "lsp.h" - -// The kind of a completion entry. -enum class lsCompletionItemKind { - Text = 1, - Method = 2, - Function = 3, - Constructor = 4, - Field = 5, - Variable = 6, - Class = 7, - Interface = 8, - Module = 9, - Property = 10, - Unit = 11, - Value = 12, - Enum = 13, - Keyword = 14, - Snippet = 15, - Color = 16, - File = 17, - Reference = 18, - Folder = 19, - EnumMember = 20, - Constant = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25, -}; -MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind); - -// Defines whether the insert text in a completion item should be interpreted as -// plain text or a snippet. -enum class lsInsertTextFormat { - // The primary text to be inserted is treated as a plain string. - PlainText = 1, - - // The primary text to be inserted is treated as a snippet. - // - // A snippet can define tab stops and placeholders with `$1`, `$2` - // and `${3:foo}`. `$0` defines the final tab stop, it defaults to - // the end of the snippet. Placeholders with equal identifiers are linked, - // that is typing in one will update others too. - // - // See also: - // https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md - Snippet = 2 -}; -MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat); - -struct lsCompletionItem { - // A set of function parameters. Used internally for signature help. Not sent - // to vscode. - std::vector parameters_; - - // The label of this completion item. By default - // also the text that is inserted when selecting - // this completion. - std::string label; - - // The kind of this completion item. Based of the kind - // an icon is chosen by the editor. - lsCompletionItemKind kind = lsCompletionItemKind::Text; - - // A human-readable string with additional information - // about this item, like type or symbol information. - std::string detail; - - // A human-readable string that represents a doc-comment. - std::optional documentation; - - // Internal information to order candidates. - int score_; - unsigned priority_; - - // Use <> or "" by default as include path. - bool use_angle_brackets_ = false; - - // A string that shoud be used when comparing this item - // with other items. When `falsy` the label is used. - std::string sortText; - - // A string that should be used when filtering a set of - // completion items. When `falsy` the label is used. - std::optional filterText; - - // A string that should be inserted a document when selecting - // this completion. When `falsy` the label is used. - std::string insertText; - - // The format of the insert text. The format applies to both the `insertText` - // property and the `newText` property of a provided `textEdit`. - lsInsertTextFormat insertTextFormat = lsInsertTextFormat::PlainText; - - // An edit which is applied to a document when selecting this completion. When - // an edit is provided the value of `insertText` is ignored. - // - // *Note:* The range of the edit must be a single line range and it must - // contain the position at which completion has been requested. - lsTextEdit textEdit; - - // An std::optional array of additional text edits that are applied when - // selecting this completion. Edits must not overlap with the main edit - // nor with themselves. - std::vector additionalTextEdits; - - // An std::optional command that is executed *after* inserting this - // completion. *Note* that additional modifications to the current document - // should be described with the additionalTextEdits-property. Command command; - - // An data entry field that is preserved on a completion item between - // a completion and a completion resolve request. - // data ? : any -}; -MAKE_REFLECT_STRUCT(lsCompletionItem, label, kind, detail, documentation, - sortText, filterText, insertText, insertTextFormat, - textEdit, additionalTextEdits); diff --git a/src/main.cc b/src/main.cc index 4af74f40..e6521c05 100644 --- a/src/main.cc +++ b/src/main.cc @@ -4,7 +4,7 @@ #include "log.hh" #include "pipeline.hh" #include "platform.h" -#include "serializer.h" +#include "serializer.hh" #include "serializers/json.h" #include "test.h" #include "working_files.h" @@ -28,7 +28,9 @@ using namespace ccls; using namespace llvm; using namespace llvm::cl; +namespace ccls { std::string g_init_options; +} namespace { OptionCategory C("ccls options"); @@ -92,7 +94,7 @@ int main(int argc, char **argv) { if (opt_test_index != "!") { language_server = false; - if (!RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput())) + if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput())) return 1; } diff --git a/src/match.cc b/src/match.cc index ce614b35..808bc180 100644 --- a/src/match.cc +++ b/src/match.cc @@ -3,10 +3,10 @@ #include "match.h" -#include "lsp.h" +#include "lsp.hh" #include "pipeline.hh" -using namespace ccls; +namespace ccls { std::optional Matcher::Create(const std::string &search) { try { Matcher m; @@ -19,7 +19,7 @@ std::optional Matcher::Create(const std::string &search) { return m; } catch (const std::exception &e) { lsShowMessageParams params; - params.type = lsMessageType::Error; + params.type = MessageType::Error; params.message = "failed to parse EMCAScript regex " + search + " : " + e.what(); pipeline::Notify(window_showMessage, params); @@ -62,3 +62,4 @@ bool GroupMatch::IsMatch(const std::string &value, return true; } +} // namespace ccls diff --git a/src/match.h b/src/match.h index 9f31b319..e2d205c1 100644 --- a/src/match.h +++ b/src/match.h @@ -9,6 +9,7 @@ #include #include +namespace ccls { struct Matcher { static std::optional Create(const std::string &search); @@ -28,4 +29,5 @@ struct GroupMatch { std::vector whitelist; std::vector blacklist; -}; \ No newline at end of file +}; +} // namespace ccls diff --git a/src/maybe.h b/src/maybe.h index 8e897d42..f0adf8a7 100644 --- a/src/maybe.h +++ b/src/maybe.h @@ -7,6 +7,7 @@ #include +namespace ccls { // Like std::optional, but the stored data is responsible for containing the // empty state. T should define a function `bool T::Valid()`. template class Maybe { @@ -44,3 +45,4 @@ public: bool operator==(const Maybe &o) const { return storage == o.storage; } bool operator!=(const Maybe &o) const { return !(*this == o); } }; +} // namespace ccls diff --git a/src/message_handler.cc b/src/message_handler.cc index 7540c990..b860d074 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -1,23 +1,55 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" +#include "message_handler.hh" #include "log.hh" #include "match.h" #include "pipeline.hh" -#include "project.h" +#include "project.hh" #include "query_utils.h" -using namespace ccls; +#include "serializers/json.h" using namespace clang; #include -MAKE_HASHABLE(SymbolIdx, t.usr, t.kind); +MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind); + +namespace ccls { +MAKE_REFLECT_STRUCT(EmptyParam, placeholder); +MAKE_REFLECT_STRUCT(TextDocumentParam, textDocument); +MAKE_REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument); +MAKE_REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text); +MAKE_REFLECT_STRUCT(TextDocumentDidChangeParam, textDocument, contentChanges); +MAKE_REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position); +MAKE_REFLECT_STRUCT(RenameParam, textDocument, position, newName); + +// code* +MAKE_REFLECT_STRUCT(CodeActionParam::Context, diagnostics); +MAKE_REFLECT_STRUCT(CodeActionParam, textDocument, range, context); + +// completion +MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind); +MAKE_REFLECT_STRUCT(lsCompletionContext, triggerKind, triggerCharacter); +MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context); + +// formatting +MAKE_REFLECT_STRUCT(FormattingOptions, tabSize, insertSpaces); +MAKE_REFLECT_STRUCT(DocumentFormattingParam, textDocument, options); +MAKE_REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position, + ch, options); +MAKE_REFLECT_STRUCT(DocumentRangeFormattingParam, textDocument, range, options); + +// workspace +MAKE_REFLECT_TYPE_PROXY(FileChangeType); +MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam::Event, uri, type); +MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam, changes); +MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam::Event, added, removed); +MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam, event); +MAKE_REFLECT_STRUCT(WorkspaceSymbolParam, query); namespace { - struct CclsSemanticHighlightSymbol { int id = 0; lsSymbolKind parentKind; @@ -43,6 +75,7 @@ struct CclsSetSkippedRangesParams { }; MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges); + struct ScanLineEvent { lsPosition pos; lsPosition end_pos; // Second key when there is a tie for insertion events. @@ -61,55 +94,161 @@ struct ScanLineEvent { }; } // namespace -MessageHandler::MessageHandler() { - // Dynamically allocate |message_handlers|, otherwise there will be static - // initialization order races. - if (!message_handlers) - message_handlers = new std::vector(); - message_handlers->push_back(this); +void MessageHandler::Bind(const char *method, void (MessageHandler::*handler)(Reader &)) { + method2notification[method] = [this, handler](Reader &reader) { + (this->*handler)(reader); + }; } -std::vector *MessageHandler::message_handlers = nullptr; +template +void MessageHandler::Bind(const char *method, + void (MessageHandler::*handler)(Param &)) { + method2notification[method] = [this, handler](Reader &reader) { + Param param{}; + Reflect(reader, param); + (this->*handler)(param); + }; +} -bool FindFileOrFail(DB *db, Project *project, std::optional id, - const std::string &absolute_path, - QueryFile **out_query_file, int *out_file_id) { - *out_query_file = nullptr; +void MessageHandler::Bind(const char *method, + void (MessageHandler::*handler)(Reader &, + ReplyOnce &)) { + method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) { + (this->*handler)(reader, reply); + }; +} - auto it = db->name2file_id.find(LowerPathIfInsensitive(absolute_path)); +template +void MessageHandler::Bind(const char *method, + void (MessageHandler::*handler)(Param &, + ReplyOnce &)) { + method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) { + Param param{}; + Reflect(reader, param); + (this->*handler)(param, reply); + }; +} + +MessageHandler::MessageHandler() { + Bind("$ccls/call", &MessageHandler::ccls_call); + Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo); + Bind("$ccls/info", &MessageHandler::ccls_info); + Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance); + Bind("$ccls/member", &MessageHandler::ccls_member); + Bind("$ccls/navigate", &MessageHandler::ccls_navigate); + Bind("$ccls/reload", &MessageHandler::ccls_reload); + Bind("$ccls/vars", &MessageHandler::ccls_vars); + Bind("exit", &MessageHandler::exit); + Bind("initialize", &MessageHandler::initialize); + Bind("shutdown", &MessageHandler::shutdown); + Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction); + Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens); + Bind("textDocument/completion", &MessageHandler::textDocument_completion); + Bind("textDocument/definition", &MessageHandler::textDocument_definition); + Bind("textDocument/didChange", &MessageHandler::textDocument_didChange); + Bind("textDocument/didClose", &MessageHandler::textDocument_didClose); + Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen); + Bind("textDocument/didSave", &MessageHandler::textDocument_didSave); + Bind("textDocument/documentHighlight", + &MessageHandler::textDocument_documentHighlight); + Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink); + Bind("textDocument/documentSymbol", + &MessageHandler::textDocument_documentSymbol); + Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange); + Bind("textDocument/formatting", &MessageHandler::textDocument_formatting); + Bind("textDocument/hover", &MessageHandler::textDocument_hover); + Bind("textDocument/implementation", + &MessageHandler::textDocument_implementation); + Bind("textDocument/onTypeFormatting", + &MessageHandler::textDocument_onTypeFormatting); + Bind("textDocument/rangeFormatting", + &MessageHandler::textDocument_rangeFormatting); + Bind("textDocument/references", &MessageHandler::textDocument_references); + Bind("textDocument/rename", &MessageHandler::textDocument_rename); + Bind("textDocument/signatureHelp", + &MessageHandler::textDocument_signatureHelp); + Bind("textDocument/typeDefinition", + &MessageHandler::textDocument_typeDefinition); + Bind("workspace/didChangeConfiguration", + &MessageHandler::workspace_didChangeConfiguration); + Bind("workspace/didChangeWatchedFiles", + &MessageHandler::workspace_didChangeWatchedFiles); + Bind("workspace/didChangeWorkspaceFolders", + &MessageHandler::workspace_didChangeWorkspaceFolders); + Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand); + Bind("workspace/symbol", &MessageHandler::workspace_symbol); +} + +void MessageHandler::Run(InMessage &msg) { + rapidjson::Document &doc = *msg.document; + rapidjson::Value param; + auto it = doc.FindMember("params"); + if (it != doc.MemberEnd()) + param = it->value; + JsonReader reader(¶m); + if (msg.id.Valid()) { + ReplyOnce reply{msg.id}; + auto it = method2request.find(msg.method); + if (it != method2request.end()) { + try { + it->second(reader, reply); + } catch (...) { + LOG_S(ERROR) << "failed to process request " << msg.method; + } + } else { + lsResponseError err; + err.code = lsErrorCodes::MethodNotFound; + err.message = "unknown request " + msg.method; + reply.Error(err); + } + } else { + auto it = method2notification.find(msg.method); + if (it != method2notification.end()) + try { + it->second(reader); + } catch (...) { + LOG_S(ERROR) << "failed to process " << msg.method; + } + } +} + +QueryFile *MessageHandler::FindFile(ReplyOnce &reply, + const std::string &path, + int *out_file_id) { + QueryFile *ret = nullptr; + auto it = db->name2file_id.find(LowerPathIfInsensitive(path)); if (it != db->name2file_id.end()) { QueryFile &file = db->files[it->second]; if (file.def) { - *out_query_file = &file; + ret = &file; if (out_file_id) *out_file_id = it->second; - return true; + return ret; } } if (out_file_id) *out_file_id = -1; - bool has_entry = false; - { - std::lock_guard lock(project->mutex_); - for (auto &[root, folder] : project->root2folder) - has_entry |= folder.path2entry_index.count(absolute_path); - } - - if (id) { + if (reply.id.Valid()) { + bool has_entry = false; + { + std::lock_guard lock(project->mutex_); + for (auto &[root, folder] : project->root2folder) + has_entry |= folder.path2entry_index.count(path); + } lsResponseError err; if (has_entry) { err.code = lsErrorCodes::ServerNotInitialized; - err.message = absolute_path + " is being indexed"; + err.message = path + " is being indexed"; } else { err.code = lsErrorCodes::InternalError; - err.message = "unable to find " + absolute_path; + err.message = "unable to find " + path; } - pipeline::ReplyError(*id, err); + reply.Error(err); } - return false; + return ret; } void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) { @@ -310,3 +449,4 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) { params.symbols.push_back(std::move(entry.second)); pipeline::Notify("$ccls/publishSemanticHighlight", params); } +} // namespace ccls diff --git a/src/message_handler.h b/src/message_handler.h deleted file mode 100644 index dd882597..00000000 --- a/src/message_handler.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include "lsp.h" -#include "query.h" - -#include -#include -#include -#include - -struct CompletionManager; -struct Config; -struct GroupMatch; -struct VFS; -struct IncludeComplete; -struct MultiQueueWaiter; -struct Project; -struct DB; -struct WorkingFile; -struct WorkingFiles; - - -// Usage: -// -// struct FooHandler : MessageHandler { -// // ... -// }; -// REGISTER_MESSAGE_HANDLER(FooHandler); -// -// Then there will be a global FooHandler instance in -// |MessageHandler::message_handlers|. - -#define REGISTER_MESSAGE_HANDLER(type) \ - static type type##message_handler_instance_; - -struct MessageHandler { - DB *db = nullptr; - Project *project = nullptr; - VFS *vfs = nullptr; - WorkingFiles *working_files = nullptr; - CompletionManager *clang_complete = nullptr; - IncludeComplete *include_complete = nullptr; - - virtual MethodType GetMethodType() const = 0; - virtual void Run(std::unique_ptr message) = 0; - - static std::vector *message_handlers; - -protected: - MessageHandler(); -}; - -template struct BaseMessageHandler : MessageHandler { - virtual void Run(TMessage *message) = 0; - - // MessageHandler: - void Run(std::unique_ptr message) override { - Run(static_cast(message.get())); - } -}; - -bool FindFileOrFail(DB *db, Project *project, std::optional id, - const std::string &absolute_path, - QueryFile **out_query_file, int *out_file_id = nullptr); - -void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file); - -void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file); diff --git a/src/message_handler.hh b/src/message_handler.hh new file mode 100644 index 00000000..ad1c5009 --- /dev/null +++ b/src/message_handler.hh @@ -0,0 +1,265 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "lsp.hh" +#include "query.h" + +#include +#include +#include +#include +#include + +namespace ccls { +struct CompletionManager; +struct Config; +struct GroupMatch; +struct VFS; +struct IncludeComplete; +struct MultiQueueWaiter; +struct Project; +struct DB; +struct WorkingFile; +struct WorkingFiles; + +namespace pipeline { +void Reply(lsRequestId id, const std::function &fn); +void ReplyError(lsRequestId id, const std::function &fn); +} + +struct EmptyParam { + bool placeholder; +}; +struct TextDocumentParam { + lsTextDocumentIdentifier textDocument; +}; +struct DidOpenTextDocumentParam { + lsTextDocumentItem textDocument; +}; + +struct TextDocumentPositionParam { + lsTextDocumentIdentifier textDocument; + lsPosition position; +}; + +struct RenameParam { + lsTextDocumentIdentifier textDocument; + lsPosition position; + std::string newName; +}; + +// code* +struct CodeActionParam { + lsTextDocumentIdentifier textDocument; + lsRange range; + struct Context { + std::vector diagnostics; + } context; +}; + +// completion +enum class lsCompletionTriggerKind { + Invoked = 1, + TriggerCharacter = 2, + TriggerForIncompleteCompletions = 3, +}; +struct lsCompletionContext { + lsCompletionTriggerKind triggerKind = lsCompletionTriggerKind::Invoked; + std::optional triggerCharacter; +}; +struct lsCompletionParams : TextDocumentPositionParam { + lsCompletionContext context; +}; +enum class lsCompletionItemKind { + Text = 1, + Method = 2, + Function = 3, + Constructor = 4, + Field = 5, + Variable = 6, + Class = 7, + Interface = 8, + Module = 9, + Property = 10, + Unit = 11, + Value = 12, + Enum = 13, + Keyword = 14, + Snippet = 15, + Color = 16, + File = 17, + Reference = 18, + Folder = 19, + EnumMember = 20, + Constant = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25, +}; +enum class lsInsertTextFormat { + PlainText = 1, + Snippet = 2 +}; +struct lsCompletionItem { + std::string label; + lsCompletionItemKind kind = lsCompletionItemKind::Text; + std::string detail; + std::optional documentation; + std::string sortText; + std::optional filterText; + std::string insertText; + lsInsertTextFormat insertTextFormat = lsInsertTextFormat::PlainText; + lsTextEdit textEdit; + std::vector additionalTextEdits; + + std::vector parameters_; + int score_; + unsigned priority_; + bool use_angle_brackets_ = false; +}; + +// formatting +struct FormattingOptions { + int tabSize; + bool insertSpaces; +}; +struct DocumentFormattingParam { + lsTextDocumentIdentifier textDocument; + FormattingOptions options; +}; +struct DocumentOnTypeFormattingParam { + lsTextDocumentIdentifier textDocument; + lsPosition position; + std::string ch; + FormattingOptions options; +}; +struct DocumentRangeFormattingParam { + lsTextDocumentIdentifier textDocument; + lsRange range; + FormattingOptions options; +}; + +// workspace +enum class FileChangeType { + Created = 1, + Changed = 2, + Deleted = 3, +}; +struct DidChangeWatchedFilesParam { + struct Event { + lsDocumentUri uri; + FileChangeType type; + }; + std::vector changes; +}; +struct DidChangeWorkspaceFoldersParam { + struct Event { + std::vector added, removed; + } event; +}; +struct WorkspaceSymbolParam { + std::string query; +}; + +// TODO llvm 8 llvm::unique_function +template +using Callback = std::function; + +struct ReplyOnce { + lsRequestId id; + template void operator()(Res &result) const { + if (id.Valid()) + pipeline::Reply(id, [&](Writer &w) { Reflect(w, result); }); + } + template void Error(Err &err) const { + if (id.Valid()) + pipeline::ReplyError(id, [&](Writer &w) { Reflect(w, err); }); + } +}; + +struct MessageHandler { + DB *db = nullptr; + Project *project = nullptr; + VFS *vfs = nullptr; + WorkingFiles *working_files = nullptr; + CompletionManager *clang_complete = nullptr; + IncludeComplete *include_complete = nullptr; + + llvm::StringMap> method2notification; + llvm::StringMap> method2request; + + MessageHandler(); + void Run(InMessage &msg); + QueryFile *FindFile(ReplyOnce &reply, const std::string &path, + int *out_file_id = nullptr); + +private: + void Bind(const char *method, void (MessageHandler::*handler)(Reader &)); + template + void Bind(const char *method, void (MessageHandler::*handler)(Param &)); + void Bind(const char *method, + void (MessageHandler::*handler)(Reader &, ReplyOnce &)); + template + void Bind(const char *method, + void (MessageHandler::*handler)(Param &, ReplyOnce &)); + + void ccls_call(Reader &, ReplyOnce &); + void ccls_fileInfo(TextDocumentParam &, ReplyOnce &); + void ccls_info(EmptyParam &, ReplyOnce &); + void ccls_inheritance(Reader &, ReplyOnce &); + void ccls_member(Reader &, ReplyOnce &); + void ccls_navigate(Reader &, ReplyOnce &); + void ccls_reload(Reader &); + void ccls_vars(Reader &, ReplyOnce &); + void exit(EmptyParam &); + void initialize(Reader &, ReplyOnce &); + void shutdown(EmptyParam &, ReplyOnce &); + void textDocument_codeAction(CodeActionParam &, ReplyOnce &); + void textDocument_codeLens(TextDocumentParam &, ReplyOnce &); + void textDocument_completion(lsCompletionParams &, ReplyOnce &); + void textDocument_definition(TextDocumentPositionParam &, ReplyOnce &); + void textDocument_didChange(TextDocumentDidChangeParam &); + void textDocument_didClose(TextDocumentParam &); + void textDocument_didOpen(DidOpenTextDocumentParam &); + void textDocument_didSave(TextDocumentParam &); + void textDocument_documentHighlight(TextDocumentPositionParam &, ReplyOnce &); + void textDocument_documentLink(TextDocumentParam &, ReplyOnce &); + void textDocument_documentSymbol(Reader &, ReplyOnce &); + void textDocument_foldingRange(TextDocumentParam &, ReplyOnce &); + void textDocument_formatting(DocumentFormattingParam &, ReplyOnce &); + void textDocument_hover(TextDocumentPositionParam &, ReplyOnce &); + void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &); + void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &, + ReplyOnce &); + void textDocument_rangeFormatting(DocumentRangeFormattingParam &, + ReplyOnce &); + void textDocument_references(Reader &, ReplyOnce &); + void textDocument_rename(RenameParam &, ReplyOnce &); + void textDocument_signatureHelp(TextDocumentPositionParam &, ReplyOnce &); + void textDocument_typeDefinition(TextDocumentPositionParam &, ReplyOnce &); + void workspace_didChangeConfiguration(EmptyParam &); + void workspace_didChangeWatchedFiles(DidChangeWatchedFilesParam &); + void workspace_didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParam &); + void workspace_executeCommand(Reader &, ReplyOnce &); + void workspace_symbol(WorkspaceSymbolParam &, ReplyOnce &); +}; + +void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file); + +void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file); +} // namespace ccls diff --git a/src/messages/ccls_call.cc b/src/messages/ccls_call.cc index dc6f57c1..46b9044e 100644 --- a/src/messages/ccls_call.cc +++ b/src/messages/ccls_call.cc @@ -2,18 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 #include "hierarchy.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "query_utils.h" #include -using namespace ccls; +namespace ccls { namespace { -MethodType kMethodType = "$ccls/call"; - enum class CallType : uint8_t { Direct = 0, Base = 1, @@ -26,34 +24,24 @@ bool operator&(CallType lhs, CallType rhs) { return uint8_t(lhs) & uint8_t(rhs); } -struct In_cclsCall : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } +struct Param : TextDocumentPositionParam { + // If id is specified, expand a node; otherwise textDocument+position should + // be specified for building the root and |levels| of nodes below. + Usr usr; + std::string id; - struct Params { - // If id is specified, expand a node; otherwise textDocument+position should - // be specified for building the root and |levels| of nodes below. - lsTextDocumentIdentifier textDocument; - lsPosition position; - - Usr usr; - std::string id; - - // true: callee tree (functions called by this function); false: caller tree - // (where this function is called) - bool callee = false; - // Base: include base functions; All: include both base and derived - // functions. - CallType callType = CallType::All; - bool qualified = true; - int levels = 1; - bool hierarchy = false; - }; - Params params; + // true: callee tree (functions called by this function); false: caller tree + // (where this function is called) + bool callee = false; + // Base: include base functions; All: include both base and derived + // functions. + CallType callType = CallType::All; + bool qualified = true; + int levels = 1; + bool hierarchy = false; }; -MAKE_REFLECT_STRUCT(In_cclsCall::Params, textDocument, position, id, callee, - callType, qualified, levels, hierarchy); -MAKE_REFLECT_STRUCT(In_cclsCall, id, params); -REGISTER_IN_MESSAGE(In_cclsCall); +MAKE_REFLECT_STRUCT(Param, textDocument, position, id, callee, callType, + qualified, levels, hierarchy); struct Out_cclsCall { Usr usr; @@ -163,70 +151,64 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee, return true; } -struct Handler_cclsCall : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } +std::optional BuildInitial(MessageHandler *m, Usr root_usr, + bool callee, CallType call_type, + bool qualified, int levels) { + const auto *def = m->db->Func(root_usr).AnyDef(); + if (!def) + return {}; - std::optional BuildInitial(Usr root_usr, bool callee, - CallType call_type, bool qualified, - int levels) { - const auto *def = db->Func(root_usr).AnyDef(); - if (!def) - return {}; - - Out_cclsCall entry; - entry.id = std::to_string(root_usr); - entry.usr = root_usr; - entry.callType = CallType::Direct; - if (def->spell) { - if (std::optional loc = - GetLsLocation(db, working_files, *def->spell)) - entry.location = *loc; - } - Expand(this, &entry, callee, call_type, qualified, levels); - return entry; + Out_cclsCall entry; + entry.id = std::to_string(root_usr); + entry.usr = root_usr; + entry.callType = CallType::Direct; + if (def->spell) { + if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell)) + entry.location = *loc; } - - void Run(In_cclsCall *request) override { - auto ¶ms = request->params; - std::optional result; - if (params.id.size()) { - try { - params.usr = std::stoull(params.id); - } catch (...) { - return; - } - result.emplace(); - result->id = std::to_string(params.usr); - result->usr = params.usr; - result->callType = CallType::Direct; - if (db->HasFunc(params.usr)) - Expand(this, &*result, params.callee, params.callType, params.qualified, - params.levels); - } else { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *working_file = - working_files->GetFileByFilename(file->def->path); - for (SymbolRef sym : - FindSymbolsAtLocation(working_file, file, params.position)) { - if (sym.kind == SymbolKind::Func) { - result = BuildInitial(sym.usr, params.callee, params.callType, - params.qualified, params.levels); - break; - } - } - } - - if (params.hierarchy) - pipeline::Reply(request->id, result); - else { - auto out = FlattenHierarchy(result); - pipeline::Reply(request->id, out); - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsCall); - + Expand(m, &entry, callee, call_type, qualified, levels); + return entry; +} } // namespace + +void MessageHandler::ccls_call(Reader &reader, ReplyOnce &reply) { + Param param; + Reflect(reader, param); + std::optional result; + if (param.id.size()) { + try { + param.usr = std::stoull(param.id); + } catch (...) { + return; + } + result.emplace(); + result->id = std::to_string(param.usr); + result->usr = param.usr; + result->callType = CallType::Direct; + if (db->HasFunc(param.usr)) + Expand(this, &*result, param.callee, param.callType, param.qualified, + param.levels); + } else { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *working_file = + working_files->GetFileByFilename(file->def->path); + for (SymbolRef sym : + FindSymbolsAtLocation(working_file, file, param.position)) { + if (sym.kind == SymbolKind::Func) { + result = BuildInitial(this, sym.usr, param.callee, param.callType, + param.qualified, param.levels); + break; + } + } + } + + if (param.hierarchy) + reply(result); + else { + auto out = FlattenHierarchy(result); + reply(out); + } +} +} // namespace ccls diff --git a/src/messages/ccls_info.cc b/src/messages/ccls_info.cc index 4e4766bb..19a68a2b 100644 --- a/src/messages/ccls_info.cc +++ b/src/messages/ccls_info.cc @@ -13,24 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" -#include "project.h" +#include "project.hh" #include "query_utils.h" -using namespace ccls; +namespace ccls { MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges, dependencies); namespace { -MethodType cclsInfo = "$ccls/info", fileInfo = "$ccls/fileInfo"; - -struct In_cclsInfo : public RequestMessage { - MethodType GetMethodType() const override { return cclsInfo; } -}; -MAKE_REFLECT_STRUCT(In_cclsInfo, id); -REGISTER_IN_MESSAGE(In_cclsInfo); - struct Out_cclsInfo { struct DB { int files, funcs, types, vars; @@ -46,51 +38,33 @@ MAKE_REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars); MAKE_REFLECT_STRUCT(Out_cclsInfo::Pipeline, pendingIndexRequests); MAKE_REFLECT_STRUCT(Out_cclsInfo::Project, entries); MAKE_REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project); - -struct Handler_cclsInfo : BaseMessageHandler { - MethodType GetMethodType() const override { return cclsInfo; } - void Run(In_cclsInfo *request) override { - Out_cclsInfo result; - result.db.files = db->files.size(); - result.db.funcs = db->funcs.size(); - result.db.types = db->types.size(); - result.db.vars = db->vars.size(); - result.pipeline.pendingIndexRequests = pipeline::pending_index_requests; - result.project.entries = 0; - for (auto &[_, folder] : project->root2folder) - result.project.entries += folder.entries.size(); - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsInfo); - -struct In_cclsFileInfo : public RequestMessage { - MethodType GetMethodType() const override { return fileInfo; } - struct Params { - lsTextDocumentIdentifier textDocument; - } params; -}; -MAKE_REFLECT_STRUCT(In_cclsFileInfo::Params, textDocument); -MAKE_REFLECT_STRUCT(In_cclsFileInfo, id, params); -REGISTER_IN_MESSAGE(In_cclsFileInfo); - -struct Handler_cclsFileInfo : BaseMessageHandler { - MethodType GetMethodType() const override { return fileInfo; } - void Run(In_cclsFileInfo *request) override { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file)) - return; - - QueryFile::Def result; - // Expose some fields of |QueryFile::Def|. - result.path = file->def->path; - result.args = file->def->args; - result.language = file->def->language; - result.includes = file->def->includes; - result.skipped_ranges = file->def->skipped_ranges; - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsFileInfo); } // namespace + +void MessageHandler::ccls_info(EmptyParam &, ReplyOnce &reply) { + Out_cclsInfo result; + result.db.files = db->files.size(); + result.db.funcs = db->funcs.size(); + result.db.types = db->types.size(); + result.db.vars = db->vars.size(); + result.pipeline.pendingIndexRequests = pipeline::pending_index_requests; + result.project.entries = 0; + for (auto &[_, folder] : project->root2folder) + result.project.entries += folder.entries.size(); + reply(result); +} + +void MessageHandler::ccls_fileInfo(TextDocumentParam ¶m, ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + + QueryFile::Def result; + // Expose some fields of |QueryFile::Def|. + result.path = file->def->path; + result.args = file->def->args; + result.language = file->def->language; + result.includes = file->def->includes; + result.skipped_ranges = file->def->skipped_ranges; + reply(result); +} +} // namespace ccls diff --git a/src/messages/ccls_inheritance.cc b/src/messages/ccls_inheritance.cc index 75a11099..ae57429d 100644 --- a/src/messages/ccls_inheritance.cc +++ b/src/messages/ccls_inheritance.cc @@ -2,41 +2,30 @@ // SPDX-License-Identifier: Apache-2.0 #include "hierarchy.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "query_utils.h" -using namespace ccls; #include +namespace ccls { namespace { -MethodType kMethodType = "$ccls/inheritance", - implementation = "textDocument/implementation"; +struct Param : TextDocumentPositionParam { + // If id+kind are specified, expand a node; otherwise textDocument+position + // should be specified for building the root and |levels| of nodes below. + Usr usr; + std::string id; + SymbolKind kind = SymbolKind::Invalid; -struct In_cclsInheritance : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - // If id+kind are specified, expand a node; otherwise textDocument+position - // should be specified for building the root and |levels| of nodes below. - lsTextDocumentIdentifier textDocument; - lsPosition position; - - Usr usr; - std::string id; - SymbolKind kind = SymbolKind::Invalid; - - // true: derived classes/functions; false: base classes/functions - bool derived = false; - bool qualified = true; - int levels = 1; - bool hierarchy = false; - } params; + // true: derived classes/functions; false: base classes/functions + bool derived = false; + bool qualified = true; + int levels = 1; + bool hierarchy = false; }; -MAKE_REFLECT_STRUCT(In_cclsInheritance::Params, textDocument, position, id, - kind, derived, qualified, levels, hierarchy); -MAKE_REFLECT_STRUCT(In_cclsInheritance, id, params); -REGISTER_IN_MESSAGE(In_cclsInheritance); +MAKE_REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified, + levels, hierarchy); struct Out_cclsInheritance { Usr usr; @@ -118,85 +107,68 @@ bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived, m->db->Type(entry->usr)); } -struct Handler_cclsInheritance : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } +std::optional BuildInitial(MessageHandler *m, SymbolRef sym, bool derived, + bool qualified, int levels) { + Out_cclsInheritance entry; + entry.id = std::to_string(sym.usr); + entry.usr = sym.usr; + entry.kind = sym.kind; + Expand(m, &entry, derived, qualified, levels); + return entry; +} - std::optional BuildInitial(SymbolRef sym, bool derived, - bool qualified, int levels) { - Out_cclsInheritance entry; - entry.id = std::to_string(sym.usr); - entry.usr = sym.usr; - entry.kind = sym.kind; - Expand(this, &entry, derived, qualified, levels); - return entry; - } +void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) { + std::optional result; + if (param.id.size()) { + try { + param.usr = std::stoull(param.id); + } catch (...) { + return; + } + result.emplace(); + result->id = std::to_string(param.usr); + result->usr = param.usr; + result->kind = param.kind; + if (!(((param.kind == SymbolKind::Func && m->db->HasFunc(param.usr)) || + (param.kind == SymbolKind::Type && m->db->HasType(param.usr))) && + Expand(m, &*result, param.derived, param.qualified, + param.levels))) + result.reset(); + } else { + QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = m->working_files->GetFileByFilename(file->def->path); - void Run(In_cclsInheritance *request) override { - auto ¶ms = request->params; - std::optional result; - if (params.id.size()) { - try { - params.usr = std::stoull(params.id); - } catch (...) { - return; + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) + if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) { + result = BuildInitial(m, sym, param.derived, param.qualified, + param.levels); + break; } - result.emplace(); - result->id = std::to_string(params.usr); - result->usr = params.usr; - result->kind = params.kind; - if (!(((params.kind == SymbolKind::Func && db->HasFunc(params.usr)) || - (params.kind == SymbolKind::Type && db->HasType(params.usr))) && - Expand(this, &*result, params.derived, params.qualified, - params.levels))) - result.reset(); - } else { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - - for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) - if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) { - result = BuildInitial(sym, params.derived, params.qualified, - params.levels); - break; - } - } - - if (params.hierarchy) - pipeline::Reply(request->id, result); - else { - auto out = FlattenHierarchy(result); - pipeline::Reply(request->id, out); - } } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsInheritance); -struct In_textDocumentImplementation : public RequestMessage { - MethodType GetMethodType() const override { return implementation; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_textDocumentImplementation, id, params); -REGISTER_IN_MESSAGE(In_textDocumentImplementation); - -struct Handler_textDocumentImplementation - : BaseMessageHandler { - MethodType GetMethodType() const override { return implementation; } - void Run(In_textDocumentImplementation *request) override { - Handler_cclsInheritance handler; - handler.db = db; - handler.project = project; - handler.working_files = working_files; - - In_cclsInheritance request1; - request1.id = request->id; - request1.params.textDocument = request->params.textDocument; - request1.params.position = request->params.position; - request1.params.derived = true; - handler.Run(&request1); + if (param.hierarchy) + reply(result); + else { + auto out = FlattenHierarchy(result); + reply(out); } -}; -REGISTER_MESSAGE_HANDLER(Handler_textDocumentImplementation); +} } // namespace + +void MessageHandler::ccls_inheritance(Reader &reader, ReplyOnce &reply) { + Param param; + Reflect(reader, param); + Inheritance(this, param, reply); +} + +void MessageHandler::textDocument_implementation( + TextDocumentPositionParam ¶m, ReplyOnce &reply) { + Param param1; + param1.textDocument = param.textDocument; + param1.position = param.position; + param1.derived = true; + Inheritance(this, param1, reply); +} +} // namespace ccls diff --git a/src/messages/ccls_member.cc b/src/messages/ccls_member.cc index e7714724..9c6f087d 100644 --- a/src/messages/ccls_member.cc +++ b/src/messages/ccls_member.cc @@ -3,7 +3,7 @@ #include "clang_tu.hh" #include "hierarchy.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "query_utils.h" @@ -12,38 +12,26 @@ #include -using namespace ccls; +namespace ccls { using namespace clang; namespace { -MethodType kMethodType = "$ccls/member"; +struct Param : TextDocumentPositionParam { + // If id is specified, expand a node; otherwise textDocument+position should + // be specified for building the root and |levels| of nodes below. + Usr usr; + std::string id; -struct In_cclsMember : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - - struct Params { - // If id is specified, expand a node; otherwise textDocument+position should - // be specified for building the root and |levels| of nodes below. - lsTextDocumentIdentifier textDocument; - lsPosition position; - - // Type - Usr usr; - std::string id; - - bool qualified = false; - int levels = 1; - // If SymbolKind::Func and the point is at a type, list member functions - // instead of member variables. - SymbolKind kind = SymbolKind::Var; - bool hierarchy = false; - } params; + bool qualified = false; + int levels = 1; + // If SymbolKind::Func and the point is at a type, list member functions + // instead of member variables. + SymbolKind kind = SymbolKind::Var; + bool hierarchy = false; }; -MAKE_REFLECT_STRUCT(In_cclsMember::Params, textDocument, position, id, - qualified, levels, kind, hierarchy); -MAKE_REFLECT_STRUCT(In_cclsMember, id, params); -REGISTER_IN_MESSAGE(In_cclsMember); +MAKE_REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind, + hierarchy); struct Out_cclsMember { Usr usr; @@ -221,106 +209,99 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified, return true; } -struct Handler_cclsMember : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - - std::optional BuildInitial(SymbolKind kind, Usr root_usr, - bool qualified, int levels, - SymbolKind memberKind) { - switch (kind) { - default: +std::optional BuildInitial(MessageHandler *m, SymbolKind kind, + Usr root_usr, bool qualified, + int levels, SymbolKind memberKind) { + switch (kind) { + default: + return {}; + case SymbolKind::Func: { + const auto *def = m->db->Func(root_usr).AnyDef(); + if (!def) return {}; - case SymbolKind::Func: { - const auto *def = db->Func(root_usr).AnyDef(); - if (!def) - return {}; - Out_cclsMember entry; - // Not type, |id| is invalid. - entry.name = def->Name(qualified); - if (def->spell) { - if (std::optional loc = - GetLsLocation(db, working_files, *def->spell)) - entry.location = *loc; - } - for (Usr usr : def->vars) { - auto &var = db->Var(usr); - if (var.def.size()) - DoField(this, &entry, var, -1, qualified, levels - 1); - } - return entry; - } - case SymbolKind::Type: { - const auto *def = db->Type(root_usr).AnyDef(); - if (!def) - return {}; - - Out_cclsMember entry; - entry.id = std::to_string(root_usr); - entry.usr = root_usr; - if (def->spell) { - if (std::optional loc = - GetLsLocation(db, working_files, *def->spell)) - entry.location = *loc; - } - Expand(this, &entry, qualified, levels, memberKind); - return entry; + Out_cclsMember entry; + // Not type, |id| is invalid. + entry.name = def->Name(qualified); + if (def->spell) { + if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell)) + entry.location = *loc; } + for (Usr usr : def->vars) { + auto &var = m->db->Var(usr); + if (var.def.size()) + DoField(m, &entry, var, -1, qualified, levels - 1); } + return entry; } + case SymbolKind::Type: { + const auto *def = m->db->Type(root_usr).AnyDef(); + if (!def) + return {}; - void Run(In_cclsMember *request) override { - auto ¶ms = request->params; - std::optional result; - if (params.id.size()) { - try { - params.usr = std::stoull(params.id); - } catch (...) { - return; - } - result.emplace(); - result->id = std::to_string(params.usr); - result->usr = params.usr; - // entry.name is empty as it is known by the client. - if (!(db->HasType(params.usr) && Expand(this, &*result, params.qualified, - params.levels, params.kind))) - result.reset(); - } else { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - for (SymbolRef sym : - FindSymbolsAtLocation(wfile, file, params.position)) { - switch (sym.kind) { - case SymbolKind::Func: - case SymbolKind::Type: - result = BuildInitial(sym.kind, sym.usr, params.qualified, - params.levels, params.kind); - break; - case SymbolKind::Var: { - const QueryVar::Def *def = db->GetVar(sym).AnyDef(); - if (def && def->type) - result = BuildInitial(SymbolKind::Type, def->type, params.qualified, - params.levels, params.kind); - break; - } - default: - continue; - } + Out_cclsMember entry; + entry.id = std::to_string(root_usr); + entry.usr = root_usr; + if (def->spell) { + if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell)) + entry.location = *loc; + } + Expand(m, &entry, qualified, levels, memberKind); + return entry; + } + } +} +} // namespace + +void MessageHandler::ccls_member(Reader &reader, ReplyOnce &reply) { + Param param; + Reflect(reader, param); + std::optional result; + if (param.id.size()) { + try { + param.usr = std::stoull(param.id); + } catch (...) { + return; + } + result.emplace(); + result->id = std::to_string(param.usr); + result->usr = param.usr; + // entry.name is empty as it is known by the client. + if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified, + param.levels, param.kind))) + result.reset(); + } else { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + for (SymbolRef sym : + FindSymbolsAtLocation(wfile, file, param.position)) { + switch (sym.kind) { + case SymbolKind::Func: + case SymbolKind::Type: + result = BuildInitial(this, sym.kind, sym.usr, param.qualified, + param.levels, param.kind); + break; + case SymbolKind::Var: { + const QueryVar::Def *def = db->GetVar(sym).AnyDef(); + if (def && def->type) + result = BuildInitial(this, SymbolKind::Type, def->type, param.qualified, + param.levels, param.kind); break; } - } - - if (params.hierarchy) - pipeline::Reply(request->id, result); - else { - auto out = FlattenHierarchy(result); - pipeline::Reply(request->id, out); + default: + continue; + } + break; } } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsMember); -} // namespace + if (param.hierarchy) + reply(result); + else { + auto out = FlattenHierarchy(result); + reply(out); + } +} +} // namespace ccls diff --git a/src/messages/ccls_navigate.cc b/src/messages/ccls_navigate.cc index 0af052a8..f4ca72bb 100644 --- a/src/messages/ccls_navigate.cc +++ b/src/messages/ccls_navigate.cc @@ -13,25 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "message_handler.h" -#include "pipeline.hh" +#include "message_handler.hh" #include "query_utils.h" -using namespace ccls; +namespace ccls { namespace { -MethodType kMethodType = "$ccls/navigate"; - -struct In_CclsNavigate : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - lsTextDocumentIdentifier textDocument; - lsPosition position; - std::string direction; - } params; +struct Param { + lsTextDocumentIdentifier textDocument; + lsPosition position; + std::string direction; }; -MAKE_REFLECT_STRUCT(In_CclsNavigate::Params, textDocument, position, direction); -MAKE_REFLECT_STRUCT(In_CclsNavigate, id, params); -REGISTER_IN_MESSAGE(In_CclsNavigate); +MAKE_REFLECT_STRUCT(Param, textDocument, position, direction); Maybe FindParent(QueryFile *file, Position pos) { Maybe parent; @@ -44,75 +36,72 @@ Maybe FindParent(QueryFile *file, Position pos) { parent = sym.extent; return parent; } - -struct Handler_CclsNavigate : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_CclsNavigate *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - - WorkingFile *wfile = - working_files->GetFileByFilename(file->def->path); - lsPosition ls_pos = request->params.position; - if (wfile && wfile->index_lines.size()) - if (auto line = wfile->GetIndexPosFromBufferPos( - ls_pos.line, &ls_pos.character, false)) - ls_pos.line = *line; - Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character}; - - Maybe res; - switch (params.direction[0]) { - case 'D': { - Maybe parent = FindParent(file, pos); - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && pos < sym.extent.start && - (!parent || sym.extent.end <= parent->end) && - (!res || sym.extent.start < res->start)) - res = sym.extent; - break; - } - case 'L': - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos && - (!res || (res->end == sym.extent.end ? sym.extent.start < res->start - : res->end < sym.extent.end))) - res = sym.extent; - break; - case 'R': { - Maybe parent = FindParent(file, pos); - if (parent && parent->start.line == pos.line && pos < parent->end) { - pos = parent->end; - if (pos.column) - pos.column--; - } - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start && - (!res || - (sym.extent.start == res->start ? res->end < sym.extent.end - : sym.extent.start < res->start))) - res = sym.extent; - break; - } - case 'U': - default: - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos && - pos < sym.extent.end && (!res || res->start < sym.extent.start)) - res = sym.extent; - break; - } - std::vector result; - if (res) - if (auto ls_range = GetLsRange(wfile, *res)) { - lsLocation &ls_loc = result.emplace_back(); - ls_loc.uri = params.textDocument.uri; - ls_loc.range = *ls_range; - } - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_CclsNavigate); } // namespace + +void MessageHandler::ccls_navigate(Reader &reader, + ReplyOnce &reply) { + Param param; + Reflect(reader, param); + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + lsPosition ls_pos = param.position; + if (wfile && wfile->index_lines.size()) + if (auto line = wfile->GetIndexPosFromBufferPos(ls_pos.line, + &ls_pos.character, false)) + ls_pos.line = *line; + Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character}; + + Maybe res; + switch (param.direction[0]) { + case 'D': { + Maybe parent = FindParent(file, pos); + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && pos < sym.extent.start && + (!parent || sym.extent.end <= parent->end) && + (!res || sym.extent.start < res->start)) + res = sym.extent; + break; + } + case 'L': + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos && + (!res || (res->end == sym.extent.end ? sym.extent.start < res->start + : res->end < sym.extent.end))) + res = sym.extent; + break; + case 'R': { + Maybe parent = FindParent(file, pos); + if (parent && parent->start.line == pos.line && pos < parent->end) { + pos = parent->end; + if (pos.column) + pos.column--; + } + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start && + (!res || + (sym.extent.start == res->start ? res->end < sym.extent.end + : sym.extent.start < res->start))) + res = sym.extent; + break; + } + case 'U': + default: + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos && + pos < sym.extent.end && (!res || res->start < sym.extent.start)) + res = sym.extent; + break; + } + std::vector result; + if (res) + if (auto ls_range = GetLsRange(wfile, *res)) { + lsLocation &ls_loc = result.emplace_back(); + ls_loc.uri = param.textDocument.uri; + ls_loc.range = *ls_range; + } + reply(result); +} +} // namespace ccls diff --git a/src/messages/ccls_reload.cc b/src/messages/ccls_reload.cc index 0d4ade3a..57c29fed 100644 --- a/src/messages/ccls_reload.cc +++ b/src/messages/ccls_reload.cc @@ -3,80 +3,34 @@ #include "clang_complete.hh" #include "match.h" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" -#include "project.h" +#include "project.hh" #include "working_files.h" #include #include -using namespace ccls; - +namespace ccls { namespace { -MethodType kMethodType = "$ccls/reload"; - -struct In_cclsReload : public NotificationMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - bool dependencies = true; - std::vector whitelist; - std::vector blacklist; - } params; +struct Param { + bool dependencies = true; + std::vector whitelist; + std::vector blacklist; }; -MAKE_REFLECT_STRUCT(In_cclsReload::Params, dependencies, whitelist, blacklist); -MAKE_REFLECT_STRUCT(In_cclsReload, params); -REGISTER_IN_MESSAGE(In_cclsReload); - -struct Handler_cclsReload : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_cclsReload *request) override { - const auto ¶ms = request->params; - // Send index requests for every file. - if (params.whitelist.empty() && params.blacklist.empty()) { - vfs->Clear(); - db->clear(); - project->Index(working_files, lsRequestId()); - clang_complete->FlushAllSessions(); - return; - } - - // TODO - GroupMatch matcher(params.whitelist, params.blacklist); - - std::queue q; - // |need_index| stores every filename ever enqueued. - std::unordered_set need_index; - // Reverse dependency graph. - std::unordered_map> graph; - // filename -> QueryFile mapping for files haven't enqueued. - std::unordered_map path_to_file; - - for (const auto &file : db->files) - if (file.def) { - if (matcher.IsMatch(file.def->path)) - q.push(&file); - else - path_to_file[file.def->path] = &file; - for (const std::string &dependency : file.def->dependencies) - graph[dependency].push_back(file.def->path); - } - - while (!q.empty()) { - const QueryFile *file = q.front(); - q.pop(); - need_index.insert(file->def->path); - - if (request->params.dependencies) - for (const std::string &path : graph[file->def->path]) { - auto it = path_to_file.find(path); - if (it != path_to_file.end()) { - q.push(it->second); - path_to_file.erase(it); - } - } - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsReload); +MAKE_REFLECT_STRUCT(Param, dependencies, whitelist, blacklist); } // namespace + +void MessageHandler::ccls_reload(Reader &reader) { + Param param; + Reflect(reader, param); + // Send index requests for every file. + if (param.whitelist.empty() && param.blacklist.empty()) { + vfs->Clear(); + db->clear(); + project->Index(working_files, lsRequestId()); + clang_complete->FlushAllSessions(); + return; + } +} +} // namespace ccls diff --git a/src/messages/ccls_vars.cc b/src/messages/ccls_vars.cc index 00626e7f..edd84181 100644 --- a/src/messages/ccls_vars.cc +++ b/src/messages/ccls_vars.cc @@ -1,63 +1,50 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "query_utils.h" -using namespace ccls; +namespace ccls { namespace { -MethodType kMethodType = "$ccls/vars"; - -struct In_cclsVars : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params : lsTextDocumentPositionParams { - // 1: field - // 2: local - // 4: parameter - unsigned kind = ~0u; - } params; +struct Param : TextDocumentPositionParam { + // 1: field + // 2: local + // 4: parameter + unsigned kind = ~0u; }; -MAKE_REFLECT_STRUCT(In_cclsVars::Params, textDocument, position, kind); -MAKE_REFLECT_STRUCT(In_cclsVars, id, params); -REGISTER_IN_MESSAGE(In_cclsVars); - -struct Handler_cclsVars : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - - void Run(In_cclsVars *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - - WorkingFile *working_file = - working_files->GetFileByFilename(file->def->path); - - std::vector result; - for (SymbolRef sym : - FindSymbolsAtLocation(working_file, file, params.position)) { - Usr usr = sym.usr; - switch (sym.kind) { - default: - break; - case SymbolKind::Var: { - const QueryVar::Def *def = db->GetVar(sym).AnyDef(); - if (!def || !def->type) - continue; - usr = def->type; - [[fallthrough]]; - } - case SymbolKind::Type: - result = GetLsLocations( - db, working_files, - GetVarDeclarations(db, db->Type(usr).instances, params.kind)); - break; - } - } - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_cclsVars); +MAKE_REFLECT_STRUCT(Param, textDocument, position, kind); } // namespace + +void MessageHandler::ccls_vars(Reader &reader, ReplyOnce &reply) { + Param param; + Reflect(reader, param); + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *working_file = working_files->GetFileByFilename(file->def->path); + + std::vector result; + for (SymbolRef sym : + FindSymbolsAtLocation(working_file, file, param.position)) { + Usr usr = sym.usr; + switch (sym.kind) { + default: + break; + case SymbolKind::Var: { + const QueryVar::Def *def = db->GetVar(sym).AnyDef(); + if (!def || !def->type) + continue; + usr = def->type; + [[fallthrough]]; + } + case SymbolKind::Type: + result = GetLsLocations( + db, working_files, + GetVarDeclarations(db, db->Type(usr).instances, param.kind)); + break; + } + } + reply(result); +} +} // namespace ccls diff --git a/src/messages/exit.cc b/src/messages/exit.cc deleted file mode 100644 index 64af3f65..00000000 --- a/src/messages/exit.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "message_handler.h" - -namespace { -struct In_Exit : public NotificationMessage { - MethodType GetMethodType() const override { return kMethodType_Exit; } -}; -MAKE_REFLECT_EMPTY_STRUCT(In_Exit); -REGISTER_IN_MESSAGE(In_Exit); - -struct Handler_Exit : MessageHandler { - MethodType GetMethodType() const override { return kMethodType_Exit; } - - void Run(std::unique_ptr request) override { exit(0); } -}; -REGISTER_MESSAGE_HANDLER(Handler_Exit); -} // namespace diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 113701ff..f468120b 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -5,20 +5,21 @@ #include "filesystem.hh" #include "include_complete.h" #include "log.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "platform.h" -#include "project.h" +#include "project.hh" #include "serializers/json.h" #include "working_files.h" #include #include +#include #include #include -using namespace ccls; +namespace ccls { using namespace llvm; // TODO Cleanup global variables @@ -26,8 +27,6 @@ extern std::string g_init_options; namespace { -MethodType kMethodType = "initialize"; - // Code Lens options. struct lsCodeLensOptions { // Code lens has a resolve provider as well. @@ -316,7 +315,7 @@ struct lsInitializeParams { // The initial trace setting. If omitted trace is disabled ('off'). lsTrace trace = lsTrace::Off; - std::vector workspaceFolders; + std::vector workspaceFolders; }; void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) { @@ -336,14 +335,6 @@ void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) { MAKE_REFLECT_STRUCT(lsInitializeParams, rootUri, initializationOptions, capabilities, trace, workspaceFolders); -struct In_InitializeRequest : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - - lsInitializeParams params; -}; -MAKE_REFLECT_STRUCT(In_InitializeRequest, id, params); -REGISTER_IN_MESSAGE(In_InitializeRequest); - struct lsInitializeResult { lsServerCapabilities capabilities; }; @@ -361,120 +352,124 @@ void *Indexer(void *arg_) { h->working_files); return nullptr; } - -struct Handler_Initialize : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - - void Run(In_InitializeRequest *request) override { - auto ¶ms = request->params; - if (!params.rootUri) - return; - std::string project_path = NormalizePath(params.rootUri->GetPath()); - LOG_S(INFO) << "initialize in directory " << project_path << " with uri " - << params.rootUri->raw_uri; - - { - g_config = new Config(params.initializationOptions); - rapidjson::Document reader; - reader.Parse(g_init_options.c_str()); - if (!reader.HasParseError()) { - JsonReader json_reader{&reader}; - try { - Reflect(json_reader, *g_config); - } catch (std::invalid_argument &) { - // This will not trigger because parse error is handled in - // MessageRegistry::Parse in lsp.cc - } - } - - rapidjson::StringBuffer output; - rapidjson::Writer writer(output); - JsonWriter json_writer(&writer); - Reflect(json_writer, *g_config); - LOG_S(INFO) << "initializationOptions: " << output.GetString(); - - if (g_config->cacheDirectory.size()) { - g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory); - EnsureEndsInSlash(g_config->cacheDirectory); - } - } - - // Client capabilities - const auto &capabilities = params.capabilities; - g_config->client.snippetSupport &= - capabilities.textDocument.completion.completionItem.snippetSupport; - g_config->client.hierarchicalDocumentSymbolSupport &= - capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport; - - // Ensure there is a resource directory. - if (g_config->clang.resourceDir.empty()) - g_config->clang.resourceDir = GetDefaultResourceDirectory(); - DoPathMapping(g_config->clang.resourceDir); - LOG_S(INFO) << "Using -resource-dir=" << g_config->clang.resourceDir; - - // Send initialization before starting indexers, so we don't send a - // status update too early. - { - lsInitializeResult result; - pipeline::Reply(request->id, result); - } - - // Set project root. - EnsureEndsInSlash(project_path); - g_config->fallbackFolder = project_path; - for (const lsWorkspaceFolder &wf : request->params.workspaceFolders) { - std::string path = wf.uri.GetPath(); - EnsureEndsInSlash(path); - g_config->workspaceFolders.push_back(path); - LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path; - } - if (request->params.workspaceFolders.empty()) - g_config->workspaceFolders.push_back(project_path); - if (g_config->cacheDirectory.size()) - for (const std::string &folder : g_config->workspaceFolders) { - // Create two cache directories for files inside and outside of the - // project. - std::string escaped = - EscapeFileName(folder.substr(0, folder.size() - 1)); - sys::fs::create_directories(g_config->cacheDirectory + escaped); - sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped); - } - - idx::Init(); - for (const std::string &folder : g_config->workspaceFolders) - project->Load(folder); - - // Start indexer threads. Start this after loading the project, as that - // may take a long time. Indexer threads will emit status/progress - // reports. - if (g_config->index.threads == 0) - g_config->index.threads = std::thread::hardware_concurrency(); - - LOG_S(INFO) << "start " << g_config->index.threads << " indexers"; - for (int i = 0; i < g_config->index.threads; i++) - SpawnThread(Indexer, new std::pair{this, i}); - - // Start scanning include directories before dispatching project - // files, because that takes a long time. - include_complete->Rescan(); - - LOG_S(INFO) << "dispatch initial index requests"; - project->Index(working_files, request->id); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_Initialize); } // namespace -void StandaloneInitialize(const std::string &root, Project &project, - WorkingFiles &wfiles, VFS &vfs, - IncludeComplete &complete) { - Handler_Initialize handler; - handler.project = &project; - handler.working_files = &wfiles; - handler.vfs = &vfs; - handler.include_complete = &complete; +void Initialize(MessageHandler *m, lsInitializeParams ¶m, ReplyOnce &reply) { + std::string project_path = NormalizePath(param.rootUri->GetPath()); + LOG_S(INFO) << "initialize in directory " << project_path << " with uri " + << param.rootUri->raw_uri; - In_InitializeRequest request; - request.params.rootUri = lsDocumentUri::FromPath(root); - handler.Run(&request); + { + g_config = new Config(param.initializationOptions); + rapidjson::Document reader; + reader.Parse(g_init_options.c_str()); + if (!reader.HasParseError()) { + JsonReader json_reader{&reader}; + try { + Reflect(json_reader, *g_config); + } catch (std::invalid_argument &) { + // This will not trigger because parse error is handled in + // MessageRegistry::Parse in lsp.cc + } + } + + rapidjson::StringBuffer output; + rapidjson::Writer writer(output); + JsonWriter json_writer(&writer); + Reflect(json_writer, *g_config); + LOG_S(INFO) << "initializationOptions: " << output.GetString(); + + if (g_config->cacheDirectory.size()) { + g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory); + EnsureEndsInSlash(g_config->cacheDirectory); + } + } + + // Client capabilities + const auto &capabilities = param.capabilities; + g_config->client.snippetSupport &= + capabilities.textDocument.completion.completionItem.snippetSupport; + g_config->client.hierarchicalDocumentSymbolSupport &= + capabilities.textDocument.documentSymbol + .hierarchicalDocumentSymbolSupport; + + // Ensure there is a resource directory. + if (g_config->clang.resourceDir.empty()) + g_config->clang.resourceDir = GetDefaultResourceDirectory(); + DoPathMapping(g_config->clang.resourceDir); + LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir; + + // Send initialization before starting indexers, so we don't send a + // status update too early. + { + lsInitializeResult result; + reply(result); + } + + // Set project root. + EnsureEndsInSlash(project_path); + g_config->fallbackFolder = project_path; + for (const WorkspaceFolder &wf : param.workspaceFolders) { + std::string path = wf.uri.GetPath(); + EnsureEndsInSlash(path); + g_config->workspaceFolders.push_back(path); + LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path; + } + if (param.workspaceFolders.empty()) + g_config->workspaceFolders.push_back(project_path); + if (g_config->cacheDirectory.size()) + for (const std::string &folder : g_config->workspaceFolders) { + // Create two cache directories for files inside and outside of the + // project. + std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1)); + sys::fs::create_directories(g_config->cacheDirectory + escaped); + sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped); + } + + idx::Init(); + for (const std::string &folder : g_config->workspaceFolders) + m->project->Load(folder); + + // Start indexer threads. Start this after loading the project, as that + // may take a long time. Indexer threads will emit status/progress + // reports. + if (g_config->index.threads == 0) + g_config->index.threads = std::thread::hardware_concurrency(); + + LOG_S(INFO) << "start " << g_config->index.threads << " indexers"; + for (int i = 0; i < g_config->index.threads; i++) + SpawnThread(Indexer, new std::pair{m, i}); + + // Start scanning include directories before dispatching project + // files, because that takes a long time. + m->include_complete->Rescan(); + + LOG_S(INFO) << "dispatch initial index requests"; + m->project->Index(m->working_files, reply.id); } + +void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) { + lsInitializeParams param; + Reflect(reader, param); + if (!param.rootUri) + return; + Initialize(this, param, reply); +} + +void StandaloneInitialize(MessageHandler &handler, const std::string &root) { + lsInitializeParams param; + param.rootUri = lsDocumentUri::FromPath(root); + ReplyOnce reply; + Initialize(&handler, param, reply); +} + +void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) { + JsonNull result; + reply(result); +} + +void MessageHandler::exit(EmptyParam &) { + // FIXME cancel threads + ::exit(0); +} +} // namespace ccls diff --git a/src/messages/shutdown.cc b/src/messages/shutdown.cc deleted file mode 100644 index f19f3e03..00000000 --- a/src/messages/shutdown.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "message_handler.h" -#include "pipeline.hh" -using namespace ccls; - -namespace { -MethodType kMethodType = "shutdown"; - -struct In_Shutdown : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } -}; -MAKE_REFLECT_STRUCT(In_Shutdown, id); -REGISTER_IN_MESSAGE(In_Shutdown); - -struct Handler_Shutdown : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_Shutdown *request) override { - JsonNull result; - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_Shutdown); -} // namespace diff --git a/src/messages/textDocument_code.cc b/src/messages/textDocument_code.cc new file mode 100644 index 00000000..ee46bc8a --- /dev/null +++ b/src/messages/textDocument_code.cc @@ -0,0 +1,234 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "message_handler.hh" +#include "pipeline.hh" +#include "query_utils.h" +#include "serializers/json.h" + +#include + +#include + +namespace ccls { +namespace { +struct CodeAction { + std::string title; + const char *kind = "quickfix"; + lsWorkspaceEdit edit; +}; +MAKE_REFLECT_STRUCT(CodeAction, title, kind, edit); +} +void MessageHandler::textDocument_codeAction(CodeActionParam ¶m, + ReplyOnce &reply) { + WorkingFile *wfile = + working_files->GetFileByFilename(param.textDocument.uri.GetPath()); + if (!wfile) { + return; + } + std::vector result; + std::vector diagnostics; + working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; }); + for (lsDiagnostic &diag : diagnostics) + if (diag.fixits_.size()) { + CodeAction &cmd = result.emplace_back(); + cmd.title = "FixIt: " + diag.message; + auto &edit = cmd.edit.documentChanges.emplace_back(); + edit.textDocument.uri = param.textDocument.uri; + edit.textDocument.version = wfile->version; + edit.edits = diag.fixits_; + } + reply(result); +} + +namespace { +struct Cmd_xref { + Usr usr; + SymbolKind kind; + std::string field; +}; +struct lsCommand { + std::string title; + std::string command; + std::vector arguments; +}; +struct lsCodeLens { + lsRange range; + std::optional command; +}; +MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field); +MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments); +MAKE_REFLECT_STRUCT(lsCodeLens, range, command); + +template +std::string ToString(T &v) { + rapidjson::StringBuffer output; + rapidjson::Writer writer(output); + JsonWriter json_writer(&writer); + Reflect(json_writer, v); + return output.GetString(); +} + +struct CommonCodeLensParams { + std::vector *result; + DB *db; + WorkingFile *wfile; +}; +} // namespace + +void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m, + ReplyOnce &reply) { + std::vector result; + std::string path = param.textDocument.uri.GetPath(); + + QueryFile *file = FindFile(reply, path); + WorkingFile *wfile = + file ? working_files->GetFileByFilename(file->def->path) : nullptr; + if (!wfile) { + return; + } + + auto Add = [&](const char *singular, Cmd_xref show, Range range, int num, + bool force_display = false) { + if (!num && !force_display) + return; + std::optional ls_range = GetLsRange(wfile, range); + if (!ls_range) + return; + lsCodeLens &code_lens = result.emplace_back(); + code_lens.range = *ls_range; + code_lens.command = lsCommand(); + code_lens.command->command = std::string(ccls_xref); + bool plural = num > 1 && singular[strlen(singular) - 1] != 'd'; + code_lens.command->title = + llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str(); + code_lens.command->arguments.push_back(ToString(show)); + }; + + std::unordered_set seen; + for (auto [sym, refcnt] : file->symbol2refcnt) { + if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second) + continue; + switch (sym.kind) { + case SymbolKind::Func: { + QueryFunc &func = db->GetFunc(sym); + const QueryFunc::Def *def = func.AnyDef(); + if (!def) + continue; + std::vector base_uses = GetUsesForAllBases(db, func); + std::vector derived_uses = GetUsesForAllDerived(db, func); + Add("ref", {sym.usr, SymbolKind::Func, "uses"}, sym.range, + func.uses.size(), base_uses.empty()); + if (base_uses.size()) + Add("b.ref", {sym.usr, SymbolKind::Func, "bases uses"}, sym.range, + base_uses.size()); + if (derived_uses.size()) + Add("d.ref", {sym.usr, SymbolKind::Func, "derived uses"}, sym.range, + derived_uses.size()); + if (base_uses.empty()) + Add("base", {sym.usr, SymbolKind::Func, "bases"}, sym.range, + def->bases.size()); + Add("derived", {sym.usr, SymbolKind::Func, "derived"}, sym.range, + func.derived.size()); + break; + } + case SymbolKind::Type: { + QueryType &type = db->GetType(sym); + Add("ref", {sym.usr, SymbolKind::Type, "uses"}, sym.range, + type.uses.size(), true); + Add("derived", {sym.usr, SymbolKind::Type, "derived"}, sym.range, + type.derived.size()); + Add("var", {sym.usr, SymbolKind::Type, "instances"}, sym.range, + type.instances.size()); + break; + } + case SymbolKind::Var: { + QueryVar &var = db->GetVar(sym); + const QueryVar::Def *def = var.AnyDef(); + if (!def || (def->is_local() && !g_config->codeLens.localVariables)) + continue; + Add("ref", {sym.usr, SymbolKind::Var, "uses"}, sym.range, var.uses.size(), + def->kind != lsSymbolKind::Macro); + break; + } + case SymbolKind::File: + case SymbolKind::Invalid: + llvm_unreachable(""); + }; + } + + reply(result); +} + +void MessageHandler::workspace_executeCommand(Reader &reader, + ReplyOnce &reply) { + lsCommand param; + Reflect(reader, param); + if (param.arguments.empty()) { + return; + } + rapidjson::Document reader1; + reader1.Parse(param.arguments[0].c_str()); + JsonReader json_reader{&reader1}; + if (param.command == ccls_xref) { + Cmd_xref cmd; + Reflect(json_reader, cmd); + std::vector result; + auto Map = [&](auto &&uses) { + for (auto &use : uses) + if (auto loc = GetLsLocation(db, working_files, use)) + result.push_back(std::move(*loc)); + }; + switch (cmd.kind) { + case SymbolKind::Func: { + QueryFunc &func = db->Func(cmd.usr); + if (cmd.field == "bases") { + if (auto *def = func.AnyDef()) + Map(GetFuncDeclarations(db, def->bases)); + } else if (cmd.field == "bases uses") { + Map(GetUsesForAllBases(db, func)); + } else if (cmd.field == "derived") { + Map(GetFuncDeclarations(db, func.derived)); + } else if (cmd.field == "derived uses") { + Map(GetUsesForAllDerived(db, func)); + } else if (cmd.field == "uses") { + Map(func.uses); + } + break; + } + case SymbolKind::Type: { + QueryType &type = db->Type(cmd.usr); + if (cmd.field == "derived") { + Map(GetTypeDeclarations(db, type.derived)); + } else if (cmd.field == "instances") { + Map(GetVarDeclarations(db, type.instances, 7)); + } else if (cmd.field == "uses") { + Map(type.uses); + } + break; + } + case SymbolKind::Var: { + QueryVar &var = db->Var(cmd.usr); + if (cmd.field == "uses") + Map(var.uses); + break; + } + default: + break; + } + reply(result); + } +} +} // namespace ccls diff --git a/src/messages/textDocument_codeAction.cc b/src/messages/textDocument_codeAction.cc deleted file mode 100644 index ffaa62cd..00000000 --- a/src/messages/textDocument_codeAction.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "message_handler.h" -#include "pipeline.hh" -#include "working_files.h" -using namespace ccls; - -namespace { -MethodType kMethodType = "textDocument/codeAction"; - -struct In_TextDocumentCodeAction : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - - // Contains additional diagnostic information about the context in which - // a code action is run. - struct lsCodeActionContext { - // An array of diagnostics. - std::vector diagnostics; - }; - // Params for the CodeActionRequest - struct lsCodeActionParams { - // The document in which the command was invoked. - lsTextDocumentIdentifier textDocument; - // The range for which the command was invoked. - lsRange range; - // Context carrying additional information. - lsCodeActionContext context; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionContext, - diagnostics); -MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionParams, textDocument, - range, context); -MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentCodeAction); - -struct lsCodeAction { - std::string title; - const char *kind = "quickfix"; - lsWorkspaceEdit edit; -}; -MAKE_REFLECT_STRUCT(lsCodeAction, title, kind, edit); - -struct Handler_TextDocumentCodeAction - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - - void Run(In_TextDocumentCodeAction *request) override { - const auto ¶ms = request->params; - WorkingFile *wfile = - working_files->GetFileByFilename(params.textDocument.uri.GetPath()); - if (!wfile) - return; - std::vector result; - std::vector diagnostics; - working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; }); - for (lsDiagnostic &diag : diagnostics) - if (diag.fixits_.size()) { - lsCodeAction &cmd = result.emplace_back(); - cmd.title = "FixIt: " + diag.message; - auto &edit = cmd.edit.documentChanges.emplace_back(); - edit.textDocument.uri = params.textDocument.uri; - edit.textDocument.version = wfile->version; - edit.edits = diag.fixits_; - } - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeAction); -} // namespace diff --git a/src/messages/textDocument_codeLens.cc b/src/messages/textDocument_codeLens.cc deleted file mode 100644 index 23648250..00000000 --- a/src/messages/textDocument_codeLens.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "message_handler.h" -#include "pipeline.hh" -#include "query_utils.h" -#include "serializers/json.h" - -#include - -#include - -using namespace ccls; - -namespace { -const MethodType codeLens = "textDocument/codeLens", - executeCommand = "workspace/executeCommand"; - -struct lsCommand { - std::string title; - std::string command; - std::vector arguments; -}; -MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments); - -struct lsCodeLens { - lsRange range; - std::optional command; -}; -MAKE_REFLECT_STRUCT(lsCodeLens, range, command); - -struct Cmd_xref { - Usr usr; - SymbolKind kind; - std::string field; -}; -MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field); - -template -std::string ToString(T &v) { - rapidjson::StringBuffer output; - rapidjson::Writer writer(output); - JsonWriter json_writer(&writer); - Reflect(json_writer, v); - return output.GetString(); -} - -struct CommonCodeLensParams { - std::vector *result; - DB *db; - WorkingFile *wfile; -}; - -struct In_TextDocumentCodeLens : public RequestMessage { - MethodType GetMethodType() const override { return codeLens; } - struct Params { - lsTextDocumentIdentifier textDocument; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens::Params, textDocument); -MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentCodeLens); - -struct Handler_TextDocumentCodeLens - : BaseMessageHandler { - MethodType GetMethodType() const override { return codeLens; } - void Run(In_TextDocumentCodeLens *request) override { - auto ¶ms = request->params; - std::vector result; - std::string path = params.textDocument.uri.GetPath(); - - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, path, &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - - auto Add = [&](const char *singular, Cmd_xref show, Range range, int num, - bool force_display = false) { - if (!num && !force_display) - return; - std::optional ls_range = GetLsRange(wfile, range); - if (!ls_range) - return; - lsCodeLens &code_lens = result.emplace_back(); - code_lens.range = *ls_range; - code_lens.command = lsCommand(); - code_lens.command->command = std::string(ccls_xref); - bool plural = num > 1 && singular[strlen(singular) - 1] != 'd'; - code_lens.command->title = - llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str(); - code_lens.command->arguments.push_back(ToString(show)); - }; - - std::unordered_set seen; - for (auto [sym, refcnt] : file->symbol2refcnt) { - if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second) - continue; - switch (sym.kind) { - case SymbolKind::Func: { - QueryFunc &func = db->GetFunc(sym); - const QueryFunc::Def *def = func.AnyDef(); - if (!def) - continue; - std::vector base_uses = GetUsesForAllBases(db, func); - std::vector derived_uses = GetUsesForAllDerived(db, func); - Add("ref", {sym.usr, SymbolKind::Func, "uses"}, sym.range, - func.uses.size(), base_uses.empty()); - if (base_uses.size()) - Add("b.ref", {sym.usr, SymbolKind::Func, "bases uses"}, sym.range, - base_uses.size()); - if (derived_uses.size()) - Add("d.ref", {sym.usr, SymbolKind::Func, "derived uses"}, sym.range, - derived_uses.size()); - if (base_uses.empty()) - Add("base", {sym.usr, SymbolKind::Func, "bases"}, sym.range, - def->bases.size()); - Add("derived", {sym.usr, SymbolKind::Func, "derived"}, sym.range, - func.derived.size()); - break; - } - case SymbolKind::Type: { - QueryType &type = db->GetType(sym); - Add("ref", {sym.usr, SymbolKind::Type, "uses"}, sym.range, type.uses.size(), - true); - Add("derived", {sym.usr, SymbolKind::Type, "derived"}, sym.range, - type.derived.size()); - Add("var", {sym.usr, SymbolKind::Type, "instances"}, sym.range, - type.instances.size()); - break; - } - case SymbolKind::Var: { - QueryVar &var = db->GetVar(sym); - const QueryVar::Def *def = var.AnyDef(); - if (!def || (def->is_local() && !g_config->codeLens.localVariables)) - continue; - Add("ref", {sym.usr, SymbolKind::Var, "uses"}, sym.range, var.uses.size(), - def->kind != lsSymbolKind::Macro); - break; - } - case SymbolKind::File: - case SymbolKind::Invalid: - llvm_unreachable(""); - }; - } - - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens); - -struct In_WorkspaceExecuteCommand : public RequestMessage { - MethodType GetMethodType() const override { return executeCommand; } - lsCommand params; -}; -MAKE_REFLECT_STRUCT(In_WorkspaceExecuteCommand, id, params); -REGISTER_IN_MESSAGE(In_WorkspaceExecuteCommand); - -struct Handler_WorkspaceExecuteCommand - : BaseMessageHandler { - MethodType GetMethodType() const override { return executeCommand; } - void Run(In_WorkspaceExecuteCommand *request) override { - const auto ¶ms = request->params; - if (params.arguments.empty()) - return; - rapidjson::Document reader; - reader.Parse(params.arguments[0].c_str()); - JsonReader json_reader{&reader}; - if (params.command == ccls_xref) { - Cmd_xref cmd; - Reflect(json_reader, cmd); - std::vector result; - auto Map = [&](auto &&uses) { - for (auto &use : uses) - if (auto loc = GetLsLocation(db, working_files, use)) - result.push_back(std::move(*loc)); - }; - switch (cmd.kind) { - case SymbolKind::Func: { - QueryFunc &func = db->Func(cmd.usr); - if (cmd.field == "bases") { - if (auto *def = func.AnyDef()) - Map(GetFuncDeclarations(db, def->bases)); - } else if (cmd.field == "bases uses") { - Map(GetUsesForAllBases(db, func)); - } else if (cmd.field == "derived") { - Map(GetFuncDeclarations(db, func.derived)); - } else if (cmd.field == "derived uses") { - Map(GetUsesForAllDerived(db, func)); - } else if (cmd.field == "uses") { - Map(func.uses); - } - break; - } - case SymbolKind::Type: { - QueryType &type = db->Type(cmd.usr); - if (cmd.field == "derived") { - Map(GetTypeDeclarations(db, type.derived)); - } else if (cmd.field == "instances") { - Map(GetVarDeclarations(db, type.instances, 7)); - } else if (cmd.field == "uses") { - Map(type.uses); - } - break; - } - case SymbolKind::Var: { - QueryVar &var = db->Var(cmd.usr); - if (cmd.field == "uses") - Map(var.uses); - break; - } - default: - break; - } - pipeline::Reply(request->id, result); - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_WorkspaceExecuteCommand); -} // namespace diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 37b9c508..1e510e4e 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -5,7 +5,7 @@ #include "fuzzy_match.h" #include "include_complete.h" #include "log.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "working_files.h" @@ -14,62 +14,23 @@ #include -using namespace ccls; +namespace ccls { using namespace clang; using namespace llvm; -namespace { -MethodType kMethodType = "textDocument/completion"; - -// How a completion was triggered -enum class lsCompletionTriggerKind { - // Completion was triggered by typing an identifier (24x7 code - // complete), manual invocation (e.g Ctrl+Space) or via API. - Invoked = 1, - // Completion was triggered by a trigger character specified by - // the `triggerCharacters` properties of the `CompletionRegistrationOptions`. - TriggerCharacter = 2, - // Completion was re-triggered as the current completion list is incomplete. - TriggerForIncompleteCompletions = 3, -}; -MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind); - -// Contains additional information about the context in which a completion -// request is triggered. -struct lsCompletionContext { - // How the completion was triggered. - lsCompletionTriggerKind triggerKind = lsCompletionTriggerKind::Invoked; - - // The trigger character (a single character) that has trigger code complete. - // Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` - std::optional triggerCharacter; -}; -MAKE_REFLECT_STRUCT(lsCompletionContext, triggerKind, triggerCharacter); - -struct lsCompletionParams : lsTextDocumentPositionParams { - // The completion context. This is only available it the client specifies to - // send this using - // `ClientCapabilities.textDocument.completion.contextSupport === true` - lsCompletionContext context; -}; -MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context); - -struct In_TextDocumentComplete : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - lsCompletionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentComplete, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentComplete); - struct lsCompletionList { - // This list it not complete. Further typing should result in recomputing - // this list. bool isIncomplete = false; - // The completion items. std::vector items; }; + +MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat); +MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind); +MAKE_REFLECT_STRUCT(lsCompletionItem, label, kind, detail, documentation, + sortText, filterText, insertText, insertTextFormat, + textEdit, additionalTextEdits); MAKE_REFLECT_STRUCT(lsCompletionList, isIncomplete, items); +namespace { void DecorateIncludePaths(const std::smatch &match, std::vector *items) { std::string spaces_after_include = " "; @@ -473,139 +434,129 @@ public: CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } }; +} // namespace -struct Handler_TextDocumentCompletion - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } +void MessageHandler::textDocument_completion(lsCompletionParams ¶m, + ReplyOnce &reply) { + static CompleteConsumerCache> cache; + lsCompletionList result; + std::string path = param.textDocument.uri.GetPath(); + WorkingFile *file = working_files->GetFileByFilename(path); + if (!file) { + return; + } - void Run(In_TextDocumentComplete *request) override { - static CompleteConsumerCache> cache; + // It shouldn't be possible, but sometimes vscode will send queries out + // of order, ie, we get completion request before buffer content update. + std::string buffer_line; + if (param.position.line >= 0 && + param.position.line < file->buffer_lines.size()) + buffer_line = file->buffer_lines[param.position.line]; - const auto ¶ms = request->params; - lsCompletionList result; + // Check for - and : before completing -> or ::, since vscode does not + // support multi-character trigger characters. + if (param.context.triggerKind == lsCompletionTriggerKind::TriggerCharacter && + param.context.triggerCharacter) { + bool did_fail_check = false; - std::string path = params.textDocument.uri.GetPath(); - WorkingFile *file = working_files->GetFileByFilename(path); - if (!file) { - pipeline::Reply(request->id, result); + std::string character = *param.context.triggerCharacter; + int preceding_index = param.position.character - 2; + + // If the character is '"', '<' or '/', make sure that the line starts + // with '#'. + if (character == "\"" || character == "<" || character == "/") { + size_t i = 0; + while (i < buffer_line.size() && isspace(buffer_line[i])) + ++i; + if (i >= buffer_line.size() || buffer_line[i] != '#') + did_fail_check = true; + } + // If the character is > or : and we are at the start of the line, do not + // show completion results. + else if ((character == ">" || character == ":") && preceding_index < 0) { + did_fail_check = true; + } + // If the character is > but - does not preced it, or if it is : and : + // does not preced it, do not show completion results. + else if (preceding_index >= 0 && + preceding_index < (int)buffer_line.size()) { + char preceding = buffer_line[preceding_index]; + did_fail_check = (preceding != '-' && character == ">") || + (preceding != ':' && character == ":"); + } + + if (did_fail_check) { + reply(result); return; } + } - // It shouldn't be possible, but sometimes vscode will send queries out - // of order, ie, we get completion request before buffer content update. - std::string buffer_line; - if (params.position.line >= 0 && - params.position.line < file->buffer_lines.size()) - buffer_line = file->buffer_lines[params.position.line]; + std::string completion_text; + lsPosition end_pos = param.position; + lsPosition begin_pos = file->FindStableCompletionSource( + param.position, &completion_text, &end_pos); - // Check for - and : before completing -> or ::, since vscode does not - // support multi-character trigger characters. - if (params.context.triggerKind == - lsCompletionTriggerKind::TriggerCharacter && - params.context.triggerCharacter) { - bool did_fail_check = false; + ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); - std::string character = *params.context.triggerCharacter; - int preceding_index = params.position.character - 2; - - // If the character is '"', '<' or '/', make sure that the line starts - // with '#'. - if (character == "\"" || character == "<" || character == "/") { - size_t i = 0; - while (i < buffer_line.size() && isspace(buffer_line[i])) - ++i; - if (i >= buffer_line.size() || buffer_line[i] != '#') - did_fail_check = true; - } - // If the character is > or : and we are at the start of the line, do not - // show completion results. - else if ((character == ">" || character == ":") && preceding_index < 0) { - did_fail_check = true; - } - // If the character is > but - does not preced it, or if it is : and : - // does not preced it, do not show completion results. - else if (preceding_index >= 0 && - preceding_index < (int)buffer_line.size()) { - char preceding = buffer_line[preceding_index]; - did_fail_check = (preceding != '-' && character == ">") || - (preceding != ':' && character == ":"); - } - - if (did_fail_check) { - pipeline::Reply(request->id, result); - return; - } + if (preprocess.ok && preprocess.keyword.compare("include") == 0) { + lsCompletionList result; + { + std::unique_lock lock( + include_complete->completion_items_mutex, std::defer_lock); + if (include_complete->is_scanning) + lock.lock(); + std::string quote = preprocess.match[5]; + for (auto &item : include_complete->completion_items) + if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\"")) + result.items.push_back(item); } + begin_pos.character = 0; + end_pos.character = (int)buffer_line.size(); + FilterCandidates(result, preprocess.pattern, begin_pos, end_pos, + buffer_line); + DecorateIncludePaths(preprocess.match, &result.items); + reply(result); + } else { + std::string path = param.textDocument.uri.GetPath(); + CompletionManager::OnComplete callback = + [completion_text, path, begin_pos, end_pos, reply, + buffer_line](CodeCompleteConsumer *OptConsumer) { + if (!OptConsumer) + return; + auto *Consumer = static_cast(OptConsumer); + lsCompletionList result; + result.items = Consumer->ls_items; - std::string completion_text; - lsPosition end_pos = params.position; - lsPosition begin_pos = file->FindStableCompletionSource( - params.position, &completion_text, &end_pos); + FilterCandidates(result, completion_text, begin_pos, end_pos, + buffer_line); + reply(result); + if (!Consumer->from_cache) { + cache.WithLock([&]() { + cache.path = path; + cache.position = begin_pos; + cache.result = Consumer->ls_items; + }); + } + }; - ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); - - if (preprocess.ok && preprocess.keyword.compare("include") == 0) { - lsCompletionList result; - { - std::unique_lock lock( - include_complete->completion_items_mutex, std::defer_lock); - if (include_complete->is_scanning) - lock.lock(); - std::string quote = preprocess.match[5]; - for (auto &item : include_complete->completion_items) - if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\"")) - result.items.push_back(item); - } - begin_pos.character = 0; - end_pos.character = (int)buffer_line.size(); - FilterCandidates(result, preprocess.pattern, begin_pos, end_pos, - buffer_line); - DecorateIncludePaths(preprocess.match, &result.items); - pipeline::Reply(request->id, result); - } else { - std::string path = params.textDocument.uri.GetPath(); - CompletionManager::OnComplete callback = - [completion_text, path, begin_pos, end_pos, - id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) { - if (!OptConsumer) - return; - auto *Consumer = static_cast(OptConsumer); - lsCompletionList result; - result.items = Consumer->ls_items; - - FilterCandidates(result, completion_text, begin_pos, end_pos, - buffer_line); - pipeline::Reply(id, result); - if (!Consumer->from_cache) { - cache.WithLock([&]() { - cache.path = path; - cache.position = begin_pos; - cache.result = Consumer->ls_items; - }); - } - }; - - clang::CodeCompleteOptions CCOpts; - CCOpts.IncludeBriefComments = true; - CCOpts.IncludeCodePatterns = preprocess.ok; // if there is a # + clang::CodeCompleteOptions CCOpts; + CCOpts.IncludeBriefComments = true; + CCOpts.IncludeCodePatterns = preprocess.ok; // if there is a # #if LLVM_VERSION_MAJOR >= 7 - CCOpts.IncludeFixIts = true; + CCOpts.IncludeFixIts = true; #endif - CCOpts.IncludeMacros = true; - if (cache.IsCacheValid(path, begin_pos)) { - CompletionConsumer Consumer(CCOpts, true); - cache.WithLock([&]() { Consumer.ls_items = cache.result; }); - callback(&Consumer); - } else { - clang_complete->completion_request_.PushBack( + CCOpts.IncludeMacros = true; + if (cache.IsCacheValid(path, begin_pos)) { + CompletionConsumer Consumer(CCOpts, true); + cache.WithLock([&]() { Consumer.ls_items = cache.result; }); + callback(&Consumer); + } else { + clang_complete->completion_request_.PushBack( std::make_unique( - request->id, params.textDocument, begin_pos, - std::make_unique(CCOpts, false), CCOpts, - callback)); - } + reply.id, param.textDocument, begin_pos, + std::make_unique(CCOpts, false), CCOpts, + callback)); } } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCompletion); - -} // namespace +} +} // namespace ccls diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index 0e4575b7..05a5407e 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -1,26 +1,15 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" -#include "pipeline.hh" +#include "message_handler.hh" #include "query_utils.h" #include #include #include -using namespace ccls; - +namespace ccls { namespace { -MethodType kMethodType = "textDocument/definition"; - -struct In_TextDocumentDefinition : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDefinition, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentDefinition); - std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { switch (sym.kind) { case SymbolKind::Var: { @@ -42,133 +31,173 @@ std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { return GetNonDefDeclarations(db, sym); } } +} // namespace -struct Handler_TextDocumentDefinition - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_TextDocumentDefinition *request) override { - auto ¶ms = request->params; - int file_id; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file, &file_id)) - return; +void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, + ReplyOnce &reply) { + int file_id; + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); + if (!file) + return; - std::vector result; - Maybe on_def; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - lsPosition &ls_pos = params.position; + std::vector result; + Maybe on_def; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + lsPosition &ls_pos = param.position; - for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) { - // Special cases which are handled: - // - symbol has declaration but no definition (ie, pure virtual) - // - goto declaration while in definition of recursive type - std::vector uses; - EachEntityDef(db, sym, [&](const auto &def) { - if (def.spell) { - Use spell = *def.spell; - if (spell.file_id == file_id && - spell.range.Contains(ls_pos.line, ls_pos.character)) { - on_def = spell; - uses.clear(); - return false; - } - uses.push_back(spell); + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) { + // Special cases which are handled: + // - symbol has declaration but no definition (ie, pure virtual) + // - goto declaration while in definition of recursive type + std::vector uses; + EachEntityDef(db, sym, [&](const auto &def) { + if (def.spell) { + Use spell = *def.spell; + if (spell.file_id == file_id && + spell.range.Contains(ls_pos.line, ls_pos.character)) { + on_def = spell; + uses.clear(); + return false; } - return true; - }); - - // |uses| is empty if on a declaration/definition, otherwise it includes - // all declarations/definitions. - if (uses.empty()) { - for (Use use : GetNonDefDeclarationTargets(db, sym)) - if (!(use.file_id == file_id && - use.range.Contains(ls_pos.line, ls_pos.character))) - uses.push_back(use); - // There is no declaration but the cursor is on a definition. - if (uses.empty() && on_def) - uses.push_back(*on_def); + uses.push_back(spell); } - auto locs = GetLsLocations(db, working_files, uses); - result.insert(result.end(), locs.begin(), locs.end()); - } + return true; + }); - if (result.size()) { - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - } else { - Maybe range; - // Check #include - for (const IndexInclude &include : file->def->includes) { - if (include.line == ls_pos.line) { - result.push_back( - lsLocation{lsDocumentUri::FromPath(include.resolved_path)}); - range = {{0, 0}, {0, 0}}; + // |uses| is empty if on a declaration/definition, otherwise it includes + // all declarations/definitions. + if (uses.empty()) { + for (Use use : GetNonDefDeclarationTargets(db, sym)) + if (!(use.file_id == file_id && + use.range.Contains(ls_pos.line, ls_pos.character))) + uses.push_back(use); + // There is no declaration but the cursor is on a definition. + if (uses.empty() && on_def) + uses.push_back(*on_def); + } + auto locs = GetLsLocations(db, working_files, uses); + result.insert(result.end(), locs.begin(), locs.end()); + } + + if (result.size()) { + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + } else { + Maybe range; + // Check #include + for (const IndexInclude &include : file->def->includes) { + if (include.line == ls_pos.line) { + result.push_back( + lsLocation{lsDocumentUri::FromPath(include.resolved_path)}); + range = {{0, 0}, {0, 0}}; + break; + } + } + // Find the best match of the identifier at point. + if (!range) { + lsPosition position = param.position; + const std::string &buffer = wfile->buffer_content; + std::string_view query = LexIdentifierAroundPos(position, buffer); + std::string_view short_query = query; + { + auto pos = query.rfind(':'); + if (pos != std::string::npos) + short_query = query.substr(pos + 1); + } + + // For symbols whose short/detailed names contain |query| as a + // substring, we use the tuple to find the best match. + std::tuple best_score{INT_MAX, 0, true, 0}; + SymbolIdx best_sym; + best_sym.kind = SymbolKind::Invalid; + auto fn = [&](SymbolIdx sym) { + std::string_view short_name = db->GetSymbolName(sym, false), + name = short_query.size() < query.size() + ? db->GetSymbolName(sym, true) + : short_name; + if (short_name != short_query) + return; + if (Maybe dr = GetDefinitionSpell(db, sym)) { + std::tuple score{ + int(name.size() - short_query.size()), 0, dr->file_id != file_id, + std::abs(dr->range.start.line - position.line)}; + // Update the score with qualified name if the qualified name + // occurs in |name|. + auto pos = name.rfind(query); + if (pos != std::string::npos) { + std::get<0>(score) = int(name.size() - query.size()); + std::get<1>(score) = -int(pos); + } + if (score < best_score) { + best_score = score; + best_sym = sym; + } + } + }; + for (auto &func : db->funcs) + fn({func.usr, SymbolKind::Func}); + for (auto &type : db->types) + fn({type.usr, SymbolKind::Type}); + for (auto &var : db->vars) + if (var.def.size() && !var.def[0].is_local()) + fn({var.usr, SymbolKind::Var}); + + if (best_sym.kind != SymbolKind::Invalid) { + Maybe dr = GetDefinitionSpell(db, best_sym); + assert(dr); + if (auto loc = GetLsLocation(db, working_files, *dr)) + result.push_back(*loc); + } + } + } + + reply(result); +} + +void MessageHandler::textDocument_typeDefinition( + TextDocumentPositionParam ¶m, ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *working_file = working_files->GetFileByFilename(file->def->path); + + std::vector result; + auto Add = [&](const QueryType &type) { + for (const auto &def : type.def) + if (def.spell) { + if (auto ls_loc = GetLsLocation(db, working_files, *def.spell)) + result.push_back(*ls_loc); + } + if (result.empty()) + for (const DeclRef &dr : type.declarations) + if (auto ls_loc = GetLsLocation(db, working_files, dr)) + result.push_back(*ls_loc); + }; + for (SymbolRef sym : + FindSymbolsAtLocation(working_file, file, param.position)) { + switch (sym.kind) { + case SymbolKind::Var: { + const QueryVar::Def *def = db->GetVar(sym).AnyDef(); + if (def && def->type) + Add(db->Type(def->type)); + break; + } + case SymbolKind::Type: { + for (auto &def : db->GetType(sym).def) + if (def.alias_of) { + Add(db->Type(def.alias_of)); break; } - } - // Find the best match of the identifier at point. - if (!range) { - lsPosition position = request->params.position; - const std::string &buffer = wfile->buffer_content; - std::string_view query = LexIdentifierAroundPos(position, buffer); - std::string_view short_query = query; - { - auto pos = query.rfind(':'); - if (pos != std::string::npos) - short_query = query.substr(pos + 1); - } - - // For symbols whose short/detailed names contain |query| as a - // substring, we use the tuple to find the best match. - std::tuple best_score{INT_MAX, 0, true, 0}; - SymbolIdx best_sym; - best_sym.kind = SymbolKind::Invalid; - auto fn = [&](SymbolIdx sym) { - std::string_view short_name = db->GetSymbolName(sym, false), - name = short_query.size() < query.size() - ? db->GetSymbolName(sym, true) - : short_name; - if (short_name != short_query) - return; - if (Maybe dr = GetDefinitionSpell(db, sym)) { - std::tuple score{ - int(name.size() - short_query.size()), 0, - dr->file_id != file_id, - std::abs(dr->range.start.line - position.line)}; - // Update the score with qualified name if the qualified name - // occurs in |name|. - auto pos = name.rfind(query); - if (pos != std::string::npos) { - std::get<0>(score) = int(name.size() - query.size()); - std::get<1>(score) = -int(pos); - } - if (score < best_score) { - best_score = score; - best_sym = sym; - } - } - }; - for (auto &func : db->funcs) - fn({func.usr, SymbolKind::Func}); - for (auto &type : db->types) - fn({type.usr, SymbolKind::Type}); - for (auto &var : db->vars) - if (var.def.size() && !var.def[0].is_local()) - fn({var.usr, SymbolKind::Var}); - - if (best_sym.kind != SymbolKind::Invalid) { - Maybe dr = GetDefinitionSpell(db, best_sym); - assert(dr); - if (auto loc = GetLsLocation(db, working_files, *dr)) - result.push_back(*loc); - } - } + break; + } + default: + break; } - - pipeline::Reply(request->id, result); } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDefinition); -} // namespace + + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + reply(result); +} +} // namespace ccls diff --git a/src/messages/textDocument_did.cc b/src/messages/textDocument_did.cc index 1c284d00..e38bc2c0 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -15,148 +15,56 @@ limitations under the License. #include "clang_complete.hh" #include "include_complete.h" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" -#include "project.h" +#include "project.hh" #include "working_files.h" -using namespace ccls; -namespace { -MethodType didChange = "textDocument/didChange"; -MethodType didClose = "textDocument/didClose"; -MethodType didOpen = "textDocument/didOpen"; -MethodType didSave = "textDocument/didSave"; +namespace ccls { +void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) { + std::string path = param.textDocument.uri.GetPath(); + working_files->OnChange(param); + if (g_config->index.onChange) + pipeline::Index(path, {}, IndexMode::OnChange); + clang_complete->NotifyView(path); + if (g_config->diagnostics.onChange >= 0) + clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange); +} -struct In_TextDocumentDidChange : public NotificationMessage { - MethodType GetMethodType() const override { return didChange; } - lsTextDocumentDidChangeParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDidChange, params); -REGISTER_IN_MESSAGE(In_TextDocumentDidChange); +void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) { + std::string path = param.textDocument.uri.GetPath(); + working_files->OnClose(param.textDocument); + clang_complete->OnClose(path); +} -struct Handler_TextDocumentDidChange - : BaseMessageHandler { - MethodType GetMethodType() const override { return didChange; } +void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) { + std::string path = param.textDocument.uri.GetPath(); + WorkingFile *working_file = working_files->OnOpen(param.textDocument); + if (std::optional cached_file_contents = + pipeline::LoadIndexedContent(path)) + working_file->SetIndexContent(*cached_file_contents); - void Run(In_TextDocumentDidChange *request) override { - const auto ¶ms = request->params; - std::string path = params.textDocument.uri.GetPath(); - working_files->OnChange(params); - if (g_config->index.onChange) - pipeline::Index(path, {}, IndexMode::OnChange); - clang_complete->NotifyView(path); - if (g_config->diagnostics.onChange >= 0) - clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange); + ReplyOnce reply; + QueryFile *file = FindFile(reply, path); + if (file) { + EmitSkippedRanges(working_file, *file); + EmitSemanticHighlight(db, working_file, *file); } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange); + include_complete->AddFile(working_file->filename); -struct In_TextDocumentDidClose : public NotificationMessage { - MethodType GetMethodType() const override { return didClose; } - struct Params { - lsTextDocumentIdentifier textDocument; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDidClose::Params, textDocument); -MAKE_REFLECT_STRUCT(In_TextDocumentDidClose, params); -REGISTER_IN_MESSAGE(In_TextDocumentDidClose); - -struct Handler_TextDocumentDidClose - : BaseMessageHandler { - MethodType GetMethodType() const override { return didClose; } - - void Run(In_TextDocumentDidClose *request) override { - std::string path = request->params.textDocument.uri.GetPath(); - - working_files->OnClose(request->params.textDocument); - clang_complete->OnClose(path); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose); - -struct In_TextDocumentDidOpen : public NotificationMessage { - MethodType GetMethodType() const override { return didOpen; } - - struct Params { - lsTextDocumentItem textDocument; - - // ccls extension - // If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project - // entry (e.g. loaded from compile_commands.json or .ccls). - std::vector args; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen::Params, textDocument, args); -MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen, params); -REGISTER_IN_MESSAGE(In_TextDocumentDidOpen); - -struct Handler_TextDocumentDidOpen - : BaseMessageHandler { - MethodType GetMethodType() const override { return didOpen; } - - void Run(In_TextDocumentDidOpen *request) override { - // NOTE: This function blocks code lens. If it starts taking a long time - // we will need to find a way to unblock the code lens request. - const auto ¶ms = request->params; - const std::string &path = params.textDocument.uri.GetPath(); - - WorkingFile *working_file = working_files->OnOpen(params.textDocument); - if (std::optional cached_file_contents = - pipeline::LoadIndexedContent(path)) - working_file->SetIndexContent(*cached_file_contents); - - QueryFile *file = nullptr; - FindFileOrFail(db, project, std::nullopt, path, &file); - if (file) { - EmitSkippedRanges(working_file, *file); - EmitSemanticHighlight(db, working_file, *file); - } - - include_complete->AddFile(working_file->filename); - std::vector args; - for (const std::string &arg : params.args) - args.push_back(Intern(arg)); - if (args.size()) - project->SetArgsForFile(args, path); - - // Submit new index request if it is not a header file or there is no - // pending index request. - std::pair lang = lookupExtension(path); - if ((lang.first != LanguageId::Unknown && !lang.second) || - !pipeline::pending_index_requests) - pipeline::Index(path, args, IndexMode::Normal); - - clang_complete->NotifyView(path); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen); - -struct In_TextDocumentDidSave : public NotificationMessage { - MethodType GetMethodType() const override { return didSave; } - - struct Params { - // The document that was saved. - lsTextDocumentIdentifier textDocument; - - // Optional the content when saved. Depends on the includeText value - // when the save notifcation was requested. - // std::string text; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument); -MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params); -REGISTER_IN_MESSAGE(In_TextDocumentDidSave); - -struct Handler_TextDocumentDidSave - : BaseMessageHandler { - MethodType GetMethodType() const override { return didSave; } - - void Run(In_TextDocumentDidSave *request) override { - const auto ¶ms = request->params; - const std::string &path = params.textDocument.uri.GetPath(); + // Submit new index request if it is not a header file or there is no + // pending index request. + std::pair lang = lookupExtension(path); + if ((lang.first != LanguageId::Unknown && !lang.second) || + !pipeline::pending_index_requests) pipeline::Index(path, {}, IndexMode::Normal); - clang_complete->NotifySave(path); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidSave); -} // namespace + + clang_complete->NotifyView(path); +} + +void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) { + const std::string &path = param.textDocument.uri.GetPath(); + pipeline::Index(path, {}, IndexMode::Normal); + clang_complete->NotifySave(path); +} +} // namespace ccls diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc index 4041519e..9142683d 100644 --- a/src/messages/textDocument_document.cc +++ b/src/messages/textDocument_document.cc @@ -1,21 +1,19 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "query_utils.h" #include -MAKE_HASHABLE(SymbolIdx, t.usr, t.kind); +MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind); namespace ccls { -namespace { -MethodType documentHighlight = "textDocument/documentHighlight", - documentLink = "textDocument/documentLink", - documentSymbol = "textDocument/documentSymbol"; +MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName); -struct lsDocumentHighlight { +namespace { +struct DocumentHighlight { enum Kind { Text = 1, Read = 2, Write = 3 }; lsRange range; @@ -24,109 +22,81 @@ struct lsDocumentHighlight { // ccls extension Role role = Role::None; - bool operator<(const lsDocumentHighlight &o) const { + bool operator<(const DocumentHighlight &o) const { return !(range == o.range) ? range < o.range : kind < o.kind; } }; -MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role); +MAKE_REFLECT_STRUCT(DocumentHighlight, range, kind, role); +} // namespace -struct In_TextDocumentDocumentHighlight : public RequestMessage { - MethodType GetMethodType() const override { return documentHighlight; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDocumentHighlight, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentDocumentHighlight); +void MessageHandler::textDocument_documentHighlight( + TextDocumentPositionParam ¶m, ReplyOnce &reply) { + int file_id; + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); + if (!file) + return; -struct Handler_TextDocumentDocumentHighlight - : BaseMessageHandler { - MethodType GetMethodType() const override { return documentHighlight; } - void Run(In_TextDocumentDocumentHighlight *request) override { - int file_id; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file, - &file_id)) - return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + std::vector result; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - std::vector result; - - std::vector syms = - FindSymbolsAtLocation(wfile, file, request->params.position, true); - for (auto [sym, refcnt] : file->symbol2refcnt) { - if (refcnt <= 0) - continue; - Usr usr = sym.usr; - SymbolKind kind = sym.kind; - if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) { - return usr == sym1.usr && kind == sym1.kind; - })) - continue; - if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { - lsDocumentHighlight highlight; - highlight.range = loc->range; - if (sym.role & Role::Write) - highlight.kind = lsDocumentHighlight::Write; - else if (sym.role & Role::Read) - highlight.kind = lsDocumentHighlight::Read; - else - highlight.kind = lsDocumentHighlight::Text; - highlight.role = sym.role; - result.push_back(highlight); - } + std::vector syms = + FindSymbolsAtLocation(wfile, file, param.position, true); + for (auto [sym, refcnt] : file->symbol2refcnt) { + if (refcnt <= 0) + continue; + Usr usr = sym.usr; + SymbolKind kind = sym.kind; + if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) { + return usr == sym1.usr && kind == sym1.kind; + })) + continue; + if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { + DocumentHighlight highlight; + highlight.range = loc->range; + if (sym.role & Role::Write) + highlight.kind = DocumentHighlight::Write; + else if (sym.role & Role::Read) + highlight.kind = DocumentHighlight::Read; + else + highlight.kind = DocumentHighlight::Text; + highlight.role = sym.role; + result.push_back(highlight); } - std::sort(result.begin(), result.end()); - pipeline::Reply(request->id, result); } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDocumentHighlight); - -struct In_textDocumentDocumentLink : public RequestMessage { - MethodType GetMethodType() const override { return documentLink; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_textDocumentDocumentLink, id, params); -REGISTER_IN_MESSAGE(In_textDocumentDocumentLink); + std::sort(result.begin(), result.end()); + reply(result); +} +namespace { struct lsDocumentLink { lsRange range; lsDocumentUri target; }; MAKE_REFLECT_STRUCT(lsDocumentLink, range, target); +} // namespace -struct Handler_textDocumentDocumentLink - : BaseMessageHandler { - MethodType GetMethodType() const override { return documentLink; } - void Run(In_textDocumentDocumentLink *request) override { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file)) - return; +void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m, + ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; - std::vector result; - for (const IndexInclude &include : file->def->includes) - result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}}, - lsDocumentUri::FromPath(include.resolved_path)}); - pipeline::Reply(request->id, result); - } + std::vector result; + for (const IndexInclude &include : file->def->includes) + result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}}, + lsDocumentUri::FromPath(include.resolved_path)}); + reply(result); +} // namespace ccls + +namespace { +struct DocumentSymbolParam : TextDocumentParam { + // false: outline; true: all symbols + bool all = false; + // If >= 0, return Range[] instead of SymbolInformation[] to reduce output. + int startLine = -1; + int endLine = -1; }; -REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentLink); - -struct In_textDocumentDocumentSymbol : public RequestMessage { - MethodType GetMethodType() const override { return documentSymbol; } - struct Params { - lsTextDocumentIdentifier textDocument; - // false: outline; true: all symbols - bool all = false; - // If >= 0, return Range[] instead of SymbolInformation[] to reduce output. - int startLine = -1; - int endLine = -1; - } params; -}; -MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol::Params, textDocument, all, - startLine, endLine); -MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol, id, params); -REGISTER_IN_MESSAGE(In_textDocumentDocumentSymbol); +MAKE_REFLECT_STRUCT(DocumentSymbolParam, textDocument, all, startLine, endLine); struct lsDocumentSymbol { std::string name; @@ -155,131 +125,126 @@ template<> bool Ignore(const QueryVar::Def *def) { return !def || def->is_local(); } - -struct Handler_textDocumentDocumentSymbol - : BaseMessageHandler { - MethodType GetMethodType() const override { return documentSymbol; } - void Run(In_textDocumentDocumentSymbol *request) override { - auto ¶ms = request->params; - - QueryFile *file; - int file_id; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file, &file_id)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - if (!wfile) - return; - - if (params.startLine >= 0) { - std::vector result; - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && (params.all || sym.extent.Valid()) && - params.startLine <= sym.range.start.line && - sym.range.start.line <= params.endLine) - if (auto loc = GetLsLocation(db, working_files, sym, file_id)) - result.push_back(loc->range); - std::sort(result.begin(), result.end()); - pipeline::Reply(request->id, result); - } else if (g_config->client.hierarchicalDocumentSymbolSupport) { - std::unordered_map> sym2ds; - std::vector, lsDocumentSymbol *>> - funcs, types; - for (auto [sym, refcnt] : file->symbol2refcnt) { - if (refcnt <= 0 || !sym.extent.Valid()) - continue; - auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind}); - if (!r.second) - continue; - auto &ds = r.first->second; - ds = std::make_unique(); - std::vector def_ptrs; - WithEntity(db, sym, [&, sym = sym](const auto &entity) { - auto *def = entity.AnyDef(); - if (!def) - return; - ds->name = def->Name(false); - ds->detail = def->Name(true); - if (auto ls_range = GetLsRange(wfile, sym.range)) { - ds->selectionRange = *ls_range; - ds->range = ds->selectionRange; - if (sym.extent.Valid()) - if (auto ls_range1 = GetLsRange(wfile, sym.extent)) - ds->range = *ls_range1; - } - - for (auto &def : entity.def) - if (def.file_id == file_id && !Ignore(&def)) { - ds->kind = def.kind; - if (def.spell || def.kind == lsSymbolKind::Namespace) - def_ptrs.push_back(&def); - } - }); - if (def_ptrs.empty() || !(params.all || sym.role & Role::Definition || - ds->kind == lsSymbolKind::Namespace)) { - ds.reset(); - continue; - } - if (sym.kind == SymbolKind::Func) - funcs.emplace_back(std::move(def_ptrs), ds.get()); - else if (sym.kind == SymbolKind::Type) - types.emplace_back(std::move(def_ptrs), ds.get()); - } - - for (auto &[def_ptrs, ds] : funcs) - for (const void *def_ptr : def_ptrs) - for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) { - auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (auto &[def_ptrs, ds] : types) - for (const void *def_ptr : def_ptrs) { - auto *def = (const QueryType::Def *)def_ptr; - for (Usr usr1 : def->funcs) { - auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Func}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (Usr usr1 : def->types) { - auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Type}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (auto [usr1, _] : def->vars) { - auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - } - std::vector> result; - for (auto &[_, ds] : sym2ds) - if (ds) - result.push_back(std::move(ds)); - pipeline::Reply(request->id, result); - } else { - std::vector result; - for (auto [sym, refcnt] : file->symbol2refcnt) { - if (refcnt <= 0 || !sym.extent.Valid() || - !(params.all || sym.role & Role::Definition)) - continue; - if (std::optional info = - GetSymbolInfo(db, sym, false)) { - if ((sym.kind == SymbolKind::Type && - Ignore(db->GetType(sym).AnyDef())) || - (sym.kind == SymbolKind::Var && - Ignore(db->GetVar(sym).AnyDef()))) - continue; - if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { - info->location = *loc; - result.push_back(*info); - } - } - } - pipeline::Reply(request->id, result); - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentSymbol); } // namespace + +void MessageHandler::textDocument_documentSymbol(Reader &reader, + ReplyOnce &reply) { + DocumentSymbolParam param; + Reflect(reader, param); + + int file_id; + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!wfile) + return; + + if (param.startLine >= 0) { + std::vector result; + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && (param.all || sym.extent.Valid()) && + param.startLine <= sym.range.start.line && + sym.range.start.line <= param.endLine) + if (auto loc = GetLsLocation(db, working_files, sym, file_id)) + result.push_back(loc->range); + std::sort(result.begin(), result.end()); + reply(result); + } else if (g_config->client.hierarchicalDocumentSymbolSupport) { + std::unordered_map> sym2ds; + std::vector, lsDocumentSymbol *>> funcs, + types; + for (auto [sym, refcnt] : file->symbol2refcnt) { + if (refcnt <= 0 || !sym.extent.Valid()) + continue; + auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind}); + if (!r.second) + continue; + auto &ds = r.first->second; + ds = std::make_unique(); + std::vector def_ptrs; + WithEntity(db, sym, [&, sym = sym](const auto &entity) { + auto *def = entity.AnyDef(); + if (!def) + return; + ds->name = def->Name(false); + ds->detail = def->Name(true); + if (auto ls_range = GetLsRange(wfile, sym.range)) { + ds->selectionRange = *ls_range; + ds->range = ds->selectionRange; + if (sym.extent.Valid()) + if (auto ls_range1 = GetLsRange(wfile, sym.extent)) + ds->range = *ls_range1; + } + + for (auto &def : entity.def) + if (def.file_id == file_id && !Ignore(&def)) { + ds->kind = def.kind; + if (def.spell || def.kind == lsSymbolKind::Namespace) + def_ptrs.push_back(&def); + } + }); + if (def_ptrs.empty() || !(param.all || sym.role & Role::Definition || + ds->kind == lsSymbolKind::Namespace)) { + ds.reset(); + continue; + } + if (sym.kind == SymbolKind::Func) + funcs.emplace_back(std::move(def_ptrs), ds.get()); + else if (sym.kind == SymbolKind::Type) + types.emplace_back(std::move(def_ptrs), ds.get()); + } + + for (auto &[def_ptrs, ds] : funcs) + for (const void *def_ptr : def_ptrs) + for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) { + auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var}); + if (it != sym2ds.end() && it->second) + ds->children.push_back(std::move(it->second)); + } + for (auto &[def_ptrs, ds] : types) + for (const void *def_ptr : def_ptrs) { + auto *def = (const QueryType::Def *)def_ptr; + for (Usr usr1 : def->funcs) { + auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Func}); + if (it != sym2ds.end() && it->second) + ds->children.push_back(std::move(it->second)); + } + for (Usr usr1 : def->types) { + auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Type}); + if (it != sym2ds.end() && it->second) + ds->children.push_back(std::move(it->second)); + } + for (auto [usr1, _] : def->vars) { + auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var}); + if (it != sym2ds.end() && it->second) + ds->children.push_back(std::move(it->second)); + } + } + std::vector> result; + for (auto &[_, ds] : sym2ds) + if (ds) + result.push_back(std::move(ds)); + reply(result); + } else { + std::vector result; + for (auto [sym, refcnt] : file->symbol2refcnt) { + if (refcnt <= 0 || !sym.extent.Valid() || + !(param.all || sym.role & Role::Definition)) + continue; + if (std::optional info = + GetSymbolInfo(db, sym, false)) { + if ((sym.kind == SymbolKind::Type && + Ignore(db->GetType(sym).AnyDef())) || + (sym.kind == SymbolKind::Var && Ignore(db->GetVar(sym).AnyDef()))) + continue; + if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { + info->location = *loc; + result.push_back(*info); + } + } + } + reply(result); + } +} } // namespace ccls diff --git a/src/messages/textDocument_foldingRange.cc b/src/messages/textDocument_foldingRange.cc index ea86ba27..7777226f 100644 --- a/src/messages/textDocument_foldingRange.cc +++ b/src/messages/textDocument_foldingRange.cc @@ -13,61 +13,43 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" -#include "project.h" +#include "project.hh" #include "query_utils.h" #include "working_files.h" namespace ccls { namespace { -MethodType foldingRange = "textDocument/foldingRange"; - -struct In_textDocumentFoldingRange : public RequestMessage { - MethodType GetMethodType() const override { return foldingRange; } - struct Params { - lsTextDocumentIdentifier textDocument; - } params; -}; -MAKE_REFLECT_STRUCT(In_textDocumentFoldingRange::Params, textDocument); -MAKE_REFLECT_STRUCT(In_textDocumentFoldingRange, id, params); -REGISTER_IN_MESSAGE(In_textDocumentFoldingRange); - struct FoldingRange { int startLine, startCharacter, endLine, endCharacter; std::string kind = "region"; }; -MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, kind); - -struct Handler_textDocumentFoldingRange - : BaseMessageHandler { - MethodType GetMethodType() const override { return foldingRange; } - void Run(In_textDocumentFoldingRange *request) override { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file, - nullptr)) - return; - WorkingFile *wfile = - working_files->GetFileByFilename(file->def->path); - if (!wfile) - return; - std::vector result; - std::optional ls_range; - - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && sym.extent.Valid() && - (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) && - (ls_range = GetLsRange(wfile, sym.extent))) { - FoldingRange &fold = result.emplace_back(); - fold.startLine = ls_range->start.line; - fold.startCharacter = ls_range->start.character; - fold.endLine = ls_range->end.line; - fold.endCharacter = ls_range->end.character; - } - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_textDocumentFoldingRange); +MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, + endCharacter, kind); } // namespace + +void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m, + ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!wfile) + return; + std::vector result; + std::optional ls_range; + + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && sym.extent.Valid() && + (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) && + (ls_range = GetLsRange(wfile, sym.extent))) { + FoldingRange &fold = result.emplace_back(); + fold.startLine = ls_range->start.line; + fold.startCharacter = ls_range->start.character; + fold.endLine = ls_range->end.line; + fold.endCharacter = ls_range->end.character; + } + reply(result); +} } // namespace ccls diff --git a/src/messages/textDocument_formatting.cc b/src/messages/textDocument_formatting.cc index a6444c58..91d84e6e 100644 --- a/src/messages/textDocument_formatting.cc +++ b/src/messages/textDocument_formatting.cc @@ -13,29 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "working_files.h" #include #include -using namespace ccls; +namespace ccls { using namespace clang; namespace { -const MethodType formatting = "textDocument/formatting", - onTypeFormatting = "textDocument/onTypeFormatting", - rangeFormatting = "textDocument/rangeFormatting"; - -struct lsFormattingOptions { - // Size of a tab in spaces. - int tabSize; - // Prefer spaces over tabs. - bool insertSpaces; -}; -MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces); - llvm::Expected FormatCode(std::string_view code, std::string_view file, tooling::Range Range) { StringRef Code(code.data(), code.size()), File(file.data(), file.size()); @@ -79,116 +67,59 @@ ReplacementsToEdits(std::string_view code, const tooling::Replacements &Repls) { return ret; } -void Format(WorkingFile *wfile, tooling::Range range, lsRequestId id) { +void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) { std::string_view code = wfile->buffer_content; - auto ReplsOrErr = - FormatCode(code, wfile->filename, range); + auto ReplsOrErr = FormatCode(code, wfile->filename, range); if (ReplsOrErr) { auto result = ReplacementsToEdits(code, *ReplsOrErr); - pipeline::Reply(id, result); + reply(result); } else { lsResponseError err; err.code = lsErrorCodes::UnknownErrorCode; err.message = llvm::toString(ReplsOrErr.takeError()); - pipeline::ReplyError(id, err); + reply.Error(err); } } - -struct In_TextDocumentFormatting : public RequestMessage { - MethodType GetMethodType() const override { return formatting; } - struct Params { - lsTextDocumentIdentifier textDocument; - lsFormattingOptions options; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentFormatting::Params, textDocument, options); -MAKE_REFLECT_STRUCT(In_TextDocumentFormatting, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentFormatting); - -struct Handler_TextDocumentFormatting - : BaseMessageHandler { - MethodType GetMethodType() const override { return formatting; } - void Run(In_TextDocumentFormatting *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - if (!wfile) - return; - Format(wfile, {0, (unsigned)wfile->buffer_content.size()}, request->id); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting); - -struct In_TextDocumentOnTypeFormatting : public RequestMessage { - MethodType GetMethodType() const override { return onTypeFormatting; } - struct Params { - lsTextDocumentIdentifier textDocument; - lsPosition position; - std::string ch; - lsFormattingOptions options; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentOnTypeFormatting::Params, textDocument, - position, ch, options); -MAKE_REFLECT_STRUCT(In_TextDocumentOnTypeFormatting, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentOnTypeFormatting); - -struct Handler_TextDocumentOnTypeFormatting - : BaseMessageHandler { - MethodType GetMethodType() const override { return onTypeFormatting; } - void Run(In_TextDocumentOnTypeFormatting *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - if (!wfile) - return; - std::string_view code = wfile->buffer_content; - int pos = GetOffsetForPosition(params.position, code); - auto lbrace = code.find_last_of('{', pos); - if (lbrace == std::string::npos) - lbrace = pos; - Format(wfile, {(unsigned)lbrace, unsigned(pos - lbrace)}, request->id); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting); - -struct In_TextDocumentRangeFormatting : public RequestMessage { - MethodType GetMethodType() const override { return rangeFormatting; } - struct Params { - lsTextDocumentIdentifier textDocument; - lsRange range; - lsFormattingOptions options; - } params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting::Params, textDocument, range, - options); -MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentRangeFormatting); - -struct Handler_TextDocumentRangeFormatting - : BaseMessageHandler { - MethodType GetMethodType() const override { return rangeFormatting; } - - void Run(In_TextDocumentRangeFormatting *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - if (!wfile) - return; - std::string_view code = wfile->buffer_content; - int begin = GetOffsetForPosition(params.range.start, code), - end = GetOffsetForPosition(params.range.end, code); - Format(wfile, {(unsigned)begin, unsigned(end - begin)}, request->id); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRangeFormatting); } // namespace + +void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m, + ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!wfile) + return; + Format(reply, wfile, {0, (unsigned)wfile->buffer_content.size()}); +} + +void MessageHandler::textDocument_onTypeFormatting( + DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!wfile) + return; + std::string_view code = wfile->buffer_content; + int pos = GetOffsetForPosition(param.position, code); + auto lbrace = code.find_last_of('{', pos); + if (lbrace == std::string::npos) + lbrace = pos; + Format(reply, wfile, {(unsigned)lbrace, unsigned(pos - lbrace)}); +} + +void MessageHandler::textDocument_rangeFormatting( + DocumentRangeFormattingParam ¶m, ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!wfile) + return; + std::string_view code = wfile->buffer_content; + int begin = GetOffsetForPosition(param.range.start, code), + end = GetOffsetForPosition(param.range.end, code); + Format(reply, wfile, {(unsigned)begin, unsigned(end - begin)}); +} +} // namespace ccls diff --git a/src/messages/textDocument_hover.cc b/src/messages/textDocument_hover.cc index 6d95f88e..6c2df0a7 100644 --- a/src/messages/textDocument_hover.cc +++ b/src/messages/textDocument_hover.cc @@ -1,13 +1,33 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" -#include "pipeline.hh" +#include "message_handler.hh" #include "query_utils.h" -using namespace ccls; +namespace ccls { namespace { -MethodType kMethodType = "textDocument/hover"; +struct lsMarkedString { + std::optional language; + std::string value; +}; +struct Hover { + std::vector contents; + std::optional range; +}; + +void Reflect(Writer &visitor, lsMarkedString &value) { + // If there is a language, emit a `{language:string, value:string}` object. If + // not, emit a string. + if (value.language) { + REFLECT_MEMBER_START(); + REFLECT_MEMBER(language); + REFLECT_MEMBER(value); + REFLECT_MEMBER_END(); + } else { + Reflect(visitor, value.value); + } +} +MAKE_REFLECT_STRUCT(Hover, contents, range); const char *LanguageIdentifier(LanguageId lang) { switch (lang) { @@ -57,51 +77,34 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) { }); return {hover, ls_comments}; } - -struct In_TextDocumentHover : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentHover, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentHover); - -struct lsHover { - std::vector contents; - std::optional range; -}; -MAKE_REFLECT_STRUCT(lsHover, contents, range); - -struct Handler_TextDocumentHover : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_TextDocumentHover *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - lsHover result; - - for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { - std::optional ls_range = GetLsRange( - working_files->GetFileByFilename(file->def->path), sym.range); - if (!ls_range) - continue; - - auto [hover, comments] = GetHover(db, file->def->language, sym, file->id); - if (comments || hover) { - result.range = *ls_range; - if (comments) - result.contents.push_back(*comments); - if (hover) - result.contents.push_back(*hover); - break; - } - } - - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentHover); } // namespace + +void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m, + ReplyOnce &reply) { + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + Hover result; + + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { + std::optional ls_range = GetLsRange( + working_files->GetFileByFilename(file->def->path), sym.range); + if (!ls_range) + continue; + + auto [hover, comments] = GetHover(db, file->def->language, sym, file->id); + if (comments || hover) { + result.range = *ls_range; + if (comments) + result.contents.push_back(*comments); + if (hover) + result.contents.push_back(*hover); + break; + } + } + + reply(result); +} +} // namespace ccls diff --git a/src/messages/textDocument_references.cc b/src/messages/textDocument_references.cc index 795dc605..2a9f4e26 100644 --- a/src/messages/textDocument_references.cc +++ b/src/messages/textDocument_references.cc @@ -1,20 +1,15 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" -#include "pipeline.hh" +#include "message_handler.hh" #include "query_utils.h" #include -using namespace ccls; - +namespace ccls { namespace { -MethodType kMethodType = "textDocument/references"; - -struct In_TextDocumentReferences : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct lsReferenceContext { +struct ReferenceParam : public TextDocumentPositionParam { + struct Context { bool base = true; // Exclude references with any |Role| bits set. Role excludeRole = Role::None; @@ -22,116 +17,99 @@ struct In_TextDocumentReferences : public RequestMessage { bool includeDeclaration = false; // Include references with all |Role| bits set. Role role = Role::None; - }; - struct Params { - lsTextDocumentIdentifier textDocument; - lsPosition position; - lsReferenceContext context; - }; - - Params params; + } context; }; -MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext, base, - excludeRole, includeDeclaration, role); -MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params, textDocument, position, - context); -MAKE_REFLECT_STRUCT(In_TextDocumentReferences, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentReferences); +MAKE_REFLECT_STRUCT(ReferenceParam::Context, base, excludeRole, + includeDeclaration, role); +MAKE_REFLECT_STRUCT(ReferenceParam, textDocument, position, context); +} // namespace -struct Handler_TextDocumentReferences - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } +void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) { + ReferenceParam param; + Reflect(reader, param); + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); + if (!file) + return; + std::vector result; + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + if (!file) + return; - void Run(In_TextDocumentReferences *request) override { - auto ¶ms = request->params; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - params.textDocument.uri.GetPath(), &file)) - return; - std::vector result; - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - if (!file) { - pipeline::Reply(request->id, result); - return; - } + std::unordered_set seen_uses; + int line = param.position.line; - std::unordered_set seen_uses; - int line = params.position.line; - - for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { - // Found symbol. Return references. - std::unordered_set seen; - seen.insert(sym.usr); - std::vector stack{sym.usr}; - if (sym.kind != SymbolKind::Func) - params.context.base = false; - while (stack.size()) { - sym.usr = stack.back(); - stack.pop_back(); - auto fn = [&](Use use, lsSymbolKind parent_kind) { - if (Role(use.role & params.context.role) == params.context.role && - !(use.role & params.context.excludeRole) && - seen_uses.insert(use).second) - if (auto loc = GetLsLocation(db, working_files, use)) { - result.push_back(*loc); - } - }; - WithEntity(db, sym, [&](const auto &entity) { - lsSymbolKind parent_kind = lsSymbolKind::Unknown; + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { + // Found symbol. Return references. + std::unordered_set seen; + seen.insert(sym.usr); + std::vector stack{sym.usr}; + if (sym.kind != SymbolKind::Func) + param.context.base = false; + while (stack.size()) { + sym.usr = stack.back(); + stack.pop_back(); + auto fn = [&](Use use, lsSymbolKind parent_kind) { + if (Role(use.role & param.context.role) == param.context.role && + !(use.role & param.context.excludeRole) && + seen_uses.insert(use).second) + if (auto loc = GetLsLocation(db, working_files, use)) { + result.push_back(*loc); + } + }; + WithEntity(db, sym, [&](const auto &entity) { + lsSymbolKind parent_kind = lsSymbolKind::Unknown; + for (auto &def : entity.def) + if (def.spell) { + parent_kind = GetSymbolKind(db, sym); + if (param.context.base) + for (Usr usr : def.GetBases()) + if (!seen.count(usr)) { + seen.insert(usr); + stack.push_back(usr); + } + break; + } + for (Use use : entity.uses) + fn(use, parent_kind); + if (param.context.includeDeclaration) { for (auto &def : entity.def) - if (def.spell) { - parent_kind = GetSymbolKind(db, sym); - if (params.context.base) - for (Usr usr : def.GetBases()) - if (!seen.count(usr)) { - seen.insert(usr); - stack.push_back(usr); - } + if (def.spell) + fn(*def.spell, parent_kind); + for (Use use : entity.declarations) + fn(use, parent_kind); + } + }); + } + break; + } + + if (result.empty()) { + // |path| is the #include line. If the cursor is not on such line but line + // = 0, + // use the current filename. + std::string path; + if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1) + path = file->def->path; + for (const IndexInclude &include : file->def->includes) + if (include.line == param.position.line) { + path = include.resolved_path; + break; + } + if (path.size()) + for (QueryFile &file1 : db->files) + if (file1.def) + for (const IndexInclude &include : file1.def->includes) + if (include.resolved_path == path) { + // Another file |file1| has the same include line. + lsLocation &loc = result.emplace_back(); + loc.uri = lsDocumentUri::FromPath(file1.def->path); + loc.range.start.line = loc.range.end.line = include.line; break; } - for (Use use : entity.uses) - fn(use, parent_kind); - if (params.context.includeDeclaration) { - for (auto &def : entity.def) - if (def.spell) - fn(*def.spell, parent_kind); - for (Use use : entity.declarations) - fn(use, parent_kind); - } - }); - } - break; - } - - if (result.empty()) { - // |path| is the #include line. If the cursor is not on such line but line - // = 0, - // use the current filename. - std::string path; - if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1) - path = file->def->path; - for (const IndexInclude &include : file->def->includes) - if (include.line == params.position.line) { - path = include.resolved_path; - break; - } - if (path.size()) - for (QueryFile &file1 : db->files) - if (file1.def) - for (const IndexInclude &include : file1.def->includes) - if (include.resolved_path == path) { - // Another file |file1| has the same include line. - lsLocation &loc = result.emplace_back(); - loc.uri = lsDocumentUri::FromPath(file1.def->path); - loc.range.start.line = loc.range.end.line = include.line; - break; - } - } - - if ((int)result.size() >= g_config->xref.maxNum) - result.resize(g_config->xref.maxNum); - pipeline::Reply(request->id, result); } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentReferences); -} // namespace + + if ((int)result.size() >= g_config->xref.maxNum) + result.resize(g_config->xref.maxNum); + reply(result); +} +} // namespace ccls diff --git a/src/messages/textDocument_rename.cc b/src/messages/textDocument_rename.cc index 02a17b01..1d21c96e 100644 --- a/src/messages/textDocument_rename.cc +++ b/src/messages/textDocument_rename.cc @@ -1,14 +1,11 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "message_handler.h" -#include "pipeline.hh" +#include "message_handler.hh" #include "query_utils.h" -using namespace ccls; +namespace ccls { namespace { -MethodType kMethodType = "textDocument/rename"; - lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files, SymbolRef sym, const std::string &new_text) { std::unordered_map path_to_edit; @@ -50,49 +47,21 @@ lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files, edit.documentChanges.push_back(changes.second); return edit; } - -struct In_TextDocumentRename : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - // The document to format. - lsTextDocumentIdentifier textDocument; - - // The position at which this request was sent. - lsPosition position; - - // The new name of the symbol. If the given name is not valid the - // request must return a [ResponseError](#ResponseError) with an - // appropriate message set. - std::string newName; - }; - Params params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentRename::Params, textDocument, position, - newName); -MAKE_REFLECT_STRUCT(In_TextDocumentRename, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentRename); - -struct Handler_TextDocumentRename : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_TextDocumentRename *request) override { - int file_id; - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file, - &file_id)) - return; - - WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); - lsWorkspaceEdit result; - for (SymbolRef sym : - FindSymbolsAtLocation(wfile, file, request->params.position)) { - result = - BuildWorkspaceEdit(db, working_files, sym, request->params.newName); - break; - } - - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRename); } // namespace + +void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) { + int file_id; + QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id); + if (!file) + return; + + WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); + lsWorkspaceEdit result; + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) { + result = BuildWorkspaceEdit(db, working_files, sym, param.newName); + break; + } + + reply(result); +} +} // namespace ccls diff --git a/src/messages/textDocument_signatureHelp.cc b/src/messages/textDocument_signatureHelp.cc index ee8bef91..c58e42f8 100644 --- a/src/messages/textDocument_signatureHelp.cc +++ b/src/messages/textDocument_signatureHelp.cc @@ -2,56 +2,35 @@ // SPDX-License-Identifier: Apache-2.0 #include "clang_complete.hh" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include -using namespace ccls; +namespace ccls { using namespace clang; namespace { -MethodType kMethodType = "textDocument/signatureHelp"; - -// Represents a parameter of a callable-signature. A parameter can -// have a label and a doc-comment. -struct lsParameterInformation { +struct ParameterInformation { std::string label; - // Not available in clang - // std::optional documentation; }; -MAKE_REFLECT_STRUCT(lsParameterInformation, label); - -// Represents the signature of something callable. A signature -// can have a label, like a function-name, a doc-comment, and -// a set of parameters. -struct lsSignatureInformation { +struct SignatureInformation { std::string label; std::optional documentation; - std::vector parameters; + std::vector parameters; }; -MAKE_REFLECT_STRUCT(lsSignatureInformation, label, documentation, parameters); - -// Signature help represents the signature of something -// callable. There can be multiple signature but only one -// active and only one active parameter. struct lsSignatureHelp { - std::vector signatures; + std::vector signatures; int activeSignature = 0; int activeParameter = 0; }; +MAKE_REFLECT_STRUCT(ParameterInformation, label); +MAKE_REFLECT_STRUCT(SignatureInformation, label, documentation, parameters); MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature, activeParameter); -struct In_TextDocumentSignatureHelp : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentSignatureHelp, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentSignatureHelp); - std::string BuildOptional(const CodeCompletionString &CCS, - std::vector &ls_params) { + std::vector &ls_params) { std::string ret; for (const auto &Chunk : CCS) { switch (Chunk.Kind) { @@ -67,7 +46,7 @@ std::string BuildOptional(const CodeCompletionString &CCS, // the code-completion location within a function call, message send, // macro invocation, etc. ret += Chunk.Text; - ls_params.push_back(lsParameterInformation{Chunk.Text}); + ls_params.push_back({Chunk.Text}); break; } case CodeCompletionString::CK_VerticalSpace: @@ -113,7 +92,7 @@ public: Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true); const char *ret_type = nullptr; - lsSignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back(); + SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back(); #if LLVM_VERSION_MAJOR >= 8 const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction()); ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : ""; @@ -126,7 +105,7 @@ public: case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_CurrentParameter: { ls_sig.label += Chunk.Text; - ls_sig.parameters.push_back(lsParameterInformation{Chunk.Text}); + ls_sig.parameters.push_back({Chunk.Text}); break; } case CodeCompletionString::CK_Optional: @@ -145,7 +124,7 @@ public: } std::sort( ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(), - [](const lsSignatureInformation &l, const lsSignatureInformation &r) { + [](const SignatureInformation &l, const SignatureInformation &r) { if (l.parameters.size() != r.parameters.size()) return l.parameters.size() < r.parameters.size(); if (l.label.size() != r.label.size()) @@ -157,55 +136,50 @@ public: CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } }; - -struct Handler_TextDocumentSignatureHelp - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - - void Run(In_TextDocumentSignatureHelp *request) override { - static CompleteConsumerCache cache; - - const auto ¶ms = request->params; - std::string path = params.textDocument.uri.GetPath(); - lsPosition begin_pos = params.position; - if (WorkingFile *file = working_files->GetFileByFilename(path)) { - std::string completion_text; - lsPosition end_pos = params.position; - begin_pos = file->FindStableCompletionSource( - request->params.position, &completion_text, &end_pos); - } - - CompletionManager::OnComplete callback = - [id = request->id, path, begin_pos](CodeCompleteConsumer *OptConsumer) { - if (!OptConsumer) - return; - auto *Consumer = static_cast(OptConsumer); - pipeline::Reply(id, Consumer->ls_sighelp); - if (!Consumer->from_cache) { - cache.WithLock([&]() { - cache.path = path; - cache.position = begin_pos; - cache.result = Consumer->ls_sighelp; - }); - } - }; - - CodeCompleteOptions CCOpts; - CCOpts.IncludeGlobals = false; - CCOpts.IncludeMacros = false; - CCOpts.IncludeBriefComments = false; - if (cache.IsCacheValid(path, begin_pos)) { - SignatureHelpConsumer Consumer(CCOpts, true); - cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; }); - callback(&Consumer); - } else { - clang_complete->completion_request_.PushBack( - std::make_unique( - request->id, params.textDocument, params.position, - std::make_unique(CCOpts, false), CCOpts, - callback)); - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentSignatureHelp); } // namespace + +void MessageHandler::textDocument_signatureHelp( + TextDocumentPositionParam ¶m, ReplyOnce &reply) { + static CompleteConsumerCache cache; + + std::string path = param.textDocument.uri.GetPath(); + lsPosition begin_pos = param.position; + if (WorkingFile *file = working_files->GetFileByFilename(path)) { + std::string completion_text; + lsPosition end_pos = param.position; + begin_pos = file->FindStableCompletionSource(param.position, + &completion_text, &end_pos); + } + + CompletionManager::OnComplete callback = + [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) { + if (!OptConsumer) + return; + auto *Consumer = static_cast(OptConsumer); + reply(Consumer->ls_sighelp); + if (!Consumer->from_cache) { + cache.WithLock([&]() { + cache.path = path; + cache.position = begin_pos; + cache.result = Consumer->ls_sighelp; + }); + } + }; + + CodeCompleteOptions CCOpts; + CCOpts.IncludeGlobals = false; + CCOpts.IncludeMacros = false; + CCOpts.IncludeBriefComments = false; + if (cache.IsCacheValid(path, begin_pos)) { + SignatureHelpConsumer Consumer(CCOpts, true); + cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; }); + callback(&Consumer); + } else { + clang_complete->completion_request_.PushBack( + std::make_unique( + reply.id, param.textDocument, param.position, + std::make_unique(CCOpts, false), CCOpts, + callback)); + } +} +} // namespace ccls diff --git a/src/messages/textDocument_typeDefinition.cc b/src/messages/textDocument_typeDefinition.cc deleted file mode 100644 index 74cd6995..00000000 --- a/src/messages/textDocument_typeDefinition.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "message_handler.h" -#include "pipeline.hh" -#include "query_utils.h" -using namespace ccls; - -namespace { -MethodType kMethodType = "textDocument/typeDefinition"; - -struct In_TextDocumentTypeDefinition : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - lsTextDocumentPositionParams params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentTypeDefinition, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentTypeDefinition); - -struct Handler_TextDocumentTypeDefinition - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_TextDocumentTypeDefinition *request) override { - QueryFile *file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file, - nullptr)) - return; - WorkingFile *working_file = - working_files->GetFileByFilename(file->def->path); - - std::vector result; - auto Add = [&](const QueryType &type) { - for (const auto &def : type.def) - if (def.spell) { - if (auto ls_loc = GetLsLocation(db, working_files, *def.spell)) - result.push_back(*ls_loc); - } - if (result.empty()) - for (const DeclRef &dr : type.declarations) - if (auto ls_loc = GetLsLocation(db, working_files, dr)) - result.push_back(*ls_loc); - }; - for (SymbolRef sym : - FindSymbolsAtLocation(working_file, file, request->params.position)) { - switch (sym.kind) { - case SymbolKind::Var: { - const QueryVar::Def *def = db->GetVar(sym).AnyDef(); - if (def && def->type) - Add(db->Type(def->type)); - break; - } - case SymbolKind::Type: { - for (auto &def : db->GetType(sym).def) - if (def.alias_of) { - Add(db->Type(def.alias_of)); - break; - } - break; - } - default: - break; - } - } - - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentTypeDefinition); - -} // namespace diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc new file mode 100644 index 00000000..b0dc591b --- /dev/null +++ b/src/messages/workspace.cc @@ -0,0 +1,189 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "clang_complete.hh" +#include "fuzzy_match.h" +#include "log.hh" +#include "message_handler.hh" +#include "pipeline.hh" +#include "project.hh" +#include "query_utils.h" + +#include + +#include +#include +#include +#include + +namespace ccls { +MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName); + +void MessageHandler::workspace_didChangeConfiguration(EmptyParam &) { + for (const std::string &folder : g_config->workspaceFolders) + project->Load(folder); + project->Index(working_files, lsRequestId()); + + clang_complete->FlushAllSessions(); +}; + +void MessageHandler::workspace_didChangeWatchedFiles( + DidChangeWatchedFilesParam ¶m) { + for (auto &event : param.changes) { + std::string path = event.uri.GetPath(); + IndexMode mode = working_files->GetFileByFilename(path) + ? IndexMode::Normal + : IndexMode::NonInteractive; + switch (event.type) { + case FileChangeType::Created: + case FileChangeType::Changed: { + pipeline::Index(path, {}, mode); + if (mode == IndexMode::Normal) + clang_complete->NotifySave(path); + else + clang_complete->OnClose(path); + break; + } + case FileChangeType::Deleted: + pipeline::Index(path, {}, mode); + clang_complete->OnClose(path); + break; + } + } +} + +void MessageHandler::workspace_didChangeWorkspaceFolders( + DidChangeWorkspaceFoldersParam ¶m) { + for (const WorkspaceFolder &wf : param.event.removed) { + std::string root = wf.uri.GetPath(); + EnsureEndsInSlash(root); + LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root; + auto it = llvm::find(g_config->workspaceFolders, root); + if (it != g_config->workspaceFolders.end()) { + g_config->workspaceFolders.erase(it); + { + // auto &folder = project->root2folder[path]; + // FIXME delete + } + project->root2folder.erase(root); + } + } + for (const WorkspaceFolder &wf : param.event.added) { + std::string root = wf.uri.GetPath(); + EnsureEndsInSlash(root); + LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root; + g_config->workspaceFolders.push_back(root); + project->Load(root); + } + + project->Index(working_files, lsRequestId()); + + clang_complete->FlushAllSessions(); +} + +namespace { +// Lookup |symbol| in |db| and insert the value into |result|. +bool AddSymbol( + DB *db, WorkingFiles *working_files, SymbolIdx sym, bool use_detailed, + std::vector> *result) { + std::optional info = GetSymbolInfo(db, sym, true); + if (!info) + return false; + + Use loc; + if (Maybe dr = GetDefinitionSpell(db, sym)) + loc = *dr; + else { + auto decls = GetNonDefDeclarations(db, sym); + if (decls.empty()) + return false; + loc = decls[0]; + } + + std::optional ls_location = GetLsLocation(db, working_files, loc); + if (!ls_location) + return false; + info->location = *ls_location; + result->emplace_back(*info, int(use_detailed), sym); + return true; +} +} // namespace + +void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m, + ReplyOnce &reply) { + std::vector result; + std::string query = param.query; + + // {symbol info, matching detailed_name or short_name, index} + std::vector> cands; + bool sensitive = g_config->workspaceSymbol.caseSensitivity; + + // Find subsequence matches. + std::string query_without_space; + query_without_space.reserve(query.size()); + for (char c : query) + if (!isspace(c)) + query_without_space += c; + + auto Add = [&](SymbolIdx sym) { + std::string_view detailed_name = db->GetSymbolName(sym, true); + int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive); + return pos >= 0 && + AddSymbol(db, working_files, sym, + detailed_name.find(':', pos) != std::string::npos, + &cands) && + cands.size() >= g_config->workspaceSymbol.maxNum; + }; + for (auto &func : db->funcs) + if (Add({func.usr, SymbolKind::Func})) + goto done_add; + for (auto &type : db->types) + if (Add({type.usr, SymbolKind::Type})) + goto done_add; + for (auto &var : db->vars) + if (var.def.size() && !var.def[0].is_local() && + Add({var.usr, SymbolKind::Var})) + goto done_add; +done_add: + + if (g_config->workspaceSymbol.sort && query.size() <= FuzzyMatcher::kMaxPat) { + // Sort results with a fuzzy matching algorithm. + int longest = 0; + for (auto &cand : cands) + longest = std::max( + longest, int(db->GetSymbolName(std::get<2>(cand), true).size())); + FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity); + for (auto &cand : cands) + std::get<1>(cand) = + fuzzy.Match(db->GetSymbolName(std::get<2>(cand), std::get<1>(cand))); + std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) { + return std::get<1>(l) > std::get<1>(r); + }); + result.reserve(cands.size()); + for (auto &cand : cands) { + // Discard awful candidates. + if (std::get<1>(cand) <= FuzzyMatcher::kMinScore) + break; + result.push_back(std::get<0>(cand)); + } + } else { + result.reserve(cands.size()); + for (auto &cand : cands) + result.push_back(std::get<0>(cand)); + } + + reply(result); +} +} // namespace ccls diff --git a/src/messages/workspace_did.cc b/src/messages/workspace_did.cc deleted file mode 100644 index 46ae4cb5..00000000 --- a/src/messages/workspace_did.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* Copyright 2017-2018 ccls Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -#include "clang_complete.hh" -#include "log.hh" -#include "message_handler.h" -#include "pipeline.hh" -#include "project.h" -#include "working_files.h" - -#include -using namespace ccls; - -namespace { -MethodType didChangeConfiguration = "workspace/didChangeConfiguration", - didChangeWatchedFiles = "workspace/didChangeWatchedFiles", - didChangeWorkspaceFolders = "workspace/didChangeWorkspaceFolders"; - -struct lsDidChangeConfigurationParams { - bool placeholder; -}; -MAKE_REFLECT_STRUCT(lsDidChangeConfigurationParams, placeholder); - -struct In_workspaceDidChangeConfiguration : public NotificationMessage { - MethodType GetMethodType() const override { return didChangeConfiguration; } - lsDidChangeConfigurationParams params; -}; -MAKE_REFLECT_STRUCT(In_workspaceDidChangeConfiguration, params); -REGISTER_IN_MESSAGE(In_workspaceDidChangeConfiguration); - -struct Handler_workspaceDidChangeConfiguration - : BaseMessageHandler { - MethodType GetMethodType() const override { return didChangeConfiguration; } - void Run(In_workspaceDidChangeConfiguration *request) override { - for (const std::string &folder : g_config->workspaceFolders) - project->Load(folder); - - project->Index(working_files, lsRequestId()); - - clang_complete->FlushAllSessions(); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeConfiguration); - -enum class lsFileChangeType { - Created = 1, - Changed = 2, - Deleted = 3, -}; -MAKE_REFLECT_TYPE_PROXY(lsFileChangeType); - -struct lsFileEvent { - lsDocumentUri uri; - lsFileChangeType type; -}; -MAKE_REFLECT_STRUCT(lsFileEvent, uri, type); - -struct lsDidChangeWatchedFilesParams { - std::vector changes; -}; -MAKE_REFLECT_STRUCT(lsDidChangeWatchedFilesParams, changes); - -struct In_WorkspaceDidChangeWatchedFiles : public NotificationMessage { - MethodType GetMethodType() const override { return didChangeWatchedFiles; } - lsDidChangeWatchedFilesParams params; -}; -MAKE_REFLECT_STRUCT(In_WorkspaceDidChangeWatchedFiles, params); -REGISTER_IN_MESSAGE(In_WorkspaceDidChangeWatchedFiles); - -struct Handler_WorkspaceDidChangeWatchedFiles - : BaseMessageHandler { - MethodType GetMethodType() const override { return didChangeWatchedFiles; } - void Run(In_WorkspaceDidChangeWatchedFiles *request) override { - for (lsFileEvent &event : request->params.changes) { - std::string path = event.uri.GetPath(); - IndexMode mode = working_files->GetFileByFilename(path) - ? IndexMode::Normal - : IndexMode::NonInteractive; - switch (event.type) { - case lsFileChangeType::Created: - case lsFileChangeType::Changed: { - pipeline::Index(path, {}, mode); - if (mode == IndexMode::Normal) - clang_complete->NotifySave(path); - else - clang_complete->OnClose(path); - break; - } - case lsFileChangeType::Deleted: - pipeline::Index(path, {}, mode); - clang_complete->OnClose(path); - break; - } - } - } -}; -REGISTER_MESSAGE_HANDLER(Handler_WorkspaceDidChangeWatchedFiles); - -struct lsWorkspaceFoldersChangeEvent { - std::vector added, removed; -}; -MAKE_REFLECT_STRUCT(lsWorkspaceFoldersChangeEvent, added, removed); - -struct In_workspaceDidChangeWorkspaceFolders : public NotificationMessage { - MethodType GetMethodType() const override { - return didChangeWorkspaceFolders; - } - struct Params { - lsWorkspaceFoldersChangeEvent event; - } params; -}; -MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders::Params, event); -MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders, params); -REGISTER_IN_MESSAGE(In_workspaceDidChangeWorkspaceFolders); - -struct Handler_workspaceDidChangeWorkspaceFolders - : BaseMessageHandler { - MethodType GetMethodType() const override { - return didChangeWorkspaceFolders; - } - void Run(In_workspaceDidChangeWorkspaceFolders *request) override { - const auto &event = request->params.event; - for (const lsWorkspaceFolder &wf : event.removed) { - std::string root = wf.uri.GetPath(); - EnsureEndsInSlash(root); - LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root; - auto it = llvm::find(g_config->workspaceFolders, root); - if (it != g_config->workspaceFolders.end()) { - g_config->workspaceFolders.erase(it); - { - // auto &folder = project->root2folder[path]; - // FIXME delete - } - project->root2folder.erase(root); - } - } - for (const lsWorkspaceFolder &wf : event.added) { - std::string root = wf.uri.GetPath(); - EnsureEndsInSlash(root); - LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root; - g_config->workspaceFolders.push_back(root); - project->Load(root); - } - - project->Index(working_files, lsRequestId()); - - clang_complete->FlushAllSessions(); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeWorkspaceFolders); -} // namespace diff --git a/src/messages/workspace_symbol.cc b/src/messages/workspace_symbol.cc deleted file mode 100644 index 02f2b59a..00000000 --- a/src/messages/workspace_symbol.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "fuzzy_match.h" -#include "message_handler.h" -#include "pipeline.hh" -#include "query_utils.h" - -#include -#include -#include -#include - -using namespace ccls; - -namespace { -MethodType kMethodType = "workspace/symbol"; - -// Lookup |symbol| in |db| and insert the value into |result|. -bool AddSymbol( - DB *db, WorkingFiles *working_files, SymbolIdx sym, bool use_detailed, - std::vector> *result) { - std::optional info = GetSymbolInfo(db, sym, true); - if (!info) - return false; - - Use loc; - if (Maybe dr = GetDefinitionSpell(db, sym)) - loc = *dr; - else { - auto decls = GetNonDefDeclarations(db, sym); - if (decls.empty()) - return false; - loc = decls[0]; - } - - std::optional ls_location = GetLsLocation(db, working_files, loc); - if (!ls_location) - return false; - info->location = *ls_location; - result->emplace_back(*info, int(use_detailed), sym); - return true; -} - -struct In_WorkspaceSymbol : public RequestMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - std::string query; - }; - Params params; -}; -MAKE_REFLECT_STRUCT(In_WorkspaceSymbol::Params, query); -MAKE_REFLECT_STRUCT(In_WorkspaceSymbol, id, params); -REGISTER_IN_MESSAGE(In_WorkspaceSymbol); - - -struct Handler_WorkspaceSymbol : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_WorkspaceSymbol *request) override { - std::vector result; - std::string query = request->params.query; - - // {symbol info, matching detailed_name or short_name, index} - std::vector> cands; - bool sensitive = g_config->workspaceSymbol.caseSensitivity; - - // Find subsequence matches. - std::string query_without_space; - query_without_space.reserve(query.size()); - for (char c : query) - if (!isspace(c)) - query_without_space += c; - - auto Add = [&](SymbolIdx sym) { - std::string_view detailed_name = db->GetSymbolName(sym, true); - int pos = - ReverseSubseqMatch(query_without_space, detailed_name, sensitive); - return pos >= 0 && - AddSymbol(db, working_files, sym, - detailed_name.find(':', pos) != std::string::npos, - &cands) && - cands.size() >= g_config->workspaceSymbol.maxNum; - }; - for (auto &func : db->funcs) - if (Add({func.usr, SymbolKind::Func})) - goto done_add; - for (auto &type : db->types) - if (Add({type.usr, SymbolKind::Type})) - goto done_add; - for (auto &var : db->vars) - if (var.def.size() && !var.def[0].is_local() && - Add({var.usr, SymbolKind::Var})) - goto done_add; - done_add: - - if (g_config->workspaceSymbol.sort && - query.size() <= FuzzyMatcher::kMaxPat) { - // Sort results with a fuzzy matching algorithm. - int longest = 0; - for (auto &cand : cands) - longest = std::max( - longest, int(db->GetSymbolName(std::get<2>(cand), true).size())); - FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity); - for (auto &cand : cands) - std::get<1>(cand) = fuzzy.Match( - db->GetSymbolName(std::get<2>(cand), std::get<1>(cand))); - std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) { - return std::get<1>(l) > std::get<1>(r); - }); - result.reserve(cands.size()); - for (auto &cand : cands) { - // Discard awful candidates. - if (std::get<1>(cand) <= FuzzyMatcher::kMinScore) - break; - result.push_back(std::get<0>(cand)); - } - } else { - result.reserve(cands.size()); - for (auto &cand : cands) - result.push_back(std::get<0>(cand)); - } - - pipeline::Reply(request->id, result); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_WorkspaceSymbol); -} // namespace diff --git a/src/pipeline.cc b/src/pipeline.cc index fa714665..3a920374 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -7,15 +7,16 @@ #include "config.h" #include "include_complete.h" #include "log.hh" -#include "lsp.h" +#include "lsp.hh" #include "match.h" -#include "message_handler.h" +#include "message_handler.hh" #include "pipeline.hh" #include "platform.h" -#include "project.h" +#include "project.hh" #include "query_utils.h" #include "serializers/json.h" +#include #include #include @@ -31,9 +32,7 @@ using namespace llvm; #include #endif -void StandaloneInitialize(const std::string &, Project &, WorkingFiles &, VFS &, - IncludeComplete &); - +namespace ccls { void VFS::Clear() { std::lock_guard lock(mutex); state.clear(); @@ -55,7 +54,10 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) { return false; } -namespace ccls::pipeline { +struct MessageHandler; +void StandaloneInitialize(MessageHandler &, const std::string &root); + +namespace pipeline { std::atomic loaded_ts = ATOMIC_VAR_INIT(0), pending_index_requests = ATOMIC_VAR_INIT(0); @@ -74,7 +76,7 @@ struct Index_Request { MultiQueueWaiter *main_waiter; MultiQueueWaiter *indexer_waiter; MultiQueueWaiter *stdout_waiter; -ThreadedQueue> *on_request; +ThreadedQueue *on_request; ThreadedQueue *index_request; ThreadedQueue *on_indexed; ThreadedQueue *for_stdout; @@ -345,7 +347,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles, void Init() { main_waiter = new MultiQueueWaiter; - on_request = new ThreadedQueue>(main_waiter); + on_request = new ThreadedQueue(main_waiter); on_indexed = new ThreadedQueue(main_waiter); indexer_waiter = new MultiQueueWaiter; @@ -402,35 +404,53 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) { void LaunchStdin() { std::thread([]() { set_thread_name("stdin"); + std::string str; while (true) { - std::unique_ptr message; - std::optional error = - MessageRegistry::instance()->ReadMessageFromStdin(&message); - - // Message parsing can fail if we don't recognize the method. - if (error) { - // The message may be partially deserialized. - // Emit an error ResponseMessage if |id| is available. - if (message) { - lsRequestId id = message->GetRequestId(); - if (id.Valid()) { - lsResponseError err; - err.code = lsErrorCodes::InvalidParams; - err.message = std::move(*error); - ReplyError(id, err); - } + constexpr std::string_view kContentLength("Content-Length: "); + int len = 0; + str.clear(); + while (true) { + int c = getchar(); + if (c == EOF) + return; + if (c == '\n') { + if (str.empty()) + break; + if (!str.compare(0, kContentLength.size(), kContentLength)) + len = atoi(str.c_str() + kContentLength.size()); + str.clear(); + } else if (c != '\r') { + str += c; } - continue; } - // Cache |method_id| so we can access it after moving |message|. - MethodType method_type = message->GetMethodType(); + str.resize(len); + for (int i = 0; i < len; ++i) { + int c = getchar(); + if (c == EOF) + return; + str[i] = c; + } - on_request->PushBack(std::move(message)); + auto message = std::make_unique(len); + std::copy(str.begin(), str.end(), message.get()); + auto document = std::make_unique(); + document->Parse(message.get(), len); + assert(!document->HasParseError()); - // If the message was to exit then querydb will take care of the actual - // exit. Stop reading from stdin since it might be detached. - if (method_type == kMethodType_Exit) + JsonReader reader{document.get()}; + if (!reader.HasMember("jsonrpc") || + std::string(reader["jsonrpc"]->GetString()) != "2.0") + return; + lsRequestId id; + std::string method; + ReflectMember(reader, "id", id); + ReflectMember(reader, "method", method); + auto param = std::make_unique(); + on_request->PushBack( + {id, std::move(method), std::move(message), std::move(document)}); + + if (method == "exit") break; } }) @@ -479,31 +499,20 @@ void MainLoop() { DB db; // Setup shared references. - for (MessageHandler *handler : *MessageHandler::message_handlers) { - handler->db = &db; - handler->project = &project; - handler->vfs = &vfs; - handler->working_files = &working_files; - handler->clang_complete = &clang_complete; - handler->include_complete = &include_complete; - } + MessageHandler handler; + handler.db = &db; + handler.project = &project; + handler.vfs = &vfs; + handler.working_files = &working_files; + handler.clang_complete = &clang_complete; + handler.include_complete = &include_complete; bool has_indexed = false; while (true) { - std::vector> messages = on_request->DequeueAll(); + std::vector messages = on_request->DequeueAll(); bool did_work = messages.size(); - for (auto &message : messages) { - // TODO: Consider using std::unordered_map to lookup the handler - for (MessageHandler *handler : *MessageHandler::message_handlers) { - if (handler->GetMethodType() == message->GetMethodType()) { - handler->Run(std::move(message)); - break; - } - } - - if (message) - LOG_S(ERROR) << "No handler for " << message->GetMethodType(); - } + for (InMessage &message : messages) + handler.Run(message); bool indexed = false; for (int i = 20; i--;) { @@ -532,7 +541,14 @@ void Standalone(const std::string &root) { WorkingFiles wfiles; VFS vfs; IncludeComplete complete(&project); - StandaloneInitialize(root, project, wfiles, vfs, complete); + + MessageHandler handler; + handler.project = &project; + handler.working_files = &wfiles; + handler.vfs = &vfs; + handler.include_complete = &complete; + + StandaloneInitialize(handler, root); bool tty = sys::Process::StandardOutIsDisplayed(); if (tty) { @@ -622,5 +638,5 @@ void Reply(lsRequestId id, const std::function &fn) { void ReplyError(lsRequestId id, const std::function &fn) { Reply(id, "error", fn); } - -} // namespace ccls::pipeline +} // namespace pipeline +} // namespace ccls diff --git a/src/pipeline.hh b/src/pipeline.hh index a52bc1d6..3e3b1861 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -3,7 +3,7 @@ #pragma once -#include "lsp.h" +#include "lsp.hh" #include "query.h" #include @@ -12,9 +12,9 @@ #include #include +namespace ccls { struct CompletionManager; struct GroupMatch; -struct VFS; struct Project; struct WorkingFiles; @@ -32,7 +32,6 @@ struct VFS { bool Stamp(const std::string &path, int64_t ts, int step); }; -namespace ccls { enum class IndexMode { NonInteractive, OnChange, diff --git a/src/position.cc b/src/position.cc index b3fe9fb5..12748374 100644 --- a/src/position.cc +++ b/src/position.cc @@ -3,12 +3,13 @@ #include "position.h" -#include "serializer.h" +#include "serializer.hh" #include #include #include +namespace ccls { Position Position::FromString(const std::string &encoded) { char *p = const_cast(encoded.c_str()); int16_t line = int16_t(strtol(p, &p, 10)) - 1; @@ -100,3 +101,4 @@ void Reflect(Writer &visitor, Range &value) { Reflect(visitor, value.end.column); } } +} // namespace ccls diff --git a/src/position.h b/src/position.h index f9a6b5b6..a90d4c7a 100644 --- a/src/position.h +++ b/src/position.h @@ -9,6 +9,7 @@ #include #include +namespace ccls { struct Position { int16_t line = -1; int16_t column = -1; @@ -30,7 +31,6 @@ struct Position { } bool operator<=(const Position &o) const { return !(o < *this); } }; -MAKE_HASHABLE(Position, t.line, t.column); struct Range { Position start; @@ -52,20 +52,6 @@ struct Range { } }; -namespace std { -template <> struct hash { - std::size_t operator()(Range x) const { - union U { - Range range = {}; - uint64_t u64; - } u; - static_assert(sizeof(Range) == 8); - u.range = x; - return hash()(u.u64); - } -}; -} // namespace std - // Reflection class Reader; class Writer; @@ -73,3 +59,20 @@ void Reflect(Reader &visitor, Position &value); void Reflect(Writer &visitor, Position &value); void Reflect(Reader &visitor, Range &value); void Reflect(Writer &visitor, Range &value); +} // namespace ccls + +namespace std { +template <> struct hash { + std::size_t operator()(ccls::Range x) const { + union U { + ccls::Range range = {}; + uint64_t u64; + } u; + static_assert(sizeof(ccls::Range) == 8); + u.range = x; + return hash()(u.u64); + } +}; +} // namespace std + +MAKE_HASHABLE(ccls::Position, t.line, t.column); diff --git a/src/project.cc b/src/project.cc index c2b4390e..90315b88 100644 --- a/src/project.cc +++ b/src/project.cc @@ -1,7 +1,7 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "project.h" +#include "project.hh" #include "filesystem.hh" #include "log.hh" @@ -31,10 +31,10 @@ #include #include -using namespace ccls; using namespace clang; using namespace llvm; +namespace ccls { std::pair lookupExtension(std::string_view filename) { using namespace clang::driver; auto I = types::lookupTypeForExtension( @@ -473,3 +473,4 @@ void Project::Index(WorkingFiles *wfiles, lsRequestId id) { // trigger refreshing semantic highlight for all working files. pipeline::Index("", {}, IndexMode::NonInteractive); } +} // namespace ccls diff --git a/src/project.h b/src/project.hh similarity index 97% rename from src/project.h rename to src/project.hh index ad7b576d..92d50df4 100644 --- a/src/project.h +++ b/src/project.hh @@ -4,7 +4,7 @@ #pragma once #include "config.h" -#include "lsp.h" +#include "lsp.hh" #include #include @@ -12,6 +12,7 @@ #include #include +namespace ccls { struct WorkingFiles; std::pair lookupExtension(std::string_view filename); @@ -64,3 +65,4 @@ struct Project { void Index(WorkingFiles *wfiles, lsRequestId id); }; +} // namespace ccls diff --git a/src/query.cc b/src/query.cc index 6d2ab2e8..8bc294c6 100644 --- a/src/query.cc +++ b/src/query.cc @@ -4,7 +4,7 @@ #include "query.h" #include "indexer.h" -#include "serializer.h" +#include "serializer.hh" #include "serializers/json.h" #include @@ -15,8 +15,8 @@ #include #include +namespace ccls { namespace { - void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) { if (use.file_id == -1) use.file_id = file_id; @@ -456,3 +456,4 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) { } return ""; } +} // namespace ccls diff --git a/src/query.h b/src/query.h index a64d2420..bccb6b1d 100644 --- a/src/query.h +++ b/src/query.h @@ -4,23 +4,26 @@ #pragma once #include "indexer.h" -#include "serializer.h" +#include "serializer.hh" #include #include #include namespace llvm { -template <> struct DenseMapInfo { - static inline ExtentRef getEmptyKey() { return {}; } - static inline ExtentRef getTombstoneKey() { return {{Range(), Usr(-1)}}; } - static unsigned getHashValue(ExtentRef sym) { - return std::hash()(sym); +template <> struct DenseMapInfo { + static inline ccls::ExtentRef getEmptyKey() { return {}; } + static inline ccls::ExtentRef getTombstoneKey() { + return {{ccls::Range(), ccls::Usr(-1)}}; } - static bool isEqual(ExtentRef l, ExtentRef r) { return l == r; } + static unsigned getHashValue(ccls::ExtentRef sym) { + return std::hash()(sym); + } + static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; } }; -} +} // namespace llvm +namespace ccls { struct QueryFile { struct Def { std::string path; @@ -181,3 +184,4 @@ struct DB { QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); } QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); } }; +} // namespace ccls diff --git a/src/query_utils.cc b/src/query_utils.cc index 908dd00c..d950f1d6 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -8,6 +8,7 @@ #include #include +namespace ccls { namespace { // Computes roughly how long |range| is. @@ -330,3 +331,4 @@ std::vector FindSymbolsAtLocation(WorkingFile *wfile, return symbols; } +} // namespace ccls diff --git a/src/query_utils.h b/src/query_utils.h index 96f9ae4a..eb95022d 100644 --- a/src/query_utils.h +++ b/src/query_utils.h @@ -8,6 +8,7 @@ #include +namespace ccls { Maybe GetDefinitionSpell(DB *db, SymbolIdx sym); // Get defining declaration (if exists) or an arbitrary declaration (otherwise) @@ -90,3 +91,4 @@ void EachDefinedFunc(DB *db, const std::vector &usrs, Fn &&fn) { fn(obj); } } +} // namespace ccls diff --git a/src/serializer.cc b/src/serializer.cc index eb6c15bd..7243ea42 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -1,7 +1,7 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 -#include "serializer.h" +#include "serializer.hh" #include "filesystem.hh" #include "indexer.h" @@ -15,11 +15,11 @@ #include #include -using namespace ccls; using namespace llvm; bool gTestOutputMode = false; +namespace ccls { Reader::~Reader() {} BinaryReader::~BinaryReader() {} JsonReader::~JsonReader() {} @@ -350,7 +350,6 @@ void Reflect(Writer &visitor, SerializeFormat &value) { } } -namespace ccls { static BumpPtrAllocator Alloc; static DenseSet Strings; static std::mutex AllocMutex; diff --git a/src/serializer.h b/src/serializer.hh similarity index 98% rename from src/serializer.h rename to src/serializer.hh index 517d2147..98614f1d 100644 --- a/src/serializer.h +++ b/src/serializer.hh @@ -23,6 +23,7 @@ class CachedHashStringRef; class StringRef; } +namespace ccls { enum class SerializeFormat { Binary, Json }; struct JsonNull {}; @@ -91,12 +92,12 @@ struct IndexFile; #define MAKE_REFLECT_TYPE_PROXY2(type, as_type) \ LLVM_ATTRIBUTE_UNUSED inline void Reflect(Reader &visitor, type &value) { \ as_type value0; \ - ::Reflect(visitor, value0); \ + ::ccls::Reflect(visitor, value0); \ value = static_cast(value0); \ } \ LLVM_ATTRIBUTE_UNUSED inline void Reflect(Writer &visitor, type &value) { \ auto value0 = static_cast(value); \ - ::Reflect(visitor, value0); \ + ::ccls::Reflect(visitor, value0); \ } #define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name); @@ -293,7 +294,6 @@ template void ReflectMember(Writer &vis, const char *name, T &v) { // API -namespace ccls { const char *Intern(llvm::StringRef str); llvm::CachedHashStringRef InternH(llvm::StringRef str); std::string Serialize(SerializeFormat format, IndexFile &file); diff --git a/src/serializers/binary.h b/src/serializers/binary.h index 1f2fa047..ffe451ee 100644 --- a/src/serializers/binary.h +++ b/src/serializers/binary.h @@ -3,10 +3,11 @@ #pragma once -#include "serializer.h" +#include "serializer.hh" #include +namespace ccls { class BinaryReader : public Reader { const char *p_; @@ -123,3 +124,4 @@ public: void EndObject() override {} void Key(const char *name) override {} }; +} // namespace ccls diff --git a/src/serializers/json.h b/src/serializers/json.h index 0c3c31c4..b28de0a4 100644 --- a/src/serializers/json.h +++ b/src/serializers/json.h @@ -3,11 +3,12 @@ #pragma once -#include "serializer.h" +#include "serializer.hh" #include #include +namespace ccls { class JsonReader : public Reader { rapidjson::GenericValue> *m_; std::vector path_; @@ -104,3 +105,4 @@ public: void EndObject() override { m_->EndObject(); } void Key(const char *name) override { m_->Key(name); } }; +} // namespace ccls diff --git a/src/test.cc b/src/test.cc index 7eed1af0..bb488e23 100644 --- a/src/test.cc +++ b/src/test.cc @@ -8,7 +8,7 @@ #include "indexer.h" #include "pipeline.hh" #include "platform.h" -#include "serializer.h" +#include "serializer.hh" #include "utils.h" #include @@ -32,6 +32,7 @@ using namespace llvm; extern bool gTestOutputMode; +namespace ccls { std::string ToString(const rapidjson::Document &document) { rapidjson::StringBuffer buffer; rapidjson::PrettyWriter writer(buffer); @@ -361,3 +362,4 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) { return success; } +} // namespace ccls diff --git a/src/test.h b/src/test.h index e93f3fa5..417c974b 100644 --- a/src/test.h +++ b/src/test.h @@ -5,4 +5,6 @@ #include +namespace ccls { bool RunIndexTests(const std::string &filter_path, bool enable_update); +} diff --git a/src/threaded_queue.h b/src/threaded_queue.h index ccafc443..ab514d12 100644 --- a/src/threaded_queue.h +++ b/src/threaded_queue.h @@ -13,17 +13,18 @@ #include #include -struct BaseThreadQueue { - virtual bool IsEmpty() = 0; - virtual ~BaseThreadQueue() = default; -}; - // std::lock accepts two or more arguments. We define an overload for one // argument. namespace std { template void lock(Lockable &l) { l.lock(); } } // namespace std +namespace ccls { +struct BaseThreadQueue { + virtual bool IsEmpty() = 0; + virtual ~BaseThreadQueue() = default; +}; + template struct MultiQueueLock { MultiQueueLock(Queue... lockable) : tuple_{lockable...} { lock(); } ~MultiQueueLock() { unlock(); } @@ -161,3 +162,4 @@ private: MultiQueueWaiter *waiter_; std::unique_ptr owned_waiter_; }; +} // namespace ccls diff --git a/src/utils.cc b/src/utils.cc index 2cdaa9ad..8cd824d0 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -21,6 +21,7 @@ using namespace llvm; #include #include +namespace ccls { uint64_t HashUsr(std::string_view s) { union { uint64_t ret; @@ -162,3 +163,4 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text, } std::string GetDefaultResourceDirectory() { return DEFAULT_RESOURCE_DIRECTORY; } +} diff --git a/src/utils.h b/src/utils.h index a4e0e04f..b4f52b21 100644 --- a/src/utils.h +++ b/src/utils.h @@ -14,6 +14,7 @@ namespace llvm { class StringRef; } +namespace ccls { uint64_t HashUsr(std::string_view s); uint64_t HashUsr(llvm::StringRef s); @@ -69,10 +70,11 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) { template <> struct hash { \ std::size_t operator()(const type &t) const { \ std::size_t ret = 0; \ - hash_combine(ret, __VA_ARGS__); \ + ccls::hash_combine(ret, __VA_ARGS__); \ return ret; \ } \ }; \ } std::string GetDefaultResourceDirectory(); +} // namespace ccls diff --git a/src/working_files.cc b/src/working_files.cc index 69b483e4..93316ded 100644 --- a/src/working_files.cc +++ b/src/working_files.cc @@ -11,6 +11,7 @@ #include #include +namespace ccls { namespace { // When finding a best match of buffer line and index line, limit the max edit @@ -456,7 +457,7 @@ WorkingFile *WorkingFiles::OnOpen(const lsTextDocumentItem &open) { return files[files.size() - 1].get(); } -void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) { +void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) { std::lock_guard lock(files_mutex); std::string filename = change.textDocument.uri.GetPath(); @@ -471,7 +472,7 @@ void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) { if (change.textDocument.version) file->version = *change.textDocument.version; - for (const lsTextDocumentContentChangeEvent &diff : change.contentChanges) { + for (const TextDocumentContentChangeEvent &diff : change.contentChanges) { // Per the spec replace everything if the rangeLength and range are not set. // See https://github.com/Microsoft/language-server-protocol/issues/9. if (!diff.range) { @@ -563,3 +564,4 @@ std::string_view LexIdentifierAroundPos(lsPosition position, return content.substr(start, end - start); } +} diff --git a/src/working_files.h b/src/working_files.h index 94cef1b5..09ccf31a 100644 --- a/src/working_files.h +++ b/src/working_files.h @@ -3,13 +3,14 @@ #pragma once -#include "lsp.h" +#include "lsp.hh" #include "utils.h" #include #include #include +namespace ccls { struct WorkingFile { int version = 0; std::string filename; @@ -105,7 +106,7 @@ struct WorkingFiles { const std::function &action); WorkingFile *OnOpen(const lsTextDocumentItem &open); - void OnChange(const lsTextDocumentDidChangeParams &change); + void OnChange(const TextDocumentDidChangeParam &change); void OnClose(const lsTextDocumentIdentifier &close); // If |filter_paths| is non-empty, only files which contain any of the given @@ -123,3 +124,4 @@ int GetOffsetForPosition(lsPosition position, std::string_view content); std::string_view LexIdentifierAroundPos(lsPosition position, std::string_view content); +} // namespace ccls