Refactor message handler and namespace ccls

This commit is contained in:
Fangrui Song 2018-10-28 10:49:31 -07:00
parent 8fa853c321
commit 3cd0ee2fc7
73 changed files with 2419 additions and 3126 deletions

View File

@ -212,11 +212,8 @@ target_sources(ccls PRIVATE
src/messages/ccls_navigate.cc src/messages/ccls_navigate.cc
src/messages/ccls_reload.cc src/messages/ccls_reload.cc
src/messages/ccls_vars.cc src/messages/ccls_vars.cc
src/messages/exit.cc
src/messages/initialize.cc src/messages/initialize.cc
src/messages/shutdown.cc src/messages/textDocument_code.cc
src/messages/textDocument_codeAction.cc
src/messages/textDocument_codeLens.cc
src/messages/textDocument_completion.cc src/messages/textDocument_completion.cc
src/messages/textDocument_definition.cc src/messages/textDocument_definition.cc
src/messages/textDocument_did.cc src/messages/textDocument_did.cc
@ -227,7 +224,5 @@ target_sources(ccls PRIVATE
src/messages/textDocument_references.cc src/messages/textDocument_references.cc
src/messages/textDocument_rename.cc src/messages/textDocument_rename.cc
src/messages/textDocument_signatureHelp.cc src/messages/textDocument_signatureHelp.cc
src/messages/textDocument_typeDefinition.cc src/messages/workspace.cc
src/messages/workspace_did.cc
src/messages/workspace_symbol.cc
) )

View File

@ -563,8 +563,6 @@ std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
return preamble; return preamble;
} }
} // namespace ccls
CompletionManager::CompletionManager(Project *project, CompletionManager::CompletionManager(Project *project,
WorkingFiles *working_files, WorkingFiles *working_files,
OnDiagnostic on_diagnostic, OnDiagnostic on_diagnostic,
@ -674,3 +672,4 @@ void CompletionManager::FlushAllSessions() {
preloads.Clear(); preloads.Clear();
sessions.Clear(); sessions.Clear();
} }
} // namespace ccls

View File

@ -5,9 +5,8 @@
#include "clang_tu.hh" #include "clang_tu.hh"
#include "lru_cache.h" #include "lru_cache.h"
#include "lsp.h" #include "lsp.hh"
#include "lsp_completion.h" #include "project.hh"
#include "project.h"
#include "threaded_queue.h" #include "threaded_queue.h"
#include "working_files.h" #include "working_files.h"
@ -61,7 +60,6 @@ struct CompletionSession
std::shared_ptr<PreambleData> GetPreamble(); std::shared_ptr<PreambleData> GetPreamble();
}; };
}
struct CompletionManager { struct CompletionManager {
using OnDiagnostic = std::function<void( using OnDiagnostic = std::function<void(
@ -181,3 +179,4 @@ struct CompleteConsumerCache {
return this->path == path && this->position == position; return this->path == path && this->position == position;
} }
}; };
} // namespace ccls

View File

@ -12,6 +12,7 @@
using namespace clang; using namespace clang;
namespace ccls {
std::string PathFromFileEntry(const FileEntry &file) { std::string PathFromFileEntry(const FileEntry &file) {
StringRef Name = file.tryGetRealPathName(); StringRef Name = file.tryGetRealPathName();
if (Name.empty()) if (Name.empty())
@ -241,3 +242,4 @@ const char *ClangBuiltinTypeName(int kind) {
return ""; return "";
} }
} }
} // namespace ccls

View File

@ -17,6 +17,7 @@ namespace vfs = clang::vfs;
} }
#endif #endif
namespace ccls {
std::string PathFromFileEntry(const clang::FileEntry &file); std::string PathFromFileEntry(const clang::FileEntry &file);
Range FromCharSourceRange(const clang::SourceManager &SM, Range FromCharSourceRange(const clang::SourceManager &SM,
@ -42,3 +43,4 @@ BuildCompilerInvocation(std::vector<const char *> args,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS); llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
const char *ClangBuiltinTypeName(int); const char *ClangBuiltinTypeName(int);
} // namespace ccls

View File

@ -3,9 +3,9 @@
#include "config.h" #include "config.h"
namespace ccls {
Config *g_config; Config *g_config;
namespace ccls {
void DoPathMapping(std::string &arg) { void DoPathMapping(std::string &arg) {
for (const std::string &mapping : g_config->clang.pathMappings) { for (const std::string &mapping : g_config->clang.pathMappings) {
auto sep = mapping.find('>'); auto sep = mapping.find('>');

View File

@ -3,10 +3,11 @@
#pragma once #pragma once
#include "serializer.h" #include "serializer.hh"
#include <string> #include <string>
namespace ccls {
/* /*
The language client plugin needs to send initialization options in the The language client plugin needs to send initialization options in the
`initialize` request to the ccls language server. `initialize` request to the ccls language server.
@ -269,6 +270,5 @@ MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand,
extern Config *g_config; extern Config *g_config;
namespace ccls {
void DoPathMapping(std::string &arg); void DoPathMapping(std::string &arg);
} }

View File

@ -11,7 +11,7 @@ using namespace llvm;
void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
const std::function<void(const std::string &)> &handler) { const std::function<void(const std::string &)> &handler) {
EnsureEndsInSlash(folder); ccls::EnsureEndsInSlash(folder);
sys::fs::file_status Status; sys::fs::file_status Status;
if (sys::fs::status(folder, Status, true)) if (sys::fs::status(folder, Status, true))
return; return;

View File

@ -8,10 +8,11 @@
#include <stdio.h> #include <stdio.h>
#include <vector> #include <vector>
namespace ccls {
namespace {
enum CharClass { Other, Lower, Upper }; enum CharClass { Other, Lower, Upper };
enum CharRole { None, Tail, Head }; enum CharRole { None, Tail, Head };
namespace {
CharClass GetCharClass(int c) { CharClass GetCharClass(int c) {
if (islower(c)) if (islower(c))
return Lower; 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)); ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j));
return ret; return ret;
} }
} // namespace ccls
#if 0 #if 0
TEST_SUITE("fuzzy_match") { TEST_SUITE("fuzzy_match") {

View File

@ -7,6 +7,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
namespace ccls {
class FuzzyMatcher { class FuzzyMatcher {
public: public:
constexpr static int kMaxPat = 100; constexpr static int kMaxPat = 100;
@ -30,3 +31,4 @@ private:
int MatchScore(int i, int j, bool last); int MatchScore(int i, int j, bool last);
int MissScore(int j, bool last); int MissScore(int j, bool last);
}; };
} // namespace ccls

View File

@ -3,11 +3,12 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.hh"
#include <algorithm> #include <algorithm>
#include <queue> #include <queue>
namespace ccls {
template <typename Node> template <typename Node>
std::vector<lsLocation> FlattenHierarchy(const std::optional<Node> &root) { std::vector<lsLocation> FlattenHierarchy(const std::optional<Node> &root) {
if (!root) if (!root)
@ -28,3 +29,4 @@ std::vector<lsLocation> FlattenHierarchy(const std::optional<Node> &root) {
ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
return ret; return ret;
} }
} // namespace ccls

View File

@ -6,15 +6,18 @@
#include "filesystem.hh" #include "filesystem.hh"
#include "match.h" #include "match.h"
#include "platform.h" #include "platform.h"
#include "project.h" #include "project.hh"
#include <llvm/ADT/Twine.h> #include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
#include <llvm/Support/Timer.h> #include <llvm/Support/Timer.h>
#include <unordered_set>
using namespace llvm; using namespace llvm;
#include <thread> #include <thread>
namespace ccls {
namespace { namespace {
struct CompletionCandidate { struct CompletionCandidate {
@ -83,7 +86,6 @@ lsCompletionItem BuildCompletionItem(const std::string &path,
item.priority_ = 0; item.priority_ = 0;
return item; return item;
} }
} // namespace } // namespace
IncludeComplete::IncludeComplete(Project *project) IncludeComplete::IncludeComplete(Project *project)
@ -198,3 +200,4 @@ IncludeComplete::FindCompletionItemForAbsolutePath(
return std::nullopt; return std::nullopt;
return completion_items[it->second]; return completion_items[it->second];
} }
} // namespace ccls

View File

@ -3,12 +3,12 @@
#pragma once #pragma once
#include "lsp_completion.h" #include "message_handler.hh"
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <unordered_set>
namespace ccls {
struct GroupMatch; struct GroupMatch;
struct Project; struct Project;
@ -26,18 +26,18 @@ struct IncludeComplete {
void InsertIncludesFromDirectory(std::string directory, void InsertIncludesFromDirectory(std::string directory,
bool use_angle_brackets); bool use_angle_brackets);
std::optional<lsCompletionItem> std::optional<ccls::lsCompletionItem>
FindCompletionItemForAbsolutePath(const std::string &absolute_path); FindCompletionItemForAbsolutePath(const std::string &absolute_path);
// Insert item to |completion_items|. // Insert item to |completion_items|.
// Update |absolute_path_to_completion_item| and |inserted_paths|. // Update |absolute_path_to_completion_item| and |inserted_paths|.
void InsertCompletionItem(const std::string &absolute_path, void InsertCompletionItem(const std::string &absolute_path,
lsCompletionItem &&item); ccls::lsCompletionItem &&item);
// Guards |completion_items| when |is_scanning| is true. // Guards |completion_items| when |is_scanning| is true.
std::mutex completion_items_mutex; std::mutex completion_items_mutex;
std::atomic<bool> is_scanning; std::atomic<bool> is_scanning;
std::vector<lsCompletionItem> completion_items; std::vector<ccls::lsCompletionItem> completion_items;
// Absolute file path to the completion item in |completion_items|. // Absolute file path to the completion item in |completion_items|.
// Keep the one with shortest include path. // Keep the one with shortest include path.
@ -50,3 +50,4 @@ struct IncludeComplete {
Project *project_; Project *project_;
std::unique_ptr<GroupMatch> match_; std::unique_ptr<GroupMatch> match_;
}; };
} // namespace ccls

View File

@ -9,7 +9,7 @@
#include "match.h" #include "match.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.h" #include "platform.h"
#include "serializer.h" #include "serializer.hh"
#include <clang/AST/AST.h> #include <clang/AST/AST.h>
#include <clang/Frontend/FrontendAction.h> #include <clang/Frontend/FrontendAction.h>
@ -26,9 +26,9 @@
#include <map> #include <map>
#include <unordered_set> #include <unordered_set>
using namespace ccls;
using namespace clang; using namespace clang;
namespace ccls {
namespace { namespace {
constexpr int kInitializerMaxLines = 3; constexpr int kInitializerMaxLines = 3;
@ -1195,7 +1195,7 @@ template <typename T> void Uniquify(std::vector<T> &a) {
a.resize(n); a.resize(n);
} }
namespace ccls::idx { namespace idx {
void Init() { void Init() {
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist,
g_config->index.multiVersionBlacklist); g_config->index.multiVersionBlacklist);
@ -1334,7 +1334,7 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
return result; return result;
} }
} // namespace ccls::idx } // namespace idx
void Reflect(Reader &vis, SymbolRef &v) { void Reflect(Reader &vis, SymbolRef &v) {
if (vis.Format() == SerializeFormat::Json) { if (vis.Format() == SerializeFormat::Json) {
@ -1422,3 +1422,4 @@ void Reflect(Writer &vis, DeclRef &v) {
Reflect(vis, v.extent); Reflect(vis, v.extent);
} }
} }
} // namespace ccls

View File

@ -3,10 +3,10 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.hh"
#include "maybe.h" #include "maybe.h"
#include "position.h" #include "position.h"
#include "serializer.h" #include "serializer.hh"
#include "utils.h" #include "utils.h"
#include <clang/Basic/FileManager.h> #include <clang/Basic/FileManager.h>
@ -19,6 +19,17 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace std {
template <> struct hash<llvm::sys::fs::UniqueID> {
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; using Usr = uint64_t;
enum class LanguageId; enum class LanguageId;
@ -48,7 +59,6 @@ struct SymbolRef {
bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); } bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); }
bool Valid() const { return range.Valid(); } bool Valid() const { return range.Valid(); }
}; };
MAKE_HASHABLE(SymbolRef, t.range, t.usr, t.kind, t.role);
struct ExtentRef : SymbolRef { struct ExtentRef : SymbolRef {
Range extent; Range extent;
@ -57,7 +67,6 @@ struct ExtentRef : SymbolRef {
} }
bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); } 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 { struct Ref {
Range range; Range range;
@ -81,12 +90,10 @@ struct Use : Ref {
return range == o.range && file_id == o.file_id; return range == o.range && file_id == o.file_id;
} }
}; };
MAKE_HASHABLE(Use, t.range, t.file_id)
struct DeclRef : Use { struct DeclRef : Use {
Range extent; Range extent;
}; };
MAKE_HASHABLE(DeclRef, t.range, t.file_id)
void Reflect(Reader &visitor, SymbolRef &value); void Reflect(Reader &visitor, SymbolRef &value);
void Reflect(Writer &visitor, SymbolRef &value); void Reflect(Writer &visitor, SymbolRef &value);
@ -233,16 +240,6 @@ struct IndexInclude {
const char *resolved_path; const char *resolved_path;
}; };
namespace std {
template <> struct hash<llvm::sys::fs::UniqueID> {
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 { struct IndexFile {
// For both JSON and MessagePack cache files. // For both JSON and MessagePack cache files.
static const int kMajorVersion; static const int kMajorVersion;
@ -296,7 +293,7 @@ struct CompletionManager;
struct WorkingFiles; struct WorkingFiles;
struct VFS; struct VFS;
namespace ccls::idx { namespace idx {
void Init(); void Init();
std::vector<std::unique_ptr<IndexFile>> std::vector<std::unique_ptr<IndexFile>>
Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs,
@ -304,4 +301,10 @@ Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs,
const std::vector<const char *> &args, const std::vector<const char *> &args,
const std::vector<std::pair<std::string, std::string>> &remapped, const std::vector<std::pair<std::string, std::string>> &remapped,
bool &ok); 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)

View File

@ -1,18 +1,15 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "lsp.h" #include "lsp.hh"
#include "log.hh" #include "log.hh"
#include "serializers/json.h" #include "serializers/json.h"
#include <rapidjson/document.h>
#include <algorithm> #include <algorithm>
#include <stdio.h> #include <stdio.h>
MethodType kMethodType_Exit = "exit"; namespace ccls {
void Reflect(Reader &visitor, lsRequestId &value) { void Reflect(Reader &visitor, lsRequestId &value) {
if (visitor.IsInt64()) { if (visitor.IsInt64()) {
value.type = lsRequestId::kInt; value.type = lsRequestId::kInt;
@ -44,10 +41,6 @@ void Reflect(Writer &visitor, lsRequestId &value) {
} }
} }
InMessage::~InMessage() {}
MessageRegistry *MessageRegistry::instance_ = nullptr;
lsTextDocumentIdentifier lsTextDocumentIdentifier
lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const { lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const {
lsTextDocumentIdentifier result; lsTextDocumentIdentifier result;
@ -55,120 +48,6 @@ lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const {
return result; return result;
} }
// Reads a JsonRpc message. |read| returns the next input character.
std::optional<std::string>
ReadJsonRpcContentFrom(std::function<std::optional<char>()> 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<char> 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<char> 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<char> c = read();
if (!c) {
LOG_S(INFO) << "No more input when reading content body";
return std::nullopt;
}
content += *c;
}
return content;
}
std::optional<char> 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<std::string>
MessageRegistry::ReadMessageFromStdin(std::unique_ptr<InMessage> *message) {
std::optional<std::string> 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<std::string>
MessageRegistry::Parse(Reader &visitor, std::unique_ptr<InMessage> *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<JsonReader &>(visitor).GetPath() + ", expected " +
e.what();
}
}
MessageRegistry *MessageRegistry::instance() {
if (!instance_)
instance_ = new MessageRegistry();
return instance_;
}
lsDocumentUri lsDocumentUri::FromPath(const std::string &path) { lsDocumentUri lsDocumentUri::FromPath(const std::string &path) {
lsDocumentUri result; lsDocumentUri result;
result.SetPath(path); result.SetPath(path);
@ -260,16 +139,4 @@ std::string lsPosition::ToString() const {
bool lsTextEdit::operator==(const lsTextEdit &that) { bool lsTextEdit::operator==(const lsTextEdit &that) {
return range == that.range && newText == that.newText; return range == that.range && newText == that.newText;
} }
} // namespace ccls
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);
}
}

View File

@ -4,15 +4,15 @@
#pragma once #pragma once
#include "config.h" #include "config.h"
#include "serializer.h" #include "serializer.hh"
#include "utils.h" #include "utils.h"
#include <rapidjson/fwd.h>
#include <iosfwd> #include <iosfwd>
#include <unordered_map> #include <unordered_map>
using MethodType = const char *; namespace ccls {
extern MethodType kMethodType_Exit;
struct lsRequestId { struct lsRequestId {
// The client can send the request id as an int or a string. We should output // The client can send the request id as an int or a string. We should output
// the same format we received. // the same format we received.
@ -27,47 +27,10 @@ void Reflect(Reader &visitor, lsRequestId &value);
void Reflect(Writer &visitor, lsRequestId &value); void Reflect(Writer &visitor, lsRequestId &value);
struct InMessage { struct InMessage {
virtual ~InMessage();
virtual MethodType GetMethodType() const = 0;
virtual lsRequestId GetRequestId() const { return {}; }
};
struct NotificationMessage : InMessage {};
struct RequestMessage : public InMessage {
lsRequestId id; lsRequestId id;
lsRequestId GetRequestId() const override { return id; } std::string method;
}; std::unique_ptr<char[]> message;
std::unique_ptr<rapidjson::Document> document;
#define REGISTER_IN_MESSAGE(type) \
static MessageRegistryRegister<type> type##message_handler_instance_;
struct MessageRegistry {
static MessageRegistry *instance_;
static MessageRegistry *instance();
using Allocator =
std::function<void(Reader &visitor, std::unique_ptr<InMessage> *)>;
std::unordered_map<std::string, Allocator> allocators;
std::optional<std::string>
ReadMessageFromStdin(std::unique_ptr<InMessage> *message);
std::optional<std::string> Parse(Reader &visitor,
std::unique_ptr<InMessage> *message);
};
template <typename T> struct MessageRegistryRegister {
MessageRegistryRegister() {
T dummy;
std::string method_name = dummy.GetMethodType();
MessageRegistry::instance()->allocators[method_name] =
[](Reader &visitor, std::unique_ptr<InMessage> *message) {
*message = std::make_unique<T>();
// Reflect may throw and *message will be partially deserialized.
Reflect(visitor, static_cast<T &>(**message));
};
}
}; };
enum class lsErrorCodes { enum class lsErrorCodes {
@ -214,7 +177,6 @@ struct lsSymbolInformation {
lsLocation location; lsLocation location;
std::optional<std::string_view> containerName; std::optional<std::string_view> containerName;
}; };
MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName);
struct lsTextDocumentIdentifier { struct lsTextDocumentIdentifier {
lsDocumentUri uri; lsDocumentUri uri;
@ -230,15 +192,6 @@ struct lsVersionedTextDocumentIdentifier {
}; };
MAKE_REFLECT_STRUCT(lsVersionedTextDocumentIdentifier, uri, version); 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 { struct lsTextEdit {
// The range of the text document to be manipulated. To insert // The range of the text document to be manipulated. To insert
// text into a document create a range where start === end. // text into a document create a range where start === end.
@ -289,26 +242,7 @@ struct lsWorkspaceEdit {
}; };
MAKE_REFLECT_STRUCT(lsWorkspaceEdit, documentChanges); MAKE_REFLECT_STRUCT(lsWorkspaceEdit, documentChanges);
// MarkedString can be used to render human readable text. It is either a struct TextDocumentContentChangeEvent {
// 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<std::string> language;
std::string value;
};
void Reflect(Writer &visitor, lsMarkedString &value);
struct lsTextDocumentContentChangeEvent {
// The range of the document that changed. // The range of the document that changed.
std::optional<lsRange> range; std::optional<lsRange> range;
// The length of the range that got replaced. // The length of the range that got replaced.
@ -316,24 +250,21 @@ struct lsTextDocumentContentChangeEvent {
// The new text of the range/document. // The new text of the range/document.
std::string text; std::string text;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentContentChangeEvent, range, rangeLength, text);
struct lsTextDocumentDidChangeParams { struct TextDocumentDidChangeParam {
lsVersionedTextDocumentIdentifier textDocument; lsVersionedTextDocumentIdentifier textDocument;
std::vector<lsTextDocumentContentChangeEvent> contentChanges; std::vector<TextDocumentContentChangeEvent> contentChanges;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentDidChangeParams, textDocument,
contentChanges);
struct lsWorkspaceFolder { struct WorkspaceFolder {
lsDocumentUri uri; lsDocumentUri uri;
std::string name; std::string name;
}; };
MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name); MAKE_REFLECT_STRUCT(WorkspaceFolder, uri, name);
// Show a message to the user. // Show a message to the user.
enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 }; enum class MessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
MAKE_REFLECT_TYPE_PROXY(lsMessageType) MAKE_REFLECT_TYPE_PROXY(MessageType)
enum class lsDiagnosticSeverity { enum class lsDiagnosticSeverity {
// Reports an error. // Reports an error.
@ -380,7 +311,7 @@ struct lsPublishDiagnosticsParams {
MAKE_REFLECT_STRUCT(lsPublishDiagnosticsParams, uri, diagnostics); MAKE_REFLECT_STRUCT(lsPublishDiagnosticsParams, uri, diagnostics);
struct lsShowMessageParams { struct lsShowMessageParams {
lsMessageType type = lsMessageType::Error; MessageType type = MessageType::Error;
std::string message; std::string message;
}; };
MAKE_REFLECT_STRUCT(lsShowMessageParams, type, 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) { inline Role operator|(Role lhs, Role rhs) {
return Role(uint16_t(lhs) | uint16_t(rhs)); return Role(uint16_t(lhs) | uint16_t(rhs));
} }
} // namespace ccls

View File

@ -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<std::string> 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<std::string> 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<std::string> 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<lsTextEdit> 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);

View File

@ -4,7 +4,7 @@
#include "log.hh" #include "log.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.h" #include "platform.h"
#include "serializer.h" #include "serializer.hh"
#include "serializers/json.h" #include "serializers/json.h"
#include "test.h" #include "test.h"
#include "working_files.h" #include "working_files.h"
@ -28,7 +28,9 @@ using namespace ccls;
using namespace llvm; using namespace llvm;
using namespace llvm::cl; using namespace llvm::cl;
namespace ccls {
std::string g_init_options; std::string g_init_options;
}
namespace { namespace {
OptionCategory C("ccls options"); OptionCategory C("ccls options");
@ -92,7 +94,7 @@ int main(int argc, char **argv) {
if (opt_test_index != "!") { if (opt_test_index != "!") {
language_server = false; language_server = false;
if (!RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput())) if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
return 1; return 1;
} }

View File

@ -3,10 +3,10 @@
#include "match.h" #include "match.h"
#include "lsp.h" #include "lsp.hh"
#include "pipeline.hh" #include "pipeline.hh"
using namespace ccls;
namespace ccls {
std::optional<Matcher> Matcher::Create(const std::string &search) { std::optional<Matcher> Matcher::Create(const std::string &search) {
try { try {
Matcher m; Matcher m;
@ -19,7 +19,7 @@ std::optional<Matcher> Matcher::Create(const std::string &search) {
return m; return m;
} catch (const std::exception &e) { } catch (const std::exception &e) {
lsShowMessageParams params; lsShowMessageParams params;
params.type = lsMessageType::Error; params.type = MessageType::Error;
params.message = params.message =
"failed to parse EMCAScript regex " + search + " : " + e.what(); "failed to parse EMCAScript regex " + search + " : " + e.what();
pipeline::Notify(window_showMessage, params); pipeline::Notify(window_showMessage, params);
@ -62,3 +62,4 @@ bool GroupMatch::IsMatch(const std::string &value,
return true; return true;
} }
} // namespace ccls

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ccls {
struct Matcher { struct Matcher {
static std::optional<Matcher> Create(const std::string &search); static std::optional<Matcher> Create(const std::string &search);
@ -29,3 +30,4 @@ struct GroupMatch {
std::vector<Matcher> whitelist; std::vector<Matcher> whitelist;
std::vector<Matcher> blacklist; std::vector<Matcher> blacklist;
}; };
} // namespace ccls

View File

@ -7,6 +7,7 @@
#include <utility> #include <utility>
namespace ccls {
// Like std::optional, but the stored data is responsible for containing the // Like std::optional, but the stored data is responsible for containing the
// empty state. T should define a function `bool T::Valid()`. // empty state. T should define a function `bool T::Valid()`.
template <typename T> class Maybe { template <typename T> class Maybe {
@ -44,3 +45,4 @@ public:
bool operator==(const Maybe &o) const { return storage == o.storage; } bool operator==(const Maybe &o) const { return storage == o.storage; }
bool operator!=(const Maybe &o) const { return !(*this == o); } bool operator!=(const Maybe &o) const { return !(*this == o); }
}; };
} // namespace ccls

View File

@ -1,23 +1,55 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "log.hh" #include "log.hh"
#include "match.h" #include "match.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.h" #include "project.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls; #include "serializers/json.h"
using namespace clang; using namespace clang;
#include <algorithm> #include <algorithm>
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 { namespace {
struct CclsSemanticHighlightSymbol { struct CclsSemanticHighlightSymbol {
int id = 0; int id = 0;
lsSymbolKind parentKind; lsSymbolKind parentKind;
@ -43,6 +75,7 @@ struct CclsSetSkippedRangesParams {
}; };
MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges); MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges);
struct ScanLineEvent { struct ScanLineEvent {
lsPosition pos; lsPosition pos;
lsPosition end_pos; // Second key when there is a tie for insertion events. lsPosition end_pos; // Second key when there is a tie for insertion events.
@ -61,55 +94,161 @@ struct ScanLineEvent {
}; };
} // namespace } // namespace
MessageHandler::MessageHandler() { void MessageHandler::Bind(const char *method, void (MessageHandler::*handler)(Reader &)) {
// Dynamically allocate |message_handlers|, otherwise there will be static method2notification[method] = [this, handler](Reader &reader) {
// initialization order races. (this->*handler)(reader);
if (!message_handlers) };
message_handlers = new std::vector<MessageHandler *>();
message_handlers->push_back(this);
} }
std::vector<MessageHandler *> *MessageHandler::message_handlers = nullptr; template <typename Param>
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<lsRequestId> id, void MessageHandler::Bind(const char *method,
const std::string &absolute_path, void (MessageHandler::*handler)(Reader &,
QueryFile **out_query_file, int *out_file_id) { ReplyOnce &)) {
*out_query_file = nullptr; method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) {
(this->*handler)(reader, reply);
};
}
auto it = db->name2file_id.find(LowerPathIfInsensitive(absolute_path)); template <typename Param>
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(&param);
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()) { if (it != db->name2file_id.end()) {
QueryFile &file = db->files[it->second]; QueryFile &file = db->files[it->second];
if (file.def) { if (file.def) {
*out_query_file = &file; ret = &file;
if (out_file_id) if (out_file_id)
*out_file_id = it->second; *out_file_id = it->second;
return true; return ret;
} }
} }
if (out_file_id) if (out_file_id)
*out_file_id = -1; *out_file_id = -1;
if (reply.id.Valid()) {
bool has_entry = false; bool has_entry = false;
{ {
std::lock_guard<std::mutex> lock(project->mutex_); std::lock_guard<std::mutex> lock(project->mutex_);
for (auto &[root, folder] : project->root2folder) for (auto &[root, folder] : project->root2folder)
has_entry |= folder.path2entry_index.count(absolute_path); has_entry |= folder.path2entry_index.count(path);
} }
if (id) {
lsResponseError err; lsResponseError err;
if (has_entry) { if (has_entry) {
err.code = lsErrorCodes::ServerNotInitialized; err.code = lsErrorCodes::ServerNotInitialized;
err.message = absolute_path + " is being indexed"; err.message = path + " is being indexed";
} else { } else {
err.code = lsErrorCodes::InternalError; 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) { 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)); params.symbols.push_back(std::move(entry.second));
pipeline::Notify("$ccls/publishSemanticHighlight", params); pipeline::Notify("$ccls/publishSemanticHighlight", params);
} }
} // namespace ccls

View File

@ -1,71 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "lsp.h"
#include "query.h"
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
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<InMessage> message) = 0;
static std::vector<MessageHandler *> *message_handlers;
protected:
MessageHandler();
};
template <typename TMessage> struct BaseMessageHandler : MessageHandler {
virtual void Run(TMessage *message) = 0;
// MessageHandler:
void Run(std::unique_ptr<InMessage> message) override {
Run(static_cast<TMessage *>(message.get()));
}
};
bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> 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);

253
src/message_handler.hh Normal file
View File

@ -0,0 +1,253 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "lsp.hh"
#include "query.h"
#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
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<void(Writer &)> &fn);
void ReplyError(lsRequestId id, const std::function<void(Writer &)> &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<lsDiagnostic> diagnostics;
} context;
};
// completion
enum class lsCompletionTriggerKind {
Invoked = 1,
TriggerCharacter = 2,
TriggerForIncompleteCompletions = 3,
};
struct lsCompletionContext {
lsCompletionTriggerKind triggerKind = lsCompletionTriggerKind::Invoked;
std::optional<std::string> 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<std::string> documentation;
std::string sortText;
std::optional<std::string> filterText;
std::string insertText;
lsInsertTextFormat insertTextFormat = lsInsertTextFormat::PlainText;
lsTextEdit textEdit;
std::vector<lsTextEdit> additionalTextEdits;
std::vector<std::string> 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<Event> changes;
};
struct DidChangeWorkspaceFoldersParam {
struct Event {
std::vector<WorkspaceFolder> added, removed;
} event;
};
struct WorkspaceSymbolParam {
std::string query;
};
// TODO llvm 8 llvm::unique_function
template <typename Res>
using Callback = std::function<void(Res*)>;
struct ReplyOnce {
lsRequestId id;
template <typename Res> void operator()(Res &result) const {
if (id.Valid())
pipeline::Reply(id, [&](Writer &w) { Reflect(w, result); });
}
template <typename Err> 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<std::function<void(Reader &)>> method2notification;
llvm::StringMap<std::function<void(Reader &, ReplyOnce &)>> 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 <typename Param>
void Bind(const char *method, void (MessageHandler::*handler)(Param &));
void Bind(const char *method,
void (MessageHandler::*handler)(Reader &, ReplyOnce &));
template <typename Param>
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

View File

@ -2,18 +2,16 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
#include <unordered_set> #include <unordered_set>
using namespace ccls; namespace ccls {
namespace { namespace {
MethodType kMethodType = "$ccls/call";
enum class CallType : uint8_t { enum class CallType : uint8_t {
Direct = 0, Direct = 0,
Base = 1, Base = 1,
@ -26,15 +24,9 @@ bool operator&(CallType lhs, CallType rhs) {
return uint8_t(lhs) & uint8_t(rhs); return uint8_t(lhs) & uint8_t(rhs);
} }
struct In_cclsCall : public RequestMessage { struct Param : TextDocumentPositionParam {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
// If id is specified, expand a node; otherwise textDocument+position should // If id is specified, expand a node; otherwise textDocument+position should
// be specified for building the root and |levels| of nodes below. // be specified for building the root and |levels| of nodes below.
lsTextDocumentIdentifier textDocument;
lsPosition position;
Usr usr; Usr usr;
std::string id; std::string id;
@ -48,12 +40,8 @@ struct In_cclsCall : public RequestMessage {
int levels = 1; int levels = 1;
bool hierarchy = false; bool hierarchy = false;
}; };
Params params; MAKE_REFLECT_STRUCT(Param, textDocument, position, id, callee, callType,
}; qualified, levels, hierarchy);
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);
struct Out_cclsCall { struct Out_cclsCall {
Usr usr; Usr usr;
@ -163,13 +151,10 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
return true; return true;
} }
struct Handler_cclsCall : BaseMessageHandler<In_cclsCall> { std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
MethodType GetMethodType() const override { return kMethodType; } bool callee, CallType call_type,
bool qualified, int levels) {
std::optional<Out_cclsCall> BuildInitial(Usr root_usr, bool callee, const auto *def = m->db->Func(root_usr).AnyDef();
CallType call_type, bool qualified,
int levels) {
const auto *def = db->Func(root_usr).AnyDef();
if (!def) if (!def)
return {}; return {};
@ -178,55 +163,52 @@ struct Handler_cclsCall : BaseMessageHandler<In_cclsCall> {
entry.usr = root_usr; entry.usr = root_usr;
entry.callType = CallType::Direct; entry.callType = CallType::Direct;
if (def->spell) { if (def->spell) {
if (std::optional<lsLocation> loc = if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
GetLsLocation(db, working_files, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
Expand(this, &entry, callee, call_type, qualified, levels); Expand(m, &entry, callee, call_type, qualified, levels);
return entry; return entry;
} }
} // namespace
void Run(In_cclsCall *request) override { void MessageHandler::ccls_call(Reader &reader, ReplyOnce &reply) {
auto &params = request->params; Param param;
Reflect(reader, param);
std::optional<Out_cclsCall> result; std::optional<Out_cclsCall> result;
if (params.id.size()) { if (param.id.size()) {
try { try {
params.usr = std::stoull(params.id); param.usr = std::stoull(param.id);
} catch (...) { } catch (...) {
return; return;
} }
result.emplace(); result.emplace();
result->id = std::to_string(params.usr); result->id = std::to_string(param.usr);
result->usr = params.usr; result->usr = param.usr;
result->callType = CallType::Direct; result->callType = CallType::Direct;
if (db->HasFunc(params.usr)) if (db->HasFunc(param.usr))
Expand(this, &*result, params.callee, params.callType, params.qualified, Expand(this, &*result, param.callee, param.callType, param.qualified,
params.levels); param.levels);
} else { } else {
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!FindFileOrFail(db, project, request->id, if (!file)
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *working_file = WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path); working_files->GetFileByFilename(file->def->path);
for (SymbolRef sym : for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, params.position)) { FindSymbolsAtLocation(working_file, file, param.position)) {
if (sym.kind == SymbolKind::Func) { if (sym.kind == SymbolKind::Func) {
result = BuildInitial(sym.usr, params.callee, params.callType, result = BuildInitial(this, sym.usr, param.callee, param.callType,
params.qualified, params.levels); param.qualified, param.levels);
break; break;
} }
} }
} }
if (params.hierarchy) if (param.hierarchy)
pipeline::Reply(request->id, result); reply(result);
else { else {
auto out = FlattenHierarchy(result); auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out); reply(out);
} }
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_cclsCall);
} // namespace

View File

@ -1,24 +1,16 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.h" #include "project.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
namespace ccls {
MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges, MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges,
dependencies); dependencies);
namespace { 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 Out_cclsInfo {
struct DB { struct DB {
int files, funcs, types, vars; int files, funcs, types, vars;
@ -34,10 +26,9 @@ MAKE_REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars);
MAKE_REFLECT_STRUCT(Out_cclsInfo::Pipeline, pendingIndexRequests); MAKE_REFLECT_STRUCT(Out_cclsInfo::Pipeline, pendingIndexRequests);
MAKE_REFLECT_STRUCT(Out_cclsInfo::Project, entries); MAKE_REFLECT_STRUCT(Out_cclsInfo::Project, entries);
MAKE_REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project); MAKE_REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project);
} // namespace
struct Handler_cclsInfo : BaseMessageHandler<In_cclsInfo> { void MessageHandler::ccls_info(EmptyParam &, ReplyOnce &reply) {
MethodType GetMethodType() const override { return cclsInfo; }
void Run(In_cclsInfo *request) override {
Out_cclsInfo result; Out_cclsInfo result;
result.db.files = db->files.size(); result.db.files = db->files.size();
result.db.funcs = db->funcs.size(); result.db.funcs = db->funcs.size();
@ -47,27 +38,12 @@ struct Handler_cclsInfo : BaseMessageHandler<In_cclsInfo> {
result.project.entries = 0; result.project.entries = 0;
for (auto &[_, folder] : project->root2folder) for (auto &[_, folder] : project->root2folder)
result.project.entries += folder.entries.size(); result.project.entries += folder.entries.size();
pipeline::Reply(request->id, result); reply(result);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_cclsInfo);
struct In_cclsFileInfo : public RequestMessage { void MessageHandler::ccls_fileInfo(TextDocumentParam &param, ReplyOnce &reply) {
MethodType GetMethodType() const override { return fileInfo; } QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
struct Params { if (!file)
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<In_cclsFileInfo> {
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; return;
QueryFile::Def result; QueryFile::Def result;
@ -77,8 +53,6 @@ struct Handler_cclsFileInfo : BaseMessageHandler<In_cclsFileInfo> {
result.language = file->def->language; result.language = file->def->language;
result.includes = file->def->includes; result.includes = file->def->includes;
result.skipped_ranges = file->def->skipped_ranges; result.skipped_ranges = file->def->skipped_ranges;
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_cclsFileInfo);
} // namespace

View File

@ -2,25 +2,17 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
#include <unordered_set> #include <unordered_set>
namespace ccls {
namespace { namespace {
MethodType kMethodType = "$ccls/inheritance", struct Param : TextDocumentPositionParam {
implementation = "textDocument/implementation";
struct In_cclsInheritance : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
// If id+kind are specified, expand a node; otherwise textDocument+position // If id+kind are specified, expand a node; otherwise textDocument+position
// should be specified for building the root and |levels| of nodes below. // should be specified for building the root and |levels| of nodes below.
lsTextDocumentIdentifier textDocument;
lsPosition position;
Usr usr; Usr usr;
std::string id; std::string id;
SymbolKind kind = SymbolKind::Invalid; SymbolKind kind = SymbolKind::Invalid;
@ -30,13 +22,10 @@ struct In_cclsInheritance : public RequestMessage {
bool qualified = true; bool qualified = true;
int levels = 1; int levels = 1;
bool hierarchy = false; bool hierarchy = false;
} params;
}; };
MAKE_REFLECT_STRUCT(In_cclsInheritance::Params, textDocument, position, id, MAKE_REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified,
kind, derived, qualified, levels, hierarchy); levels, hierarchy);
MAKE_REFLECT_STRUCT(In_cclsInheritance, id, params);
REGISTER_IN_MESSAGE(In_cclsInheritance);
struct Out_cclsInheritance { struct Out_cclsInheritance {
Usr usr; Usr usr;
@ -118,85 +107,68 @@ bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
m->db->Type(entry->usr)); m->db->Type(entry->usr));
} }
struct Handler_cclsInheritance : BaseMessageHandler<In_cclsInheritance> { std::optional<Out_cclsInheritance> BuildInitial(MessageHandler *m, SymbolRef sym, bool derived,
MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_cclsInheritance> BuildInitial(SymbolRef sym, bool derived,
bool qualified, int levels) { bool qualified, int levels) {
Out_cclsInheritance entry; Out_cclsInheritance entry;
entry.id = std::to_string(sym.usr); entry.id = std::to_string(sym.usr);
entry.usr = sym.usr; entry.usr = sym.usr;
entry.kind = sym.kind; entry.kind = sym.kind;
Expand(this, &entry, derived, qualified, levels); Expand(m, &entry, derived, qualified, levels);
return entry; return entry;
} }
void Run(In_cclsInheritance *request) override { void Inheritance(MessageHandler *m, Param &param, ReplyOnce &reply) {
auto &params = request->params;
std::optional<Out_cclsInheritance> result; std::optional<Out_cclsInheritance> result;
if (params.id.size()) { if (param.id.size()) {
try { try {
params.usr = std::stoull(params.id); param.usr = std::stoull(param.id);
} catch (...) { } catch (...) {
return; return;
} }
result.emplace(); result.emplace();
result->id = std::to_string(params.usr); result->id = std::to_string(param.usr);
result->usr = params.usr; result->usr = param.usr;
result->kind = params.kind; result->kind = param.kind;
if (!(((params.kind == SymbolKind::Func && db->HasFunc(params.usr)) || if (!(((param.kind == SymbolKind::Func && m->db->HasFunc(param.usr)) ||
(params.kind == SymbolKind::Type && db->HasType(params.usr))) && (param.kind == SymbolKind::Type && m->db->HasType(param.usr))) &&
Expand(this, &*result, params.derived, params.qualified, Expand(m, &*result, param.derived, param.qualified,
params.levels))) param.levels)))
result.reset(); result.reset();
} else { } else {
QueryFile *file; QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath());
if (!FindFileOrFail(db, project, request->id, if (!file)
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = m->working_files->GetFileByFilename(file->def->path);
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position))
if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) { if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) {
result = BuildInitial(sym, params.derived, params.qualified, result = BuildInitial(m, sym, param.derived, param.qualified,
params.levels); param.levels);
break; break;
} }
} }
if (params.hierarchy) if (param.hierarchy)
pipeline::Reply(request->id, result); reply(result);
else { else {
auto out = FlattenHierarchy(result); auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out); reply(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<In_textDocumentImplementation> {
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);
}
};
REGISTER_MESSAGE_HANDLER(Handler_textDocumentImplementation);
} // namespace } // namespace
void MessageHandler::ccls_inheritance(Reader &reader, ReplyOnce &reply) {
Param param;
Reflect(reader, param);
Inheritance(this, param, reply);
}
void MessageHandler::textDocument_implementation(
TextDocumentPositionParam &param, ReplyOnce &reply) {
Param param1;
param1.textDocument = param.textDocument;
param1.position = param.position;
param1.derived = true;
Inheritance(this, param1, reply);
}
} // namespace ccls

View File

@ -3,7 +3,7 @@
#include "clang_tu.hh" #include "clang_tu.hh"
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
@ -12,22 +12,13 @@
#include <unordered_set> #include <unordered_set>
using namespace ccls; namespace ccls {
using namespace clang; using namespace clang;
namespace { namespace {
MethodType kMethodType = "$ccls/member"; struct Param : TextDocumentPositionParam {
struct In_cclsMember : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
// If id is specified, expand a node; otherwise textDocument+position should // If id is specified, expand a node; otherwise textDocument+position should
// be specified for building the root and |levels| of nodes below. // be specified for building the root and |levels| of nodes below.
lsTextDocumentIdentifier textDocument;
lsPosition position;
// Type
Usr usr; Usr usr;
std::string id; std::string id;
@ -37,13 +28,10 @@ struct In_cclsMember : public RequestMessage {
// instead of member variables. // instead of member variables.
SymbolKind kind = SymbolKind::Var; SymbolKind kind = SymbolKind::Var;
bool hierarchy = false; bool hierarchy = false;
} params;
}; };
MAKE_REFLECT_STRUCT(In_cclsMember::Params, textDocument, position, id, MAKE_REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind,
qualified, levels, kind, hierarchy); hierarchy);
MAKE_REFLECT_STRUCT(In_cclsMember, id, params);
REGISTER_IN_MESSAGE(In_cclsMember);
struct Out_cclsMember { struct Out_cclsMember {
Usr usr; Usr usr;
@ -221,17 +209,14 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
return true; return true;
} }
struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> { std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, SymbolKind kind,
MethodType GetMethodType() const override { return kMethodType; } Usr root_usr, bool qualified,
int levels, SymbolKind memberKind) {
std::optional<Out_cclsMember> BuildInitial(SymbolKind kind, Usr root_usr,
bool qualified, int levels,
SymbolKind memberKind) {
switch (kind) { switch (kind) {
default: default:
return {}; return {};
case SymbolKind::Func: { case SymbolKind::Func: {
const auto *def = db->Func(root_usr).AnyDef(); const auto *def = m->db->Func(root_usr).AnyDef();
if (!def) if (!def)
return {}; return {};
@ -239,19 +224,18 @@ struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
// Not type, |id| is invalid. // Not type, |id| is invalid.
entry.name = def->Name(qualified); entry.name = def->Name(qualified);
if (def->spell) { if (def->spell) {
if (std::optional<lsLocation> loc = if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
GetLsLocation(db, working_files, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
for (Usr usr : def->vars) { for (Usr usr : def->vars) {
auto &var = db->Var(usr); auto &var = m->db->Var(usr);
if (var.def.size()) if (var.def.size())
DoField(this, &entry, var, -1, qualified, levels - 1); DoField(m, &entry, var, -1, qualified, levels - 1);
} }
return entry; return entry;
} }
case SymbolKind::Type: { case SymbolKind::Type: {
const auto *def = db->Type(root_usr).AnyDef(); const auto *def = m->db->Type(root_usr).AnyDef();
if (!def) if (!def)
return {}; return {};
@ -259,51 +243,51 @@ struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
entry.id = std::to_string(root_usr); entry.id = std::to_string(root_usr);
entry.usr = root_usr; entry.usr = root_usr;
if (def->spell) { if (def->spell) {
if (std::optional<lsLocation> loc = if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
GetLsLocation(db, working_files, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
Expand(this, &entry, qualified, levels, memberKind); Expand(m, &entry, qualified, levels, memberKind);
return entry; return entry;
} }
} }
} }
} // namespace
void Run(In_cclsMember *request) override { void MessageHandler::ccls_member(Reader &reader, ReplyOnce &reply) {
auto &params = request->params; Param param;
Reflect(reader, param);
std::optional<Out_cclsMember> result; std::optional<Out_cclsMember> result;
if (params.id.size()) { if (param.id.size()) {
try { try {
params.usr = std::stoull(params.id); param.usr = std::stoull(param.id);
} catch (...) { } catch (...) {
return; return;
} }
result.emplace(); result.emplace();
result->id = std::to_string(params.usr); result->id = std::to_string(param.usr);
result->usr = params.usr; result->usr = param.usr;
// entry.name is empty as it is known by the client. // entry.name is empty as it is known by the client.
if (!(db->HasType(params.usr) && Expand(this, &*result, params.qualified, if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified,
params.levels, params.kind))) param.levels, param.kind)))
result.reset(); result.reset();
} else { } else {
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!FindFileOrFail(db, project, request->id, if (!file)
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
for (SymbolRef sym : for (SymbolRef sym :
FindSymbolsAtLocation(wfile, file, params.position)) { FindSymbolsAtLocation(wfile, file, param.position)) {
switch (sym.kind) { switch (sym.kind) {
case SymbolKind::Func: case SymbolKind::Func:
case SymbolKind::Type: case SymbolKind::Type:
result = BuildInitial(sym.kind, sym.usr, params.qualified, result = BuildInitial(this, sym.kind, sym.usr, param.qualified,
params.levels, params.kind); param.levels, param.kind);
break; break;
case SymbolKind::Var: { case SymbolKind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->GetVar(sym).AnyDef();
if (def && def->type) if (def && def->type)
result = BuildInitial(SymbolKind::Type, def->type, params.qualified, result = BuildInitial(this, SymbolKind::Type, def->type, param.qualified,
params.levels, params.kind); param.levels, param.kind);
break; break;
} }
default: default:
@ -313,14 +297,11 @@ struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
} }
} }
if (params.hierarchy) if (param.hierarchy)
pipeline::Reply(request->id, result); reply(result);
else { else {
auto out = FlattenHierarchy(result); auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out); reply(out);
} }
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_cclsMember);
} // namespace

View File

@ -1,25 +1,17 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
namespace ccls {
namespace { namespace {
MethodType kMethodType = "$ccls/navigate"; struct Param {
struct In_CclsNavigate : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
lsPosition position; lsPosition position;
std::string direction; std::string direction;
} params;
}; };
MAKE_REFLECT_STRUCT(In_CclsNavigate::Params, textDocument, position, direction); MAKE_REFLECT_STRUCT(Param, textDocument, position, direction);
MAKE_REFLECT_STRUCT(In_CclsNavigate, id, params);
REGISTER_IN_MESSAGE(In_CclsNavigate);
Maybe<Range> FindParent(QueryFile *file, Position pos) { Maybe<Range> FindParent(QueryFile *file, Position pos) {
Maybe<Range> parent; Maybe<Range> parent;
@ -32,27 +24,26 @@ Maybe<Range> FindParent(QueryFile *file, Position pos) {
parent = sym.extent; parent = sym.extent;
return parent; return parent;
} }
} // namespace
struct Handler_CclsNavigate : BaseMessageHandler<In_CclsNavigate> { void MessageHandler::ccls_navigate(Reader &reader,
MethodType GetMethodType() const override { return kMethodType; } ReplyOnce &reply) {
void Run(In_CclsNavigate *request) override { Param param;
auto &params = request->params; Reflect(reader, param);
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!FindFileOrFail(db, project, request->id, if (!file)
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
working_files->GetFileByFilename(file->def->path); lsPosition ls_pos = param.position;
lsPosition ls_pos = request->params.position;
if (wfile && wfile->index_lines.size()) if (wfile && wfile->index_lines.size())
if (auto line = wfile->GetIndexPosFromBufferPos( if (auto line = wfile->GetIndexPosFromBufferPos(ls_pos.line,
ls_pos.line, &ls_pos.character, false)) &ls_pos.character, false))
ls_pos.line = *line; ls_pos.line = *line;
Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character}; Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
Maybe<Range> res; Maybe<Range> res;
switch (params.direction[0]) { switch (param.direction[0]) {
case 'D': { case 'D': {
Maybe<Range> parent = FindParent(file, pos); Maybe<Range> parent = FindParent(file, pos);
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
@ -96,11 +87,9 @@ struct Handler_CclsNavigate : BaseMessageHandler<In_CclsNavigate> {
if (res) if (res)
if (auto ls_range = GetLsRange(wfile, *res)) { if (auto ls_range = GetLsRange(wfile, *res)) {
lsLocation &ls_loc = result.emplace_back(); lsLocation &ls_loc = result.emplace_back();
ls_loc.uri = params.textDocument.uri; ls_loc.uri = param.textDocument.uri;
ls_loc.range = *ls_range; ls_loc.range = *ls_range;
} }
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_CclsNavigate);
} // namespace

View File

@ -3,80 +3,34 @@
#include "clang_complete.hh" #include "clang_complete.hh"
#include "match.h" #include "match.h"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.h" #include "project.hh"
#include "working_files.h" #include "working_files.h"
#include <queue> #include <queue>
#include <unordered_set> #include <unordered_set>
using namespace ccls; namespace ccls {
namespace { namespace {
MethodType kMethodType = "$ccls/reload"; struct Param {
struct In_cclsReload : public NotificationMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
bool dependencies = true; bool dependencies = true;
std::vector<std::string> whitelist; std::vector<std::string> whitelist;
std::vector<std::string> blacklist; std::vector<std::string> blacklist;
} params;
}; };
MAKE_REFLECT_STRUCT(In_cclsReload::Params, dependencies, whitelist, blacklist); MAKE_REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
MAKE_REFLECT_STRUCT(In_cclsReload, params); } // namespace
REGISTER_IN_MESSAGE(In_cclsReload);
struct Handler_cclsReload : BaseMessageHandler<In_cclsReload> { void MessageHandler::ccls_reload(Reader &reader) {
MethodType GetMethodType() const override { return kMethodType; } Param param;
void Run(In_cclsReload *request) override { Reflect(reader, param);
const auto &params = request->params;
// Send index requests for every file. // Send index requests for every file.
if (params.whitelist.empty() && params.blacklist.empty()) { if (param.whitelist.empty() && param.blacklist.empty()) {
vfs->Clear(); vfs->Clear();
db->clear(); db->clear();
project->Index(working_files, lsRequestId()); project->Index(working_files, lsRequestId());
clang_complete->FlushAllSessions(); clang_complete->FlushAllSessions();
return; return;
} }
// TODO
GroupMatch matcher(params.whitelist, params.blacklist);
std::queue<const QueryFile *> q;
// |need_index| stores every filename ever enqueued.
std::unordered_set<std::string> need_index;
// Reverse dependency graph.
std::unordered_map<std::string, std::vector<std::string>> graph;
// filename -> QueryFile mapping for files haven't enqueued.
std::unordered_map<std::string, const QueryFile *> 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);
} }
} // namespace ccls
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);
} // namespace

View File

@ -1,43 +1,32 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
namespace ccls {
namespace { namespace {
MethodType kMethodType = "$ccls/vars"; struct Param : TextDocumentPositionParam {
struct In_cclsVars : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params : lsTextDocumentPositionParams {
// 1: field // 1: field
// 2: local // 2: local
// 4: parameter // 4: parameter
unsigned kind = ~0u; unsigned kind = ~0u;
} params;
}; };
MAKE_REFLECT_STRUCT(In_cclsVars::Params, textDocument, position, kind); MAKE_REFLECT_STRUCT(Param, textDocument, position, kind);
MAKE_REFLECT_STRUCT(In_cclsVars, id, params); } // namespace
REGISTER_IN_MESSAGE(In_cclsVars);
struct Handler_cclsVars : BaseMessageHandler<In_cclsVars> { void MessageHandler::ccls_vars(Reader &reader, ReplyOnce &reply) {
MethodType GetMethodType() const override { return kMethodType; } Param param;
Reflect(reader, param);
void Run(In_cclsVars *request) override { QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
auto &params = request->params; if (!file)
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *working_file = working_files->GetFileByFilename(file->def->path);
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
std::vector<lsLocation> result; std::vector<lsLocation> result;
for (SymbolRef sym : for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, params.position)) { FindSymbolsAtLocation(working_file, file, param.position)) {
Usr usr = sym.usr; Usr usr = sym.usr;
switch (sym.kind) { switch (sym.kind) {
default: default:
@ -52,12 +41,10 @@ struct Handler_cclsVars : BaseMessageHandler<In_cclsVars> {
case SymbolKind::Type: case SymbolKind::Type:
result = GetLsLocations( result = GetLsLocations(
db, working_files, db, working_files,
GetVarDeclarations(db, db->Type(usr).instances, params.kind)); GetVarDeclarations(db, db->Type(usr).instances, param.kind));
break; break;
} }
} }
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_cclsVars);
} // namespace

View File

@ -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<InMessage> request) override { exit(0); }
};
REGISTER_MESSAGE_HANDLER(Handler_Exit);
} // namespace

View File

@ -5,20 +5,21 @@
#include "filesystem.hh" #include "filesystem.hh"
#include "include_complete.h" #include "include_complete.h"
#include "log.hh" #include "log.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.h" #include "platform.h"
#include "project.h" #include "project.hh"
#include "serializers/json.h" #include "serializers/json.h"
#include "working_files.h" #include "working_files.h"
#include <llvm/ADT/Twine.h> #include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
#include <stdlib.h>
#include <stdexcept> #include <stdexcept>
#include <thread> #include <thread>
using namespace ccls; namespace ccls {
using namespace llvm; using namespace llvm;
// TODO Cleanup global variables // TODO Cleanup global variables
@ -26,8 +27,6 @@ extern std::string g_init_options;
namespace { namespace {
MethodType kMethodType = "initialize";
// Code Lens options. // Code Lens options.
struct lsCodeLensOptions { struct lsCodeLensOptions {
// Code lens has a resolve provider as well. // Code lens has a resolve provider as well.
@ -316,7 +315,7 @@ struct lsInitializeParams {
// The initial trace setting. If omitted trace is disabled ('off'). // The initial trace setting. If omitted trace is disabled ('off').
lsTrace trace = lsTrace::Off; lsTrace trace = lsTrace::Off;
std::vector<lsWorkspaceFolder> workspaceFolders; std::vector<WorkspaceFolder> workspaceFolders;
}; };
void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) { void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
@ -336,14 +335,6 @@ void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
MAKE_REFLECT_STRUCT(lsInitializeParams, rootUri, initializationOptions, MAKE_REFLECT_STRUCT(lsInitializeParams, rootUri, initializationOptions,
capabilities, trace, workspaceFolders); 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 { struct lsInitializeResult {
lsServerCapabilities capabilities; lsServerCapabilities capabilities;
}; };
@ -361,20 +352,15 @@ void *Indexer(void *arg_) {
h->working_files); h->working_files);
return nullptr; return nullptr;
} }
} // namespace
struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> { void Initialize(MessageHandler *m, lsInitializeParams &param, ReplyOnce &reply) {
MethodType GetMethodType() const override { return kMethodType; } std::string project_path = NormalizePath(param.rootUri->GetPath());
void Run(In_InitializeRequest *request) override {
auto &params = request->params;
if (!params.rootUri)
return;
std::string project_path = NormalizePath(params.rootUri->GetPath());
LOG_S(INFO) << "initialize in directory " << project_path << " with uri " LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
<< params.rootUri->raw_uri; << param.rootUri->raw_uri;
{ {
g_config = new Config(params.initializationOptions); g_config = new Config(param.initializationOptions);
rapidjson::Document reader; rapidjson::Document reader;
reader.Parse(g_init_options.c_str()); reader.Parse(g_init_options.c_str());
if (!reader.HasParseError()) { if (!reader.HasParseError()) {
@ -400,49 +386,49 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
} }
// Client capabilities // Client capabilities
const auto &capabilities = params.capabilities; const auto &capabilities = param.capabilities;
g_config->client.snippetSupport &= g_config->client.snippetSupport &=
capabilities.textDocument.completion.completionItem.snippetSupport; capabilities.textDocument.completion.completionItem.snippetSupport;
g_config->client.hierarchicalDocumentSymbolSupport &= g_config->client.hierarchicalDocumentSymbolSupport &=
capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport; capabilities.textDocument.documentSymbol
.hierarchicalDocumentSymbolSupport;
// Ensure there is a resource directory. // Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty()) if (g_config->clang.resourceDir.empty())
g_config->clang.resourceDir = GetDefaultResourceDirectory(); g_config->clang.resourceDir = GetDefaultResourceDirectory();
DoPathMapping(g_config->clang.resourceDir); DoPathMapping(g_config->clang.resourceDir);
LOG_S(INFO) << "Using -resource-dir=" << 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 // Send initialization before starting indexers, so we don't send a
// status update too early. // status update too early.
{ {
lsInitializeResult result; lsInitializeResult result;
pipeline::Reply(request->id, result); reply(result);
} }
// Set project root. // Set project root.
EnsureEndsInSlash(project_path); EnsureEndsInSlash(project_path);
g_config->fallbackFolder = project_path; g_config->fallbackFolder = project_path;
for (const lsWorkspaceFolder &wf : request->params.workspaceFolders) { for (const WorkspaceFolder &wf : param.workspaceFolders) {
std::string path = wf.uri.GetPath(); std::string path = wf.uri.GetPath();
EnsureEndsInSlash(path); EnsureEndsInSlash(path);
g_config->workspaceFolders.push_back(path); g_config->workspaceFolders.push_back(path);
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path; LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
} }
if (request->params.workspaceFolders.empty()) if (param.workspaceFolders.empty())
g_config->workspaceFolders.push_back(project_path); g_config->workspaceFolders.push_back(project_path);
if (g_config->cacheDirectory.size()) if (g_config->cacheDirectory.size())
for (const std::string &folder : g_config->workspaceFolders) { for (const std::string &folder : g_config->workspaceFolders) {
// Create two cache directories for files inside and outside of the // Create two cache directories for files inside and outside of the
// project. // project.
std::string escaped = std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
EscapeFileName(folder.substr(0, folder.size() - 1));
sys::fs::create_directories(g_config->cacheDirectory + escaped); sys::fs::create_directories(g_config->cacheDirectory + escaped);
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped); sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
} }
idx::Init(); idx::Init();
for (const std::string &folder : g_config->workspaceFolders) for (const std::string &folder : g_config->workspaceFolders)
project->Load(folder); m->project->Load(folder);
// Start indexer threads. Start this after loading the project, as that // Start indexer threads. Start this after loading the project, as that
// may take a long time. Indexer threads will emit status/progress // may take a long time. Indexer threads will emit status/progress
@ -452,29 +438,38 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
LOG_S(INFO) << "start " << g_config->index.threads << " indexers"; LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; i++) for (int i = 0; i < g_config->index.threads; i++)
SpawnThread(Indexer, new std::pair<MessageHandler *, int>{this, i}); SpawnThread(Indexer, new std::pair<MessageHandler *, int>{m, i});
// Start scanning include directories before dispatching project // Start scanning include directories before dispatching project
// files, because that takes a long time. // files, because that takes a long time.
include_complete->Rescan(); m->include_complete->Rescan();
LOG_S(INFO) << "dispatch initial index requests"; LOG_S(INFO) << "dispatch initial index requests";
project->Index(working_files, request->id); m->project->Index(m->working_files, reply.id);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_Initialize);
} // namespace
void StandaloneInitialize(const std::string &root, Project &project, void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) {
WorkingFiles &wfiles, VFS &vfs, lsInitializeParams param;
IncludeComplete &complete) { Reflect(reader, param);
Handler_Initialize handler; if (!param.rootUri)
handler.project = &project; return;
handler.working_files = &wfiles; Initialize(this, param, reply);
handler.vfs = &vfs;
handler.include_complete = &complete;
In_InitializeRequest request;
request.params.rootUri = lsDocumentUri::FromPath(root);
handler.Run(&request);
} }
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

View File

@ -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<In_Shutdown> {
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

View File

@ -0,0 +1,222 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h"
#include "serializers/json.h"
#include <llvm/Support/FormatVariadic.h>
#include <unordered_set>
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 &param,
ReplyOnce &reply) {
WorkingFile *wfile =
working_files->GetFileByFilename(param.textDocument.uri.GetPath());
if (!wfile) {
return;
}
std::vector<CodeAction> result;
std::vector<lsDiagnostic> 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<std::string> arguments;
};
struct lsCodeLens {
lsRange range;
std::optional<lsCommand> command;
};
MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field);
MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments);
MAKE_REFLECT_STRUCT(lsCodeLens, range, command);
template <typename T>
std::string ToString(T &v) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer);
Reflect(json_writer, v);
return output.GetString();
}
struct CommonCodeLensParams {
std::vector<lsCodeLens> *result;
DB *db;
WorkingFile *wfile;
};
} // namespace
void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
ReplyOnce &reply) {
std::vector<lsCodeLens> 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<lsRange> 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<Range> 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<Use> base_uses = GetUsesForAllBases(db, func);
std::vector<Use> 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<lsLocation> 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

View File

@ -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<lsDiagnostic> 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<In_TextDocumentCodeAction> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentCodeAction *request) override {
const auto &params = request->params;
WorkingFile *wfile =
working_files->GetFileByFilename(params.textDocument.uri.GetPath());
if (!wfile)
return;
std::vector<lsCodeAction> result;
std::vector<lsDiagnostic> 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

View File

@ -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 <llvm/Support/FormatVariadic.h>
#include <unordered_set>
using namespace ccls;
namespace {
const MethodType codeLens = "textDocument/codeLens",
executeCommand = "workspace/executeCommand";
struct lsCommand {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments);
struct lsCodeLens {
lsRange range;
std::optional<lsCommand> 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 <typename T>
std::string ToString(T &v) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer);
Reflect(json_writer, v);
return output.GetString();
}
struct CommonCodeLensParams {
std::vector<lsCodeLens> *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<In_TextDocumentCodeLens> {
MethodType GetMethodType() const override { return codeLens; }
void Run(In_TextDocumentCodeLens *request) override {
auto &params = request->params;
std::vector<lsCodeLens> 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<lsRange> 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<Range> 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<Use> base_uses = GetUsesForAllBases(db, func);
std::vector<Use> 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<In_WorkspaceExecuteCommand> {
MethodType GetMethodType() const override { return executeCommand; }
void Run(In_WorkspaceExecuteCommand *request) override {
const auto &params = 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<lsLocation> 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

View File

@ -5,7 +5,7 @@
#include "fuzzy_match.h" #include "fuzzy_match.h"
#include "include_complete.h" #include "include_complete.h"
#include "log.hh" #include "log.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "working_files.h" #include "working_files.h"
@ -14,62 +14,23 @@
#include <regex> #include <regex>
using namespace ccls; namespace ccls {
using namespace clang; using namespace clang;
using namespace llvm; 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<std::string> 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 { struct lsCompletionList {
// This list it not complete. Further typing should result in recomputing
// this list.
bool isIncomplete = false; bool isIncomplete = false;
// The completion items.
std::vector<lsCompletionItem> items; std::vector<lsCompletionItem> 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); MAKE_REFLECT_STRUCT(lsCompletionList, isIncomplete, items);
namespace {
void DecorateIncludePaths(const std::smatch &match, void DecorateIncludePaths(const std::smatch &match,
std::vector<lsCompletionItem> *items) { std::vector<lsCompletionItem> *items) {
std::string spaces_after_include = " "; std::string spaces_after_include = " ";
@ -473,40 +434,33 @@ public:
CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionAllocator &getAllocator() override { return *Alloc; }
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
}; };
} // namespace
struct Handler_TextDocumentCompletion void MessageHandler::textDocument_completion(lsCompletionParams &param,
: BaseMessageHandler<In_TextDocumentComplete> { ReplyOnce &reply) {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentComplete *request) override {
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache; static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
const auto &params = request->params;
lsCompletionList result; lsCompletionList result;
std::string path = param.textDocument.uri.GetPath();
std::string path = params.textDocument.uri.GetPath();
WorkingFile *file = working_files->GetFileByFilename(path); WorkingFile *file = working_files->GetFileByFilename(path);
if (!file) { if (!file) {
pipeline::Reply(request->id, result);
return; return;
} }
// It shouldn't be possible, but sometimes vscode will send queries out // It shouldn't be possible, but sometimes vscode will send queries out
// of order, ie, we get completion request before buffer content update. // of order, ie, we get completion request before buffer content update.
std::string buffer_line; std::string buffer_line;
if (params.position.line >= 0 && if (param.position.line >= 0 &&
params.position.line < file->buffer_lines.size()) param.position.line < file->buffer_lines.size())
buffer_line = file->buffer_lines[params.position.line]; buffer_line = file->buffer_lines[param.position.line];
// Check for - and : before completing -> or ::, since vscode does not // Check for - and : before completing -> or ::, since vscode does not
// support multi-character trigger characters. // support multi-character trigger characters.
if (params.context.triggerKind == if (param.context.triggerKind == lsCompletionTriggerKind::TriggerCharacter &&
lsCompletionTriggerKind::TriggerCharacter && param.context.triggerCharacter) {
params.context.triggerCharacter) {
bool did_fail_check = false; bool did_fail_check = false;
std::string character = *params.context.triggerCharacter; std::string character = *param.context.triggerCharacter;
int preceding_index = params.position.character - 2; int preceding_index = param.position.character - 2;
// If the character is '"', '<' or '/', make sure that the line starts // If the character is '"', '<' or '/', make sure that the line starts
// with '#'. // with '#'.
@ -532,15 +486,15 @@ struct Handler_TextDocumentCompletion
} }
if (did_fail_check) { if (did_fail_check) {
pipeline::Reply(request->id, result); reply(result);
return; return;
} }
} }
std::string completion_text; std::string completion_text;
lsPosition end_pos = params.position; lsPosition end_pos = param.position;
lsPosition begin_pos = file->FindStableCompletionSource( lsPosition begin_pos = file->FindStableCompletionSource(
params.position, &completion_text, &end_pos); param.position, &completion_text, &end_pos);
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
@ -561,12 +515,12 @@ struct Handler_TextDocumentCompletion
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos, FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
buffer_line); buffer_line);
DecorateIncludePaths(preprocess.match, &result.items); DecorateIncludePaths(preprocess.match, &result.items);
pipeline::Reply(request->id, result); reply(result);
} else { } else {
std::string path = params.textDocument.uri.GetPath(); std::string path = param.textDocument.uri.GetPath();
CompletionManager::OnComplete callback = CompletionManager::OnComplete callback =
[completion_text, path, begin_pos, end_pos, [completion_text, path, begin_pos, end_pos, reply,
id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) { buffer_line](CodeCompleteConsumer *OptConsumer) {
if (!OptConsumer) if (!OptConsumer)
return; return;
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer); auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
@ -575,7 +529,7 @@ struct Handler_TextDocumentCompletion
FilterCandidates(result, completion_text, begin_pos, end_pos, FilterCandidates(result, completion_text, begin_pos, end_pos,
buffer_line); buffer_line);
pipeline::Reply(id, result); reply(result);
if (!Consumer->from_cache) { if (!Consumer->from_cache) {
cache.WithLock([&]() { cache.WithLock([&]() {
cache.path = path; cache.path = path;
@ -599,13 +553,10 @@ struct Handler_TextDocumentCompletion
} else { } else {
clang_complete->completion_request_.PushBack( clang_complete->completion_request_.PushBack(
std::make_unique<CompletionManager::CompletionRequest>( std::make_unique<CompletionManager::CompletionRequest>(
request->id, params.textDocument, begin_pos, reply.id, param.textDocument, begin_pos,
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts, std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
callback)); callback));
} }
} }
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCompletion);
} // namespace

View File

@ -1,26 +1,15 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
#include <ctype.h> #include <ctype.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
using namespace ccls; namespace ccls {
namespace { 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<Use> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { std::vector<Use> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) {
switch (sym.kind) { switch (sym.kind) {
case SymbolKind::Var: { case SymbolKind::Var: {
@ -42,22 +31,19 @@ std::vector<Use> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) {
return GetNonDefDeclarations(db, sym); return GetNonDefDeclarations(db, sym);
} }
} }
} // namespace
struct Handler_TextDocumentDefinition void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
: BaseMessageHandler<In_TextDocumentDefinition> { ReplyOnce &reply) {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentDefinition *request) override {
auto &params = request->params;
int file_id; int file_id;
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!FindFileOrFail(db, project, request->id, if (!file)
params.textDocument.uri.GetPath(), &file, &file_id))
return; return;
std::vector<lsLocation> result; std::vector<lsLocation> result;
Maybe<Use> on_def; Maybe<Use> on_def;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsPosition &ls_pos = params.position; lsPosition &ls_pos = param.position;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) { for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
// Special cases which are handled: // Special cases which are handled:
@ -109,7 +95,7 @@ struct Handler_TextDocumentDefinition
} }
// Find the best match of the identifier at point. // Find the best match of the identifier at point.
if (!range) { if (!range) {
lsPosition position = request->params.position; lsPosition position = param.position;
const std::string &buffer = wfile->buffer_content; const std::string &buffer = wfile->buffer_content;
std::string_view query = LexIdentifierAroundPos(position, buffer); std::string_view query = LexIdentifierAroundPos(position, buffer);
std::string_view short_query = query; std::string_view short_query = query;
@ -134,8 +120,7 @@ struct Handler_TextDocumentDefinition
return; return;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) { if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
std::tuple<int, int, bool, int> score{ std::tuple<int, int, bool, int> score{
int(name.size() - short_query.size()), 0, int(name.size() - short_query.size()), 0, dr->file_id != file_id,
dr->file_id != file_id,
std::abs(dr->range.start.line - position.line)}; std::abs(dr->range.start.line - position.line)};
// Update the score with qualified name if the qualified name // Update the score with qualified name if the qualified name
// occurs in |name|. // occurs in |name|.
@ -167,8 +152,52 @@ struct Handler_TextDocumentDefinition
} }
} }
pipeline::Reply(request->id, result); reply(result);
} }
void MessageHandler::textDocument_typeDefinition(
TextDocumentPositionParam &param, ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file)
return;
WorkingFile *working_file = working_files->GetFileByFilename(file->def->path);
std::vector<lsLocation> 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);
}; };
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDefinition); for (SymbolRef sym :
} // namespace 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;
}
break;
}
default:
break;
}
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
reply(result);
}
} // namespace ccls

View File

@ -3,148 +3,56 @@
#include "clang_complete.hh" #include "clang_complete.hh"
#include "include_complete.h" #include "include_complete.h"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.h" #include "project.hh"
#include "working_files.h" #include "working_files.h"
using namespace ccls;
namespace { namespace ccls {
MethodType didChange = "textDocument/didChange"; void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam &param) {
MethodType didClose = "textDocument/didClose"; std::string path = param.textDocument.uri.GetPath();
MethodType didOpen = "textDocument/didOpen"; working_files->OnChange(param);
MethodType didSave = "textDocument/didSave";
struct In_TextDocumentDidChange : public NotificationMessage {
MethodType GetMethodType() const override { return didChange; }
lsTextDocumentDidChangeParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentDidChange, params);
REGISTER_IN_MESSAGE(In_TextDocumentDidChange);
struct Handler_TextDocumentDidChange
: BaseMessageHandler<In_TextDocumentDidChange> {
MethodType GetMethodType() const override { return didChange; }
void Run(In_TextDocumentDidChange *request) override {
const auto &params = request->params;
std::string path = params.textDocument.uri.GetPath();
working_files->OnChange(params);
if (g_config->index.onChange) if (g_config->index.onChange)
pipeline::Index(path, {}, IndexMode::OnChange); pipeline::Index(path, {}, IndexMode::OnChange);
clang_complete->NotifyView(path); clang_complete->NotifyView(path);
if (g_config->diagnostics.onChange >= 0) if (g_config->diagnostics.onChange >= 0)
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange); clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);
struct In_TextDocumentDidClose : public NotificationMessage { void MessageHandler::textDocument_didClose(TextDocumentParam &param) {
MethodType GetMethodType() const override { return didClose; } std::string path = param.textDocument.uri.GetPath();
struct Params { working_files->OnClose(param.textDocument);
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<In_TextDocumentDidClose> {
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); clang_complete->OnClose(path);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);
struct In_TextDocumentDidOpen : public NotificationMessage { void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam &param) {
MethodType GetMethodType() const override { return didOpen; } std::string path = param.textDocument.uri.GetPath();
WorkingFile *working_file = working_files->OnOpen(param.textDocument);
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<std::string> 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<In_TextDocumentDidOpen> {
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 &params = request->params;
const std::string &path = params.textDocument.uri.GetPath();
WorkingFile *working_file = working_files->OnOpen(params.textDocument);
if (std::optional<std::string> cached_file_contents = if (std::optional<std::string> cached_file_contents =
pipeline::LoadIndexedContent(path)) pipeline::LoadIndexedContent(path))
working_file->SetIndexContent(*cached_file_contents); working_file->SetIndexContent(*cached_file_contents);
QueryFile *file = nullptr; ReplyOnce reply;
FindFileOrFail(db, project, std::nullopt, path, &file); QueryFile *file = FindFile(reply, path);
if (file) { if (file) {
EmitSkippedRanges(working_file, *file); EmitSkippedRanges(working_file, *file);
EmitSemanticHighlight(db, working_file, *file); EmitSemanticHighlight(db, working_file, *file);
} }
include_complete->AddFile(working_file->filename); include_complete->AddFile(working_file->filename);
std::vector<const char *> 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 // Submit new index request if it is not a header file or there is no
// pending index request. // pending index request.
std::pair<LanguageId, bool> lang = lookupExtension(path); std::pair<LanguageId, bool> lang = lookupExtension(path);
if ((lang.first != LanguageId::Unknown && !lang.second) || if ((lang.first != LanguageId::Unknown && !lang.second) ||
!pipeline::pending_index_requests) !pipeline::pending_index_requests)
pipeline::Index(path, args, IndexMode::Normal); pipeline::Index(path, {}, IndexMode::Normal);
clang_complete->NotifyView(path); clang_complete->NotifyView(path);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
struct In_TextDocumentDidSave : public NotificationMessage { void MessageHandler::textDocument_didSave(TextDocumentParam &param) {
MethodType GetMethodType() const override { return didSave; } const std::string &path = param.textDocument.uri.GetPath();
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<In_TextDocumentDidSave> {
MethodType GetMethodType() const override { return didSave; }
void Run(In_TextDocumentDidSave *request) override {
const auto &params = request->params;
const std::string &path = params.textDocument.uri.GetPath();
pipeline::Index(path, {}, IndexMode::Normal); pipeline::Index(path, {}, IndexMode::Normal);
clang_complete->NotifySave(path); clang_complete->NotifySave(path);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidSave);
} // namespace

View File

@ -1,21 +1,19 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
#include <algorithm> #include <algorithm>
MAKE_HASHABLE(SymbolIdx, t.usr, t.kind); MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
namespace ccls { namespace ccls {
namespace { MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName);
MethodType documentHighlight = "textDocument/documentHighlight",
documentLink = "textDocument/documentLink",
documentSymbol = "textDocument/documentSymbol";
struct lsDocumentHighlight { namespace {
struct DocumentHighlight {
enum Kind { Text = 1, Read = 2, Write = 3 }; enum Kind { Text = 1, Read = 2, Write = 3 };
lsRange range; lsRange range;
@ -24,35 +22,25 @@ struct lsDocumentHighlight {
// ccls extension // ccls extension
Role role = Role::None; 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; 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 { void MessageHandler::textDocument_documentHighlight(
MethodType GetMethodType() const override { return documentHighlight; } TextDocumentPositionParam &param, ReplyOnce &reply) {
lsTextDocumentPositionParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentDocumentHighlight, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentDocumentHighlight);
struct Handler_TextDocumentDocumentHighlight
: BaseMessageHandler<In_TextDocumentDocumentHighlight> {
MethodType GetMethodType() const override { return documentHighlight; }
void Run(In_TextDocumentDocumentHighlight *request) override {
int file_id; int file_id;
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!FindFileOrFail(db, project, request->id, if (!file)
request->params.textDocument.uri.GetPath(), &file,
&file_id))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
std::vector<lsDocumentHighlight> result; std::vector<DocumentHighlight> result;
std::vector<SymbolRef> syms = std::vector<SymbolRef> syms =
FindSymbolsAtLocation(wfile, file, request->params.position, true); FindSymbolsAtLocation(wfile, file, param.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0) if (refcnt <= 0)
continue; continue;
@ -63,70 +51,52 @@ struct Handler_TextDocumentDocumentHighlight
})) }))
continue; continue;
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
lsDocumentHighlight highlight; DocumentHighlight highlight;
highlight.range = loc->range; highlight.range = loc->range;
if (sym.role & Role::Write) if (sym.role & Role::Write)
highlight.kind = lsDocumentHighlight::Write; highlight.kind = DocumentHighlight::Write;
else if (sym.role & Role::Read) else if (sym.role & Role::Read)
highlight.kind = lsDocumentHighlight::Read; highlight.kind = DocumentHighlight::Read;
else else
highlight.kind = lsDocumentHighlight::Text; highlight.kind = DocumentHighlight::Text;
highlight.role = sym.role; highlight.role = sym.role;
result.push_back(highlight); result.push_back(highlight);
} }
} }
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
pipeline::Reply(request->id, result); reply(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);
namespace {
struct lsDocumentLink { struct lsDocumentLink {
lsRange range; lsRange range;
lsDocumentUri target; lsDocumentUri target;
}; };
MAKE_REFLECT_STRUCT(lsDocumentLink, range, target); MAKE_REFLECT_STRUCT(lsDocumentLink, range, target);
} // namespace
struct Handler_textDocumentDocumentLink void MessageHandler::textDocument_documentLink(TextDocumentParam &param,
: BaseMessageHandler<In_textDocumentDocumentLink> { ReplyOnce &reply) {
MethodType GetMethodType() const override { return documentLink; } QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
void Run(In_textDocumentDocumentLink *request) override { if (!file)
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file))
return; return;
std::vector<lsDocumentLink> result; std::vector<lsDocumentLink> result;
for (const IndexInclude &include : file->def->includes) for (const IndexInclude &include : file->def->includes)
result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}}, result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}},
lsDocumentUri::FromPath(include.resolved_path)}); lsDocumentUri::FromPath(include.resolved_path)});
pipeline::Reply(request->id, result); reply(result);
} } // namespace ccls
};
REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentLink);
struct In_textDocumentDocumentSymbol : public RequestMessage { namespace {
MethodType GetMethodType() const override { return documentSymbol; } struct DocumentSymbolParam : TextDocumentParam {
struct Params {
lsTextDocumentIdentifier textDocument;
// false: outline; true: all symbols // false: outline; true: all symbols
bool all = false; bool all = false;
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output. // If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
int startLine = -1; int startLine = -1;
int endLine = -1; int endLine = -1;
} params;
}; };
MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol::Params, textDocument, all, MAKE_REFLECT_STRUCT(DocumentSymbolParam, textDocument, all, startLine, endLine);
startLine, endLine);
MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol, id, params);
REGISTER_IN_MESSAGE(In_textDocumentDocumentSymbol);
struct lsDocumentSymbol { struct lsDocumentSymbol {
std::string name; std::string name;
@ -155,36 +125,35 @@ template<>
bool Ignore(const QueryVar::Def *def) { bool Ignore(const QueryVar::Def *def) {
return !def || def->is_local(); return !def || def->is_local();
} }
} // namespace
struct Handler_textDocumentDocumentSymbol void MessageHandler::textDocument_documentSymbol(Reader &reader,
: BaseMessageHandler<In_textDocumentDocumentSymbol> { ReplyOnce &reply) {
MethodType GetMethodType() const override { return documentSymbol; } DocumentSymbolParam param;
void Run(In_textDocumentDocumentSymbol *request) override { Reflect(reader, param);
auto &params = request->params;
QueryFile *file;
int file_id; int file_id;
if (!FindFileOrFail(db, project, request->id, QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
params.textDocument.uri.GetPath(), &file, &file_id)) if (!file)
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!wfile) if (!wfile)
return; return;
if (params.startLine >= 0) { if (param.startLine >= 0) {
std::vector<lsRange> result; std::vector<lsRange> result;
for (auto [sym, refcnt] : file->symbol2refcnt) for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && (params.all || sym.extent.Valid()) && if (refcnt > 0 && (param.all || sym.extent.Valid()) &&
params.startLine <= sym.range.start.line && param.startLine <= sym.range.start.line &&
sym.range.start.line <= params.endLine) sym.range.start.line <= param.endLine)
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) if (auto loc = GetLsLocation(db, working_files, sym, file_id))
result.push_back(loc->range); result.push_back(loc->range);
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
pipeline::Reply(request->id, result); reply(result);
} else if (g_config->client.hierarchicalDocumentSymbolSupport) { } else if (g_config->client.hierarchicalDocumentSymbolSupport) {
std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds; std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds;
std::vector<std::pair<std::vector<const void *>, lsDocumentSymbol *>> std::vector<std::pair<std::vector<const void *>, lsDocumentSymbol *>> funcs,
funcs, types; types;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid()) if (refcnt <= 0 || !sym.extent.Valid())
continue; continue;
@ -215,7 +184,7 @@ struct Handler_textDocumentDocumentSymbol
def_ptrs.push_back(&def); def_ptrs.push_back(&def);
} }
}); });
if (def_ptrs.empty() || !(params.all || sym.role & Role::Definition || if (def_ptrs.empty() || !(param.all || sym.role & Role::Definition ||
ds->kind == lsSymbolKind::Namespace)) { ds->kind == lsSymbolKind::Namespace)) {
ds.reset(); ds.reset();
continue; continue;
@ -256,19 +225,18 @@ struct Handler_textDocumentDocumentSymbol
for (auto &[_, ds] : sym2ds) for (auto &[_, ds] : sym2ds)
if (ds) if (ds)
result.push_back(std::move(ds)); result.push_back(std::move(ds));
pipeline::Reply(request->id, result); reply(result);
} else { } else {
std::vector<lsSymbolInformation> result; std::vector<lsSymbolInformation> result;
for (auto [sym, refcnt] : file->symbol2refcnt) { for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid() || if (refcnt <= 0 || !sym.extent.Valid() ||
!(params.all || sym.role & Role::Definition)) !(param.all || sym.role & Role::Definition))
continue; continue;
if (std::optional<lsSymbolInformation> info = if (std::optional<lsSymbolInformation> info =
GetSymbolInfo(db, sym, false)) { GetSymbolInfo(db, sym, false)) {
if ((sym.kind == SymbolKind::Type && if ((sym.kind == SymbolKind::Type &&
Ignore(db->GetType(sym).AnyDef())) || Ignore(db->GetType(sym).AnyDef())) ||
(sym.kind == SymbolKind::Var && (sym.kind == SymbolKind::Var && Ignore(db->GetVar(sym).AnyDef())))
Ignore(db->GetVar(sym).AnyDef())))
continue; continue;
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
info->location = *loc; info->location = *loc;
@ -276,10 +244,7 @@ struct Handler_textDocumentDocumentSymbol
} }
} }
} }
pipeline::Reply(request->id, result); reply(result);
} }
} }
};
REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentSymbol);
} // namespace
} // namespace ccls } // namespace ccls

View File

@ -1,43 +1,28 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.h" #include "project.hh"
#include "query_utils.h" #include "query_utils.h"
#include "working_files.h" #include "working_files.h"
namespace ccls { namespace ccls {
namespace { 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 { struct FoldingRange {
int startLine, startCharacter, endLine, endCharacter; int startLine, startCharacter, endLine, endCharacter;
std::string kind = "region"; std::string kind = "region";
}; };
MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, kind); MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine,
endCharacter, kind);
} // namespace
struct Handler_textDocumentFoldingRange void MessageHandler::textDocument_foldingRange(TextDocumentParam &param,
: BaseMessageHandler<In_textDocumentFoldingRange> { ReplyOnce &reply) {
MethodType GetMethodType() const override { return foldingRange; } QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
void Run(In_textDocumentFoldingRange *request) override { if (!file)
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file,
nullptr))
return; return;
WorkingFile *wfile = WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
working_files->GetFileByFilename(file->def->path);
if (!wfile) if (!wfile)
return; return;
std::vector<FoldingRange> result; std::vector<FoldingRange> result;
@ -53,9 +38,6 @@ struct Handler_textDocumentFoldingRange
fold.endLine = ls_range->end.line; fold.endLine = ls_range->end.line;
fold.endCharacter = ls_range->end.character; fold.endCharacter = ls_range->end.character;
} }
pipeline::Reply(request->id, result); reply(result);
} }
};
REGISTER_MESSAGE_HANDLER(Handler_textDocumentFoldingRange);
} // namespace
} // namespace ccls } // namespace ccls

View File

@ -1,29 +1,17 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "working_files.h" #include "working_files.h"
#include <clang/Format/Format.h> #include <clang/Format/Format.h>
#include <clang/Tooling/Core/Replacement.h> #include <clang/Tooling/Core/Replacement.h>
using namespace ccls; namespace ccls {
using namespace clang; using namespace clang;
namespace { 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<tooling::Replacements> llvm::Expected<tooling::Replacements>
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) { FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
StringRef Code(code.data(), code.size()), File(file.data(), file.size()); StringRef Code(code.data(), code.size()), File(file.data(), file.size());
@ -67,116 +55,59 @@ ReplacementsToEdits(std::string_view code, const tooling::Replacements &Repls) {
return ret; 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; std::string_view code = wfile->buffer_content;
auto ReplsOrErr = auto ReplsOrErr = FormatCode(code, wfile->filename, range);
FormatCode(code, wfile->filename, range);
if (ReplsOrErr) { if (ReplsOrErr) {
auto result = ReplacementsToEdits(code, *ReplsOrErr); auto result = ReplacementsToEdits(code, *ReplsOrErr);
pipeline::Reply(id, result); reply(result);
} else { } else {
lsResponseError err; lsResponseError err;
err.code = lsErrorCodes::UnknownErrorCode; err.code = lsErrorCodes::UnknownErrorCode;
err.message = llvm::toString(ReplsOrErr.takeError()); err.message = llvm::toString(ReplsOrErr.takeError());
pipeline::ReplyError(id, err); reply.Error(err);
} }
} }
} // namespace
struct In_TextDocumentFormatting : public RequestMessage { void MessageHandler::textDocument_formatting(DocumentFormattingParam &param,
MethodType GetMethodType() const override { return formatting; } ReplyOnce &reply) {
struct Params { QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
lsTextDocumentIdentifier textDocument; if (!file)
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<In_TextDocumentFormatting> {
MethodType GetMethodType() const override { return formatting; }
void Run(In_TextDocumentFormatting *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!wfile) if (!wfile)
return; return;
Format(wfile, {0, (unsigned)wfile->buffer_content.size()}, request->id); Format(reply, wfile, {0, (unsigned)wfile->buffer_content.size()});
} }
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting);
struct In_TextDocumentOnTypeFormatting : public RequestMessage { void MessageHandler::textDocument_onTypeFormatting(
MethodType GetMethodType() const override { return onTypeFormatting; } DocumentOnTypeFormattingParam &param, ReplyOnce &reply) {
struct Params { QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
lsTextDocumentIdentifier textDocument; if (!file)
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<In_TextDocumentOnTypeFormatting> {
MethodType GetMethodType() const override { return onTypeFormatting; }
void Run(In_TextDocumentOnTypeFormatting *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!wfile) if (!wfile)
return; return;
std::string_view code = wfile->buffer_content; std::string_view code = wfile->buffer_content;
int pos = GetOffsetForPosition(params.position, code); int pos = GetOffsetForPosition(param.position, code);
auto lbrace = code.find_last_of('{', pos); auto lbrace = code.find_last_of('{', pos);
if (lbrace == std::string::npos) if (lbrace == std::string::npos)
lbrace = pos; lbrace = pos;
Format(wfile, {(unsigned)lbrace, unsigned(pos - lbrace)}, request->id); Format(reply, wfile, {(unsigned)lbrace, unsigned(pos - lbrace)});
} }
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting);
struct In_TextDocumentRangeFormatting : public RequestMessage { void MessageHandler::textDocument_rangeFormatting(
MethodType GetMethodType() const override { return rangeFormatting; } DocumentRangeFormattingParam &param, ReplyOnce &reply) {
struct Params { QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
lsTextDocumentIdentifier textDocument; if (!file)
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<In_TextDocumentRangeFormatting> {
MethodType GetMethodType() const override { return rangeFormatting; }
void Run(In_TextDocumentRangeFormatting *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!wfile) if (!wfile)
return; return;
std::string_view code = wfile->buffer_content; std::string_view code = wfile->buffer_content;
int begin = GetOffsetForPosition(params.range.start, code), int begin = GetOffsetForPosition(param.range.start, code),
end = GetOffsetForPosition(params.range.end, code); end = GetOffsetForPosition(param.range.end, code);
Format(wfile, {(unsigned)begin, unsigned(end - begin)}, request->id); Format(reply, wfile, {(unsigned)begin, unsigned(end - begin)});
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRangeFormatting);
} // namespace

View File

@ -1,13 +1,33 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
namespace ccls {
namespace { namespace {
MethodType kMethodType = "textDocument/hover"; struct lsMarkedString {
std::optional<std::string> language;
std::string value;
};
struct Hover {
std::vector<lsMarkedString> contents;
std::optional<lsRange> 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) { const char *LanguageIdentifier(LanguageId lang) {
switch (lang) { switch (lang) {
@ -57,33 +77,18 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
}); });
return {hover, ls_comments}; return {hover, ls_comments};
} }
} // namespace
struct In_TextDocumentHover : public RequestMessage { void MessageHandler::textDocument_hover(TextDocumentPositionParam &param,
MethodType GetMethodType() const override { return kMethodType; } ReplyOnce &reply) {
lsTextDocumentPositionParams params; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
}; if (!file)
MAKE_REFLECT_STRUCT(In_TextDocumentHover, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentHover);
struct lsHover {
std::vector<lsMarkedString> contents;
std::optional<lsRange> range;
};
MAKE_REFLECT_STRUCT(lsHover, contents, range);
struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentHover *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsHover result; Hover result;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
std::optional<lsRange> ls_range = GetLsRange( std::optional<lsRange> ls_range = GetLsRange(
working_files->GetFileByFilename(file->def->path), sym.range); working_files->GetFileByFilename(file->def->path), sym.range);
if (!ls_range) if (!ls_range)
@ -100,8 +105,6 @@ struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
} }
} }
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentHover);
} // namespace

View File

@ -1,20 +1,15 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
#include <unordered_set> #include <unordered_set>
using namespace ccls; namespace ccls {
namespace { namespace {
MethodType kMethodType = "textDocument/references"; struct ReferenceParam : public TextDocumentPositionParam {
struct Context {
struct In_TextDocumentReferences : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct lsReferenceContext {
bool base = true; bool base = true;
// Exclude references with any |Role| bits set. // Exclude references with any |Role| bits set.
Role excludeRole = Role::None; Role excludeRole = Role::None;
@ -22,55 +17,40 @@ struct In_TextDocumentReferences : public RequestMessage {
bool includeDeclaration = false; bool includeDeclaration = false;
// Include references with all |Role| bits set. // Include references with all |Role| bits set.
Role role = Role::None; Role role = Role::None;
} context;
}; };
struct Params { MAKE_REFLECT_STRUCT(ReferenceParam::Context, base, excludeRole,
lsTextDocumentIdentifier textDocument; includeDeclaration, role);
lsPosition position; MAKE_REFLECT_STRUCT(ReferenceParam, textDocument, position, context);
lsReferenceContext context; } // namespace
};
Params params; void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
}; ReferenceParam param;
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext, base, Reflect(reader, param);
excludeRole, includeDeclaration, role); QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params, textDocument, position, if (!file)
context);
MAKE_REFLECT_STRUCT(In_TextDocumentReferences, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentReferences);
struct Handler_TextDocumentReferences
: BaseMessageHandler<In_TextDocumentReferences> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentReferences *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return; return;
std::vector<lsLocation> result; std::vector<lsLocation> result;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!file) { if (!file)
pipeline::Reply(request->id, result);
return; return;
}
std::unordered_set<Use> seen_uses; std::unordered_set<Use> seen_uses;
int line = params.position.line; int line = param.position.line;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
// Found symbol. Return references. // Found symbol. Return references.
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
seen.insert(sym.usr); seen.insert(sym.usr);
std::vector<Usr> stack{sym.usr}; std::vector<Usr> stack{sym.usr};
if (sym.kind != SymbolKind::Func) if (sym.kind != SymbolKind::Func)
params.context.base = false; param.context.base = false;
while (stack.size()) { while (stack.size()) {
sym.usr = stack.back(); sym.usr = stack.back();
stack.pop_back(); stack.pop_back();
auto fn = [&](Use use, lsSymbolKind parent_kind) { auto fn = [&](Use use, lsSymbolKind parent_kind) {
if (Role(use.role & params.context.role) == params.context.role && if (Role(use.role & param.context.role) == param.context.role &&
!(use.role & params.context.excludeRole) && !(use.role & param.context.excludeRole) &&
seen_uses.insert(use).second) seen_uses.insert(use).second)
if (auto loc = GetLsLocation(db, working_files, use)) { if (auto loc = GetLsLocation(db, working_files, use)) {
result.push_back(*loc); result.push_back(*loc);
@ -81,7 +61,7 @@ struct Handler_TextDocumentReferences
for (auto &def : entity.def) for (auto &def : entity.def)
if (def.spell) { if (def.spell) {
parent_kind = GetSymbolKind(db, sym); parent_kind = GetSymbolKind(db, sym);
if (params.context.base) if (param.context.base)
for (Usr usr : def.GetBases()) for (Usr usr : def.GetBases())
if (!seen.count(usr)) { if (!seen.count(usr)) {
seen.insert(usr); seen.insert(usr);
@ -91,7 +71,7 @@ struct Handler_TextDocumentReferences
} }
for (Use use : entity.uses) for (Use use : entity.uses)
fn(use, parent_kind); fn(use, parent_kind);
if (params.context.includeDeclaration) { if (param.context.includeDeclaration) {
for (auto &def : entity.def) for (auto &def : entity.def)
if (def.spell) if (def.spell)
fn(*def.spell, parent_kind); fn(*def.spell, parent_kind);
@ -111,7 +91,7 @@ struct Handler_TextDocumentReferences
if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1) if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1)
path = file->def->path; path = file->def->path;
for (const IndexInclude &include : file->def->includes) for (const IndexInclude &include : file->def->includes)
if (include.line == params.position.line) { if (include.line == param.position.line) {
path = include.resolved_path; path = include.resolved_path;
break; break;
} }
@ -130,8 +110,6 @@ struct Handler_TextDocumentReferences
if ((int)result.size() >= g_config->xref.maxNum) if ((int)result.size() >= g_config->xref.maxNum)
result.resize(g_config->xref.maxNum); result.resize(g_config->xref.maxNum);
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentReferences);
} // namespace

View File

@ -1,14 +1,11 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"
using namespace ccls;
namespace ccls {
namespace { namespace {
MethodType kMethodType = "textDocument/rename";
lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files, lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files,
SymbolRef sym, const std::string &new_text) { SymbolRef sym, const std::string &new_text) {
std::unordered_map<int, lsTextDocumentEdit> path_to_edit; std::unordered_map<int, lsTextDocumentEdit> path_to_edit;
@ -50,49 +47,21 @@ lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files,
edit.documentChanges.push_back(changes.second); edit.documentChanges.push_back(changes.second);
return edit; return edit;
} }
} // namespace
struct In_TextDocumentRename : public RequestMessage { void MessageHandler::textDocument_rename(RenameParam &param, ReplyOnce &reply) {
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<In_TextDocumentRename> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentRename *request) override {
int file_id; int file_id;
QueryFile *file; QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
if (!FindFileOrFail(db, project, request->id, if (!file)
request->params.textDocument.uri.GetPath(), &file,
&file_id))
return; return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsWorkspaceEdit result; lsWorkspaceEdit result;
for (SymbolRef sym : for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
FindSymbolsAtLocation(wfile, file, request->params.position)) { result = BuildWorkspaceEdit(db, working_files, sym, param.newName);
result =
BuildWorkspaceEdit(db, working_files, sym, request->params.newName);
break; break;
} }
pipeline::Reply(request->id, result); reply(result);
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRename);
} // namespace

View File

@ -2,56 +2,35 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "clang_complete.hh" #include "clang_complete.hh"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include <clang/Sema/Sema.h> #include <clang/Sema/Sema.h>
using namespace ccls; namespace ccls {
using namespace clang; using namespace clang;
namespace { namespace {
MethodType kMethodType = "textDocument/signatureHelp"; struct ParameterInformation {
// Represents a parameter of a callable-signature. A parameter can
// have a label and a doc-comment.
struct lsParameterInformation {
std::string label; std::string label;
// Not available in clang
// std::optional<std::string> documentation;
}; };
MAKE_REFLECT_STRUCT(lsParameterInformation, label); struct SignatureInformation {
// 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 {
std::string label; std::string label;
std::optional<std::string> documentation; std::optional<std::string> documentation;
std::vector<lsParameterInformation> parameters; std::vector<ParameterInformation> 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 { struct lsSignatureHelp {
std::vector<lsSignatureInformation> signatures; std::vector<SignatureInformation> signatures;
int activeSignature = 0; int activeSignature = 0;
int activeParameter = 0; int activeParameter = 0;
}; };
MAKE_REFLECT_STRUCT(ParameterInformation, label);
MAKE_REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature, MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature,
activeParameter); 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::string BuildOptional(const CodeCompletionString &CCS,
std::vector<lsParameterInformation> &ls_params) { std::vector<ParameterInformation> &ls_params) {
std::string ret; std::string ret;
for (const auto &Chunk : CCS) { for (const auto &Chunk : CCS) {
switch (Chunk.Kind) { switch (Chunk.Kind) {
@ -67,7 +46,7 @@ std::string BuildOptional(const CodeCompletionString &CCS,
// the code-completion location within a function call, message send, // the code-completion location within a function call, message send,
// macro invocation, etc. // macro invocation, etc.
ret += Chunk.Text; ret += Chunk.Text;
ls_params.push_back(lsParameterInformation{Chunk.Text}); ls_params.push_back({Chunk.Text});
break; break;
} }
case CodeCompletionString::CK_VerticalSpace: case CodeCompletionString::CK_VerticalSpace:
@ -113,7 +92,7 @@ public:
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true); Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
const char *ret_type = nullptr; 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 #if LLVM_VERSION_MAJOR >= 8
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction()); const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : ""; ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
@ -126,7 +105,7 @@ public:
case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_Placeholder:
case CodeCompletionString::CK_CurrentParameter: { case CodeCompletionString::CK_CurrentParameter: {
ls_sig.label += Chunk.Text; ls_sig.label += Chunk.Text;
ls_sig.parameters.push_back(lsParameterInformation{Chunk.Text}); ls_sig.parameters.push_back({Chunk.Text});
break; break;
} }
case CodeCompletionString::CK_Optional: case CodeCompletionString::CK_Optional:
@ -145,7 +124,7 @@ public:
} }
std::sort( std::sort(
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(), 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()) if (l.parameters.size() != r.parameters.size())
return l.parameters.size() < r.parameters.size(); return l.parameters.size() < r.parameters.size();
if (l.label.size() != r.label.size()) if (l.label.size() != r.label.size())
@ -157,30 +136,27 @@ public:
CodeCompletionAllocator &getAllocator() override { return *Alloc; } CodeCompletionAllocator &getAllocator() override { return *Alloc; }
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
}; };
} // namespace
struct Handler_TextDocumentSignatureHelp void MessageHandler::textDocument_signatureHelp(
: BaseMessageHandler<In_TextDocumentSignatureHelp> { TextDocumentPositionParam &param, ReplyOnce &reply) {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentSignatureHelp *request) override {
static CompleteConsumerCache<lsSignatureHelp> cache; static CompleteConsumerCache<lsSignatureHelp> cache;
const auto &params = request->params; std::string path = param.textDocument.uri.GetPath();
std::string path = params.textDocument.uri.GetPath(); lsPosition begin_pos = param.position;
lsPosition begin_pos = params.position;
if (WorkingFile *file = working_files->GetFileByFilename(path)) { if (WorkingFile *file = working_files->GetFileByFilename(path)) {
std::string completion_text; std::string completion_text;
lsPosition end_pos = params.position; lsPosition end_pos = param.position;
begin_pos = file->FindStableCompletionSource( begin_pos = file->FindStableCompletionSource(param.position,
request->params.position, &completion_text, &end_pos); &completion_text, &end_pos);
} }
CompletionManager::OnComplete callback = CompletionManager::OnComplete callback =
[id = request->id, path, begin_pos](CodeCompleteConsumer *OptConsumer) { [reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
if (!OptConsumer) if (!OptConsumer)
return; return;
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer); auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
pipeline::Reply(id, Consumer->ls_sighelp); reply(Consumer->ls_sighelp);
if (!Consumer->from_cache) { if (!Consumer->from_cache) {
cache.WithLock([&]() { cache.WithLock([&]() {
cache.path = path; cache.path = path;
@ -201,11 +177,9 @@ struct Handler_TextDocumentSignatureHelp
} else { } else {
clang_complete->completion_request_.PushBack( clang_complete->completion_request_.PushBack(
std::make_unique<CompletionManager::CompletionRequest>( std::make_unique<CompletionManager::CompletionRequest>(
request->id, params.textDocument, params.position, reply.id, param.textDocument, param.position,
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts, std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
callback)); callback));
} }
} }
}; } // namespace ccls
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentSignatureHelp);
} // namespace

View File

@ -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<In_TextDocumentTypeDefinition> {
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<lsLocation> 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

177
src/messages/workspace.cc Normal file
View File

@ -0,0 +1,177 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#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 <llvm/ADT/STLExtras.h>
#include <algorithm>
#include <ctype.h>
#include <functional>
#include <limits.h>
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 &param) {
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 &param) {
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<std::tuple<lsSymbolInformation, int, SymbolIdx>> *result) {
std::optional<lsSymbolInformation> info = GetSymbolInfo(db, sym, true);
if (!info)
return false;
Use loc;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym))
loc = *dr;
else {
auto decls = GetNonDefDeclarations(db, sym);
if (decls.empty())
return false;
loc = decls[0];
}
std::optional<lsLocation> 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 &param,
ReplyOnce &reply) {
std::vector<lsSymbolInformation> result;
std::string query = param.query;
// {symbol info, matching detailed_name or short_name, index}
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> 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

View File

@ -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 <llvm/ADT/STLExtras.h>
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<In_workspaceDidChangeConfiguration> {
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<lsFileEvent> 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<In_WorkspaceDidChangeWatchedFiles> {
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<lsWorkspaceFolder> 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<In_workspaceDidChangeWorkspaceFolders> {
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

View File

@ -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 <algorithm>
#include <ctype.h>
#include <functional>
#include <limits.h>
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<std::tuple<lsSymbolInformation, int, SymbolIdx>> *result) {
std::optional<lsSymbolInformation> info = GetSymbolInfo(db, sym, true);
if (!info)
return false;
Use loc;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym))
loc = *dr;
else {
auto decls = GetNonDefDeclarations(db, sym);
if (decls.empty())
return false;
loc = decls[0];
}
std::optional<lsLocation> 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<In_WorkspaceSymbol> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_WorkspaceSymbol *request) override {
std::vector<lsSymbolInformation> result;
std::string query = request->params.query;
// {symbol info, matching detailed_name or short_name, index}
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> 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

View File

@ -7,15 +7,16 @@
#include "config.h" #include "config.h"
#include "include_complete.h" #include "include_complete.h"
#include "log.hh" #include "log.hh"
#include "lsp.h" #include "lsp.hh"
#include "match.h" #include "match.h"
#include "message_handler.h" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.h" #include "platform.h"
#include "project.h" #include "project.hh"
#include "query_utils.h" #include "query_utils.h"
#include "serializers/json.h" #include "serializers/json.h"
#include <rapidjson/document.h>
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#include <llvm/Support/Process.h> #include <llvm/Support/Process.h>
@ -31,9 +32,7 @@ using namespace llvm;
#include <unistd.h> #include <unistd.h>
#endif #endif
void StandaloneInitialize(const std::string &, Project &, WorkingFiles &, VFS &, namespace ccls {
IncludeComplete &);
void VFS::Clear() { void VFS::Clear() {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
state.clear(); state.clear();
@ -55,7 +54,10 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
return false; return false;
} }
namespace ccls::pipeline { struct MessageHandler;
void StandaloneInitialize(MessageHandler &, const std::string &root);
namespace pipeline {
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0), std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0),
pending_index_requests = ATOMIC_VAR_INIT(0); pending_index_requests = ATOMIC_VAR_INIT(0);
@ -74,7 +76,7 @@ struct Index_Request {
MultiQueueWaiter *main_waiter; MultiQueueWaiter *main_waiter;
MultiQueueWaiter *indexer_waiter; MultiQueueWaiter *indexer_waiter;
MultiQueueWaiter *stdout_waiter; MultiQueueWaiter *stdout_waiter;
ThreadedQueue<std::unique_ptr<InMessage>> *on_request; ThreadedQueue<InMessage> *on_request;
ThreadedQueue<Index_Request> *index_request; ThreadedQueue<Index_Request> *index_request;
ThreadedQueue<IndexUpdate> *on_indexed; ThreadedQueue<IndexUpdate> *on_indexed;
ThreadedQueue<std::string> *for_stdout; ThreadedQueue<std::string> *for_stdout;
@ -345,7 +347,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
void Init() { void Init() {
main_waiter = new MultiQueueWaiter; main_waiter = new MultiQueueWaiter;
on_request = new ThreadedQueue<std::unique_ptr<InMessage>>(main_waiter); on_request = new ThreadedQueue<InMessage>(main_waiter);
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter); on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
indexer_waiter = new MultiQueueWaiter; indexer_waiter = new MultiQueueWaiter;
@ -402,35 +404,53 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
void LaunchStdin() { void LaunchStdin() {
std::thread([]() { std::thread([]() {
set_thread_name("stdin"); set_thread_name("stdin");
std::string str;
while (true) { while (true) {
std::unique_ptr<InMessage> message; constexpr std::string_view kContentLength("Content-Length: ");
std::optional<std::string> error = int len = 0;
MessageRegistry::instance()->ReadMessageFromStdin(&message); str.clear();
while (true) {
// Message parsing can fail if we don't recognize the method. int c = getchar();
if (error) { if (c == EOF)
// The message may be partially deserialized. return;
// Emit an error ResponseMessage if |id| is available. if (c == '\n') {
if (message) { if (str.empty())
lsRequestId id = message->GetRequestId(); break;
if (id.Valid()) { if (!str.compare(0, kContentLength.size(), kContentLength))
lsResponseError err; len = atoi(str.c_str() + kContentLength.size());
err.code = lsErrorCodes::InvalidParams; str.clear();
err.message = std::move(*error); } else if (c != '\r') {
ReplyError(id, err); str += c;
} }
} }
continue;
str.resize(len);
for (int i = 0; i < len; ++i) {
int c = getchar();
if (c == EOF)
return;
str[i] = c;
} }
// Cache |method_id| so we can access it after moving |message|. auto message = std::make_unique<char[]>(len);
MethodType method_type = message->GetMethodType(); std::copy(str.begin(), str.end(), message.get());
auto document = std::make_unique<rapidjson::Document>();
document->Parse(message.get(), len);
assert(!document->HasParseError());
on_request->PushBack(std::move(message)); 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<rapidjson::Value>();
on_request->PushBack(
{id, std::move(method), std::move(message), std::move(document)});
// If the message was to exit then querydb will take care of the actual if (method == "exit")
// exit. Stop reading from stdin since it might be detached.
if (method_type == kMethodType_Exit)
break; break;
} }
}) })
@ -479,31 +499,20 @@ void MainLoop() {
DB db; DB db;
// Setup shared references. // Setup shared references.
for (MessageHandler *handler : *MessageHandler::message_handlers) { MessageHandler handler;
handler->db = &db; handler.db = &db;
handler->project = &project; handler.project = &project;
handler->vfs = &vfs; handler.vfs = &vfs;
handler->working_files = &working_files; handler.working_files = &working_files;
handler->clang_complete = &clang_complete; handler.clang_complete = &clang_complete;
handler->include_complete = &include_complete; handler.include_complete = &include_complete;
}
bool has_indexed = false; bool has_indexed = false;
while (true) { while (true) {
std::vector<std::unique_ptr<InMessage>> messages = on_request->DequeueAll(); std::vector<InMessage> messages = on_request->DequeueAll();
bool did_work = messages.size(); bool did_work = messages.size();
for (auto &message : messages) { for (InMessage &message : messages)
// TODO: Consider using std::unordered_map to lookup the handler handler.Run(message);
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();
}
bool indexed = false; bool indexed = false;
for (int i = 20; i--;) { for (int i = 20; i--;) {
@ -532,7 +541,14 @@ void Standalone(const std::string &root) {
WorkingFiles wfiles; WorkingFiles wfiles;
VFS vfs; VFS vfs;
IncludeComplete complete(&project); 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(); bool tty = sys::Process::StandardOutIsDisplayed();
if (tty) { if (tty) {
@ -622,5 +638,5 @@ void Reply(lsRequestId id, const std::function<void(Writer &)> &fn) {
void ReplyError(lsRequestId id, const std::function<void(Writer &)> &fn) { void ReplyError(lsRequestId id, const std::function<void(Writer &)> &fn) {
Reply(id, "error", fn); Reply(id, "error", fn);
} }
} // namespace pipeline
} // namespace ccls::pipeline } // namespace ccls

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.hh"
#include "query.h" #include "query.h"
#include <atomic> #include <atomic>
@ -12,9 +12,9 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace ccls {
struct CompletionManager; struct CompletionManager;
struct GroupMatch; struct GroupMatch;
struct VFS;
struct Project; struct Project;
struct WorkingFiles; struct WorkingFiles;
@ -32,7 +32,6 @@ struct VFS {
bool Stamp(const std::string &path, int64_t ts, int step); bool Stamp(const std::string &path, int64_t ts, int step);
}; };
namespace ccls {
enum class IndexMode { enum class IndexMode {
NonInteractive, NonInteractive,
OnChange, OnChange,

View File

@ -3,12 +3,13 @@
#include "position.h" #include "position.h"
#include "serializer.h" #include "serializer.hh"
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
namespace ccls {
Position Position::FromString(const std::string &encoded) { Position Position::FromString(const std::string &encoded) {
char *p = const_cast<char *>(encoded.c_str()); char *p = const_cast<char *>(encoded.c_str());
int16_t line = int16_t(strtol(p, &p, 10)) - 1; 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); Reflect(visitor, value.end.column);
} }
} }
} // namespace ccls

View File

@ -9,6 +9,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
namespace ccls {
struct Position { struct Position {
int16_t line = -1; int16_t line = -1;
int16_t column = -1; int16_t column = -1;
@ -30,7 +31,6 @@ struct Position {
} }
bool operator<=(const Position &o) const { return !(o < *this); } bool operator<=(const Position &o) const { return !(o < *this); }
}; };
MAKE_HASHABLE(Position, t.line, t.column);
struct Range { struct Range {
Position start; Position start;
@ -52,20 +52,6 @@ struct Range {
} }
}; };
namespace std {
template <> struct hash<Range> {
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<uint64_t>()(u.u64);
}
};
} // namespace std
// Reflection // Reflection
class Reader; class Reader;
class Writer; class Writer;
@ -73,3 +59,20 @@ void Reflect(Reader &visitor, Position &value);
void Reflect(Writer &visitor, Position &value); void Reflect(Writer &visitor, Position &value);
void Reflect(Reader &visitor, Range &value); void Reflect(Reader &visitor, Range &value);
void Reflect(Writer &visitor, Range &value); void Reflect(Writer &visitor, Range &value);
} // namespace ccls
namespace std {
template <> struct hash<ccls::Range> {
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<uint64_t>()(u.u64);
}
};
} // namespace std
MAKE_HASHABLE(ccls::Position, t.line, t.column);

View File

@ -1,7 +1,7 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "project.h" #include "project.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "log.hh" #include "log.hh"
@ -31,10 +31,10 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
using namespace ccls;
using namespace clang; using namespace clang;
using namespace llvm; using namespace llvm;
namespace ccls {
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) { std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
using namespace clang::driver; using namespace clang::driver;
auto I = types::lookupTypeForExtension( auto I = types::lookupTypeForExtension(
@ -473,3 +473,4 @@ void Project::Index(WorkingFiles *wfiles, lsRequestId id) {
// trigger refreshing semantic highlight for all working files. // trigger refreshing semantic highlight for all working files.
pipeline::Index("", {}, IndexMode::NonInteractive); pipeline::Index("", {}, IndexMode::NonInteractive);
} }
} // namespace ccls

View File

@ -4,7 +4,7 @@
#pragma once #pragma once
#include "config.h" #include "config.h"
#include "lsp.h" #include "lsp.hh"
#include <functional> #include <functional>
#include <mutex> #include <mutex>
@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace ccls {
struct WorkingFiles; struct WorkingFiles;
std::pair<LanguageId, bool> lookupExtension(std::string_view filename); std::pair<LanguageId, bool> lookupExtension(std::string_view filename);
@ -64,3 +65,4 @@ struct Project {
void Index(WorkingFiles *wfiles, lsRequestId id); void Index(WorkingFiles *wfiles, lsRequestId id);
}; };
} // namespace ccls

View File

@ -4,7 +4,7 @@
#include "query.h" #include "query.h"
#include "indexer.h" #include "indexer.h"
#include "serializer.h" #include "serializer.hh"
#include "serializers/json.h" #include "serializers/json.h"
#include <cassert> #include <cassert>
@ -15,8 +15,8 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
namespace ccls {
namespace { namespace {
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) { void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
if (use.file_id == -1) if (use.file_id == -1)
use.file_id = file_id; use.file_id = file_id;
@ -456,3 +456,4 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
} }
return ""; return "";
} }
} // namespace ccls

View File

@ -4,23 +4,26 @@
#pragma once #pragma once
#include "indexer.h" #include "indexer.h"
#include "serializer.h" #include "serializer.hh"
#include <llvm/ADT/DenseMap.h> #include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/SmallVector.h> #include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringMap.h> #include <llvm/ADT/StringMap.h>
namespace llvm { namespace llvm {
template <> struct DenseMapInfo<ExtentRef> { template <> struct DenseMapInfo<ccls::ExtentRef> {
static inline ExtentRef getEmptyKey() { return {}; } static inline ccls::ExtentRef getEmptyKey() { return {}; }
static inline ExtentRef getTombstoneKey() { return {{Range(), Usr(-1)}}; } static inline ccls::ExtentRef getTombstoneKey() {
static unsigned getHashValue(ExtentRef sym) { return {{ccls::Range(), ccls::Usr(-1)}};
return std::hash<ExtentRef>()(sym);
} }
static bool isEqual(ExtentRef l, ExtentRef r) { return l == r; } static unsigned getHashValue(ccls::ExtentRef sym) {
return std::hash<ccls::ExtentRef>()(sym);
}
static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; }
}; };
} } // namespace llvm
namespace ccls {
struct QueryFile { struct QueryFile {
struct Def { struct Def {
std::string path; std::string path;
@ -181,3 +184,4 @@ struct DB {
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); } QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); } QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
}; };
} // namespace ccls

View File

@ -8,6 +8,7 @@
#include <limits.h> #include <limits.h>
#include <unordered_set> #include <unordered_set>
namespace ccls {
namespace { namespace {
// Computes roughly how long |range| is. // Computes roughly how long |range| is.
@ -330,3 +331,4 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
return symbols; return symbols;
} }
} // namespace ccls

View File

@ -8,6 +8,7 @@
#include <optional> #include <optional>
namespace ccls {
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym); Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
// Get defining declaration (if exists) or an arbitrary declaration (otherwise) // Get defining declaration (if exists) or an arbitrary declaration (otherwise)
@ -90,3 +91,4 @@ void EachDefinedFunc(DB *db, const std::vector<Usr> &usrs, Fn &&fn) {
fn(obj); fn(obj);
} }
} }
} // namespace ccls

View File

@ -1,7 +1,7 @@
// Copyright 2017-2018 ccls Authors // Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "serializer.h" #include "serializer.hh"
#include "filesystem.hh" #include "filesystem.hh"
#include "indexer.h" #include "indexer.h"
@ -15,11 +15,11 @@
#include <mutex> #include <mutex>
#include <stdexcept> #include <stdexcept>
using namespace ccls;
using namespace llvm; using namespace llvm;
bool gTestOutputMode = false; bool gTestOutputMode = false;
namespace ccls {
Reader::~Reader() {} Reader::~Reader() {}
BinaryReader::~BinaryReader() {} BinaryReader::~BinaryReader() {}
JsonReader::~JsonReader() {} JsonReader::~JsonReader() {}
@ -350,7 +350,6 @@ void Reflect(Writer &visitor, SerializeFormat &value) {
} }
} }
namespace ccls {
static BumpPtrAllocator Alloc; static BumpPtrAllocator Alloc;
static DenseSet<CachedHashStringRef> Strings; static DenseSet<CachedHashStringRef> Strings;
static std::mutex AllocMutex; static std::mutex AllocMutex;

View File

@ -23,6 +23,7 @@ class CachedHashStringRef;
class StringRef; class StringRef;
} }
namespace ccls {
enum class SerializeFormat { Binary, Json }; enum class SerializeFormat { Binary, Json };
struct JsonNull {}; struct JsonNull {};
@ -91,12 +92,12 @@ struct IndexFile;
#define MAKE_REFLECT_TYPE_PROXY2(type, as_type) \ #define MAKE_REFLECT_TYPE_PROXY2(type, as_type) \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(Reader &visitor, type &value) { \ LLVM_ATTRIBUTE_UNUSED inline void Reflect(Reader &visitor, type &value) { \
as_type value0; \ as_type value0; \
::Reflect(visitor, value0); \ ::ccls::Reflect(visitor, value0); \
value = static_cast<type>(value0); \ value = static_cast<type>(value0); \
} \ } \
LLVM_ATTRIBUTE_UNUSED inline void Reflect(Writer &visitor, type &value) { \ LLVM_ATTRIBUTE_UNUSED inline void Reflect(Writer &visitor, type &value) { \
auto value0 = static_cast<as_type>(value); \ auto value0 = static_cast<as_type>(value); \
::Reflect(visitor, value0); \ ::ccls::Reflect(visitor, value0); \
} }
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name); #define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
@ -293,7 +294,6 @@ template <typename T> void ReflectMember(Writer &vis, const char *name, T &v) {
// API // API
namespace ccls {
const char *Intern(llvm::StringRef str); const char *Intern(llvm::StringRef str);
llvm::CachedHashStringRef InternH(llvm::StringRef str); llvm::CachedHashStringRef InternH(llvm::StringRef str);
std::string Serialize(SerializeFormat format, IndexFile &file); std::string Serialize(SerializeFormat format, IndexFile &file);

View File

@ -3,10 +3,11 @@
#pragma once #pragma once
#include "serializer.h" #include "serializer.hh"
#include <string.h> #include <string.h>
namespace ccls {
class BinaryReader : public Reader { class BinaryReader : public Reader {
const char *p_; const char *p_;
@ -123,3 +124,4 @@ public:
void EndObject() override {} void EndObject() override {}
void Key(const char *name) override {} void Key(const char *name) override {}
}; };
} // namespace ccls

View File

@ -3,11 +3,12 @@
#pragma once #pragma once
#include "serializer.h" #include "serializer.hh"
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
namespace ccls {
class JsonReader : public Reader { class JsonReader : public Reader {
rapidjson::GenericValue<rapidjson::UTF8<>> *m_; rapidjson::GenericValue<rapidjson::UTF8<>> *m_;
std::vector<const char *> path_; std::vector<const char *> path_;
@ -104,3 +105,4 @@ public:
void EndObject() override { m_->EndObject(); } void EndObject() override { m_->EndObject(); }
void Key(const char *name) override { m_->Key(name); } void Key(const char *name) override { m_->Key(name); }
}; };
} // namespace ccls

View File

@ -8,7 +8,7 @@
#include "indexer.h" #include "indexer.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.h" #include "platform.h"
#include "serializer.h" #include "serializer.hh"
#include "utils.h" #include "utils.h"
#include <llvm/Config/llvm-config.h> #include <llvm/Config/llvm-config.h>
@ -32,6 +32,7 @@ using namespace llvm;
extern bool gTestOutputMode; extern bool gTestOutputMode;
namespace ccls {
std::string ToString(const rapidjson::Document &document) { std::string ToString(const rapidjson::Document &document) {
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer); rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
@ -361,3 +362,4 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
return success; return success;
} }
} // namespace ccls

View File

@ -5,4 +5,6 @@
#include <string> #include <string>
namespace ccls {
bool RunIndexTests(const std::string &filter_path, bool enable_update); bool RunIndexTests(const std::string &filter_path, bool enable_update);
}

View File

@ -13,17 +13,18 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
struct BaseThreadQueue {
virtual bool IsEmpty() = 0;
virtual ~BaseThreadQueue() = default;
};
// std::lock accepts two or more arguments. We define an overload for one // std::lock accepts two or more arguments. We define an overload for one
// argument. // argument.
namespace std { namespace std {
template <typename Lockable> void lock(Lockable &l) { l.lock(); } template <typename Lockable> void lock(Lockable &l) { l.lock(); }
} // namespace std } // namespace std
namespace ccls {
struct BaseThreadQueue {
virtual bool IsEmpty() = 0;
virtual ~BaseThreadQueue() = default;
};
template <typename... Queue> struct MultiQueueLock { template <typename... Queue> struct MultiQueueLock {
MultiQueueLock(Queue... lockable) : tuple_{lockable...} { lock(); } MultiQueueLock(Queue... lockable) : tuple_{lockable...} { lock(); }
~MultiQueueLock() { unlock(); } ~MultiQueueLock() { unlock(); }
@ -161,3 +162,4 @@ private:
MultiQueueWaiter *waiter_; MultiQueueWaiter *waiter_;
std::unique_ptr<MultiQueueWaiter> owned_waiter_; std::unique_ptr<MultiQueueWaiter> owned_waiter_;
}; };
} // namespace ccls

View File

@ -21,6 +21,7 @@ using namespace llvm;
#include <string.h> #include <string.h>
#include <unordered_map> #include <unordered_map>
namespace ccls {
uint64_t HashUsr(std::string_view s) { uint64_t HashUsr(std::string_view s) {
union { union {
uint64_t ret; uint64_t ret;
@ -162,3 +163,4 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text,
} }
std::string GetDefaultResourceDirectory() { return DEFAULT_RESOURCE_DIRECTORY; } std::string GetDefaultResourceDirectory() { return DEFAULT_RESOURCE_DIRECTORY; }
}

View File

@ -14,6 +14,7 @@ namespace llvm {
class StringRef; class StringRef;
} }
namespace ccls {
uint64_t HashUsr(std::string_view s); uint64_t HashUsr(std::string_view s);
uint64_t HashUsr(llvm::StringRef 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<type> { \ template <> struct hash<type> { \
std::size_t operator()(const type &t) const { \ std::size_t operator()(const type &t) const { \
std::size_t ret = 0; \ std::size_t ret = 0; \
hash_combine(ret, __VA_ARGS__); \ ccls::hash_combine(ret, __VA_ARGS__); \
return ret; \ return ret; \
} \ } \
}; \ }; \
} }
std::string GetDefaultResourceDirectory(); std::string GetDefaultResourceDirectory();
} // namespace ccls

View File

@ -11,6 +11,7 @@
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
namespace ccls {
namespace { namespace {
// When finding a best match of buffer line and index line, limit the max edit // 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(); return files[files.size() - 1].get();
} }
void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) { void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard<std::mutex> lock(files_mutex);
std::string filename = change.textDocument.uri.GetPath(); std::string filename = change.textDocument.uri.GetPath();
@ -471,7 +472,7 @@ void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) {
if (change.textDocument.version) if (change.textDocument.version)
file->version = *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. // Per the spec replace everything if the rangeLength and range are not set.
// See https://github.com/Microsoft/language-server-protocol/issues/9. // See https://github.com/Microsoft/language-server-protocol/issues/9.
if (!diff.range) { if (!diff.range) {
@ -563,3 +564,4 @@ std::string_view LexIdentifierAroundPos(lsPosition position,
return content.substr(start, end - start); return content.substr(start, end - start);
} }
}

View File

@ -3,13 +3,14 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.hh"
#include "utils.h" #include "utils.h"
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string> #include <string>
namespace ccls {
struct WorkingFile { struct WorkingFile {
int version = 0; int version = 0;
std::string filename; std::string filename;
@ -105,7 +106,7 @@ struct WorkingFiles {
const std::function<void(WorkingFile *file)> &action); const std::function<void(WorkingFile *file)> &action);
WorkingFile *OnOpen(const lsTextDocumentItem &open); WorkingFile *OnOpen(const lsTextDocumentItem &open);
void OnChange(const lsTextDocumentDidChangeParams &change); void OnChange(const TextDocumentDidChangeParam &change);
void OnClose(const lsTextDocumentIdentifier &close); void OnClose(const lsTextDocumentIdentifier &close);
// If |filter_paths| is non-empty, only files which contain any of the given // 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 LexIdentifierAroundPos(lsPosition position,
std::string_view content); std::string_view content);
} // namespace ccls