mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-23 16:15:07 +00:00
Refactor message handler and namespace ccls
This commit is contained in:
parent
05929b8afb
commit
67eb1e6b23
@ -212,11 +212,8 @@ target_sources(ccls PRIVATE
|
||||
src/messages/ccls_navigate.cc
|
||||
src/messages/ccls_reload.cc
|
||||
src/messages/ccls_vars.cc
|
||||
src/messages/exit.cc
|
||||
src/messages/initialize.cc
|
||||
src/messages/shutdown.cc
|
||||
src/messages/textDocument_codeAction.cc
|
||||
src/messages/textDocument_codeLens.cc
|
||||
src/messages/textDocument_code.cc
|
||||
src/messages/textDocument_completion.cc
|
||||
src/messages/textDocument_definition.cc
|
||||
src/messages/textDocument_did.cc
|
||||
@ -227,7 +224,5 @@ target_sources(ccls PRIVATE
|
||||
src/messages/textDocument_references.cc
|
||||
src/messages/textDocument_rename.cc
|
||||
src/messages/textDocument_signatureHelp.cc
|
||||
src/messages/textDocument_typeDefinition.cc
|
||||
src/messages/workspace_did.cc
|
||||
src/messages/workspace_symbol.cc
|
||||
src/messages/workspace.cc
|
||||
)
|
||||
|
@ -563,8 +563,6 @@ std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
|
||||
return preamble;
|
||||
}
|
||||
|
||||
} // namespace ccls
|
||||
|
||||
CompletionManager::CompletionManager(Project *project,
|
||||
WorkingFiles *working_files,
|
||||
OnDiagnostic on_diagnostic,
|
||||
@ -674,3 +672,4 @@ void CompletionManager::FlushAllSessions() {
|
||||
preloads.Clear();
|
||||
sessions.Clear();
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -5,9 +5,8 @@
|
||||
|
||||
#include "clang_tu.hh"
|
||||
#include "lru_cache.h"
|
||||
#include "lsp.h"
|
||||
#include "lsp_completion.h"
|
||||
#include "project.h"
|
||||
#include "lsp.hh"
|
||||
#include "project.hh"
|
||||
#include "threaded_queue.h"
|
||||
#include "working_files.h"
|
||||
|
||||
@ -61,7 +60,6 @@ struct CompletionSession
|
||||
|
||||
std::shared_ptr<PreambleData> GetPreamble();
|
||||
};
|
||||
}
|
||||
|
||||
struct CompletionManager {
|
||||
using OnDiagnostic = std::function<void(
|
||||
@ -181,3 +179,4 @@ struct CompleteConsumerCache {
|
||||
return this->path == path && this->position == position;
|
||||
}
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace ccls {
|
||||
std::string PathFromFileEntry(const FileEntry &file) {
|
||||
StringRef Name = file.tryGetRealPathName();
|
||||
if (Name.empty())
|
||||
@ -241,3 +242,4 @@ const char *ClangBuiltinTypeName(int kind) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -17,6 +17,7 @@ namespace vfs = clang::vfs;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ccls {
|
||||
std::string PathFromFileEntry(const clang::FileEntry &file);
|
||||
|
||||
Range FromCharSourceRange(const clang::SourceManager &SM,
|
||||
@ -42,3 +43,4 @@ BuildCompilerInvocation(std::vector<const char *> args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
|
||||
|
||||
const char *ClangBuiltinTypeName(int);
|
||||
} // namespace ccls
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace ccls {
|
||||
Config *g_config;
|
||||
|
||||
namespace ccls {
|
||||
void DoPathMapping(std::string &arg) {
|
||||
for (const std::string &mapping : g_config->clang.pathMappings) {
|
||||
auto sep = mapping.find('>');
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ccls {
|
||||
/*
|
||||
The language client plugin needs to send initialization options in the
|
||||
`initialize` request to the ccls language server.
|
||||
@ -269,6 +270,5 @@ MAKE_REFLECT_STRUCT(Config, compilationDatabaseCommand,
|
||||
|
||||
extern Config *g_config;
|
||||
|
||||
namespace ccls {
|
||||
void DoPathMapping(std::string &arg);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ using namespace llvm;
|
||||
|
||||
void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix,
|
||||
const std::function<void(const std::string &)> &handler) {
|
||||
EnsureEndsInSlash(folder);
|
||||
ccls::EnsureEndsInSlash(folder);
|
||||
sys::fs::file_status Status;
|
||||
if (sys::fs::status(folder, Status, true))
|
||||
return;
|
||||
|
@ -8,10 +8,11 @@
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
enum CharClass { Other, Lower, Upper };
|
||||
enum CharRole { None, Tail, Head };
|
||||
|
||||
namespace {
|
||||
CharClass GetCharClass(int c) {
|
||||
if (islower(c))
|
||||
return Lower;
|
||||
@ -133,6 +134,7 @@ int FuzzyMatcher::Match(std::string_view text) {
|
||||
ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j));
|
||||
return ret;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
||||
#if 0
|
||||
TEST_SUITE("fuzzy_match") {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ccls {
|
||||
class FuzzyMatcher {
|
||||
public:
|
||||
constexpr static int kMaxPat = 100;
|
||||
@ -30,3 +31,4 @@ private:
|
||||
int MatchScore(int i, int j, bool last);
|
||||
int MissScore(int j, bool last);
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
namespace ccls {
|
||||
template <typename Node>
|
||||
std::vector<lsLocation> FlattenHierarchy(const std::optional<Node> &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());
|
||||
return ret;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -6,15 +6,18 @@
|
||||
#include "filesystem.hh"
|
||||
#include "match.h"
|
||||
#include "platform.h"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Support/Timer.h>
|
||||
|
||||
#include <unordered_set>
|
||||
using namespace llvm;
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
||||
struct CompletionCandidate {
|
||||
@ -83,7 +86,6 @@ lsCompletionItem BuildCompletionItem(const std::string &path,
|
||||
item.priority_ = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IncludeComplete::IncludeComplete(Project *project)
|
||||
@ -198,3 +200,4 @@ IncludeComplete::FindCompletionItemForAbsolutePath(
|
||||
return std::nullopt;
|
||||
return completion_items[it->second];
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp_completion.h"
|
||||
#include "message_handler.hh"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
struct GroupMatch;
|
||||
struct Project;
|
||||
|
||||
@ -26,18 +26,18 @@ struct IncludeComplete {
|
||||
void InsertIncludesFromDirectory(std::string directory,
|
||||
bool use_angle_brackets);
|
||||
|
||||
std::optional<lsCompletionItem>
|
||||
std::optional<ccls::lsCompletionItem>
|
||||
FindCompletionItemForAbsolutePath(const std::string &absolute_path);
|
||||
|
||||
// Insert item to |completion_items|.
|
||||
// Update |absolute_path_to_completion_item| and |inserted_paths|.
|
||||
void InsertCompletionItem(const std::string &absolute_path,
|
||||
lsCompletionItem &&item);
|
||||
ccls::lsCompletionItem &&item);
|
||||
|
||||
// Guards |completion_items| when |is_scanning| is true.
|
||||
std::mutex completion_items_mutex;
|
||||
std::atomic<bool> is_scanning;
|
||||
std::vector<lsCompletionItem> completion_items;
|
||||
std::vector<ccls::lsCompletionItem> completion_items;
|
||||
|
||||
// Absolute file path to the completion item in |completion_items|.
|
||||
// Keep the one with shortest include path.
|
||||
@ -50,3 +50,4 @@ struct IncludeComplete {
|
||||
Project *project_;
|
||||
std::unique_ptr<GroupMatch> match_;
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "match.h"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <clang/AST/AST.h>
|
||||
#include <clang/Frontend/FrontendAction.h>
|
||||
@ -26,9 +26,9 @@
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace ccls;
|
||||
using namespace clang;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
||||
constexpr int kInitializerMaxLines = 3;
|
||||
@ -1195,7 +1195,7 @@ template <typename T> void Uniquify(std::vector<T> &a) {
|
||||
a.resize(n);
|
||||
}
|
||||
|
||||
namespace ccls::idx {
|
||||
namespace idx {
|
||||
void Init() {
|
||||
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist,
|
||||
g_config->index.multiVersionBlacklist);
|
||||
@ -1334,7 +1334,7 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace ccls::idx
|
||||
} // namespace idx
|
||||
|
||||
void Reflect(Reader &vis, SymbolRef &v) {
|
||||
if (vis.Format() == SerializeFormat::Json) {
|
||||
@ -1422,3 +1422,4 @@ void Reflect(Writer &vis, DeclRef &v) {
|
||||
Reflect(vis, v.extent);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
#include "maybe.h"
|
||||
#include "position.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
#include "utils.h"
|
||||
|
||||
#include <clang/Basic/FileManager.h>
|
||||
@ -19,6 +19,17 @@
|
||||
#include <unordered_map>
|
||||
#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;
|
||||
enum class LanguageId;
|
||||
|
||||
@ -48,7 +59,6 @@ struct SymbolRef {
|
||||
bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); }
|
||||
bool Valid() const { return range.Valid(); }
|
||||
};
|
||||
MAKE_HASHABLE(SymbolRef, t.range, t.usr, t.kind, t.role);
|
||||
|
||||
struct ExtentRef : SymbolRef {
|
||||
Range extent;
|
||||
@ -57,7 +67,6 @@ struct ExtentRef : SymbolRef {
|
||||
}
|
||||
bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); }
|
||||
};
|
||||
MAKE_HASHABLE(ExtentRef, t.range, t.usr, t.kind, t.role, t.extent);
|
||||
|
||||
struct Ref {
|
||||
Range range;
|
||||
@ -81,12 +90,10 @@ struct Use : Ref {
|
||||
return range == o.range && file_id == o.file_id;
|
||||
}
|
||||
};
|
||||
MAKE_HASHABLE(Use, t.range, t.file_id)
|
||||
|
||||
struct DeclRef : Use {
|
||||
Range extent;
|
||||
};
|
||||
MAKE_HASHABLE(DeclRef, t.range, t.file_id)
|
||||
|
||||
void Reflect(Reader &visitor, SymbolRef &value);
|
||||
void Reflect(Writer &visitor, SymbolRef &value);
|
||||
@ -233,16 +240,6 @@ struct IndexInclude {
|
||||
const char *resolved_path;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<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 {
|
||||
// For both JSON and MessagePack cache files.
|
||||
static const int kMajorVersion;
|
||||
@ -296,7 +293,7 @@ struct CompletionManager;
|
||||
struct WorkingFiles;
|
||||
struct VFS;
|
||||
|
||||
namespace ccls::idx {
|
||||
namespace idx {
|
||||
void Init();
|
||||
std::vector<std::unique_ptr<IndexFile>>
|
||||
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<std::pair<std::string, std::string>> &remapped,
|
||||
bool &ok);
|
||||
} // namespace ccls::idx
|
||||
} // namespace idx
|
||||
} // namespace ccls
|
||||
|
||||
MAKE_HASHABLE(ccls::SymbolRef, t.range, t.usr, t.kind, t.role);
|
||||
MAKE_HASHABLE(ccls::ExtentRef, t.range, t.usr, t.kind, t.role, t.extent);
|
||||
MAKE_HASHABLE(ccls::Use, t.range, t.file_id)
|
||||
MAKE_HASHABLE(ccls::DeclRef, t.range, t.file_id)
|
||||
|
139
src/lsp.cc
139
src/lsp.cc
@ -1,18 +1,15 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
|
||||
#include "log.hh"
|
||||
#include "serializers/json.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
|
||||
MethodType kMethodType_Exit = "exit";
|
||||
|
||||
namespace ccls {
|
||||
void Reflect(Reader &visitor, lsRequestId &value) {
|
||||
if (visitor.IsInt64()) {
|
||||
value.type = lsRequestId::kInt;
|
||||
@ -44,10 +41,6 @@ void Reflect(Writer &visitor, lsRequestId &value) {
|
||||
}
|
||||
}
|
||||
|
||||
InMessage::~InMessage() {}
|
||||
|
||||
MessageRegistry *MessageRegistry::instance_ = nullptr;
|
||||
|
||||
lsTextDocumentIdentifier
|
||||
lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const {
|
||||
lsTextDocumentIdentifier result;
|
||||
@ -55,120 +48,6 @@ lsVersionedTextDocumentIdentifier::AsTextDocumentIdentifier() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reads a JsonRpc message. |read| returns the next input character.
|
||||
std::optional<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 result;
|
||||
result.SetPath(path);
|
||||
@ -260,16 +139,4 @@ std::string lsPosition::ToString() const {
|
||||
bool lsTextEdit::operator==(const lsTextEdit &that) {
|
||||
return range == that.range && newText == that.newText;
|
||||
}
|
||||
|
||||
void Reflect(Writer &visitor, lsMarkedString &value) {
|
||||
// If there is a language, emit a `{language:string, value:string}` object. If
|
||||
// not, emit a string.
|
||||
if (value.language) {
|
||||
REFLECT_MEMBER_START();
|
||||
REFLECT_MEMBER(language);
|
||||
REFLECT_MEMBER(value);
|
||||
REFLECT_MEMBER_END();
|
||||
} else {
|
||||
Reflect(visitor, value.value);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -4,15 +4,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
#include "utils.h"
|
||||
|
||||
#include <rapidjson/fwd.h>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <unordered_map>
|
||||
|
||||
using MethodType = const char *;
|
||||
extern MethodType kMethodType_Exit;
|
||||
|
||||
namespace ccls {
|
||||
struct lsRequestId {
|
||||
// The client can send the request id as an int or a string. We should output
|
||||
// the same format we received.
|
||||
@ -27,47 +27,10 @@ void Reflect(Reader &visitor, lsRequestId &value);
|
||||
void Reflect(Writer &visitor, lsRequestId &value);
|
||||
|
||||
struct InMessage {
|
||||
virtual ~InMessage();
|
||||
|
||||
virtual MethodType GetMethodType() const = 0;
|
||||
virtual lsRequestId GetRequestId() const { return {}; }
|
||||
};
|
||||
|
||||
struct NotificationMessage : InMessage {};
|
||||
|
||||
struct RequestMessage : public InMessage {
|
||||
lsRequestId id;
|
||||
lsRequestId GetRequestId() const override { return id; }
|
||||
};
|
||||
|
||||
#define REGISTER_IN_MESSAGE(type) \
|
||||
static MessageRegistryRegister<type> 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));
|
||||
};
|
||||
}
|
||||
std::string method;
|
||||
std::unique_ptr<char[]> message;
|
||||
std::unique_ptr<rapidjson::Document> document;
|
||||
};
|
||||
|
||||
enum class lsErrorCodes {
|
||||
@ -214,7 +177,6 @@ struct lsSymbolInformation {
|
||||
lsLocation location;
|
||||
std::optional<std::string_view> containerName;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName);
|
||||
|
||||
struct lsTextDocumentIdentifier {
|
||||
lsDocumentUri uri;
|
||||
@ -230,15 +192,6 @@ struct lsVersionedTextDocumentIdentifier {
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsVersionedTextDocumentIdentifier, uri, version);
|
||||
|
||||
struct lsTextDocumentPositionParams {
|
||||
// The text document.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
|
||||
// The position inside the text document.
|
||||
lsPosition position;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentPositionParams, textDocument, position);
|
||||
|
||||
struct lsTextEdit {
|
||||
// The range of the text document to be manipulated. To insert
|
||||
// text into a document create a range where start === end.
|
||||
@ -289,26 +242,7 @@ struct lsWorkspaceEdit {
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsWorkspaceEdit, documentChanges);
|
||||
|
||||
// MarkedString can be used to render human readable text. It is either a
|
||||
// markdown string or a code-block that provides a language and a code snippet.
|
||||
// The language identifier is sematically equal to the std::optional language
|
||||
// identifier in fenced code blocks in GitHub issues. See
|
||||
// https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
|
||||
//
|
||||
// The pair of a language and a value is an equivalent to markdown:
|
||||
// ```${language}
|
||||
// ${value}
|
||||
// ```
|
||||
//
|
||||
// Note that markdown strings will be sanitized - that means html will be
|
||||
// escaped.
|
||||
struct lsMarkedString {
|
||||
std::optional<std::string> language;
|
||||
std::string value;
|
||||
};
|
||||
void Reflect(Writer &visitor, lsMarkedString &value);
|
||||
|
||||
struct lsTextDocumentContentChangeEvent {
|
||||
struct TextDocumentContentChangeEvent {
|
||||
// The range of the document that changed.
|
||||
std::optional<lsRange> range;
|
||||
// The length of the range that got replaced.
|
||||
@ -316,24 +250,21 @@ struct lsTextDocumentContentChangeEvent {
|
||||
// The new text of the range/document.
|
||||
std::string text;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentContentChangeEvent, range, rangeLength, text);
|
||||
|
||||
struct lsTextDocumentDidChangeParams {
|
||||
struct TextDocumentDidChangeParam {
|
||||
lsVersionedTextDocumentIdentifier textDocument;
|
||||
std::vector<lsTextDocumentContentChangeEvent> contentChanges;
|
||||
std::vector<TextDocumentContentChangeEvent> contentChanges;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentDidChangeParams, textDocument,
|
||||
contentChanges);
|
||||
|
||||
struct lsWorkspaceFolder {
|
||||
struct WorkspaceFolder {
|
||||
lsDocumentUri uri;
|
||||
std::string name;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name);
|
||||
MAKE_REFLECT_STRUCT(WorkspaceFolder, uri, name);
|
||||
|
||||
// Show a message to the user.
|
||||
enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
|
||||
MAKE_REFLECT_TYPE_PROXY(lsMessageType)
|
||||
enum class MessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
|
||||
MAKE_REFLECT_TYPE_PROXY(MessageType)
|
||||
|
||||
enum class lsDiagnosticSeverity {
|
||||
// Reports an error.
|
||||
@ -380,7 +311,7 @@ struct lsPublishDiagnosticsParams {
|
||||
MAKE_REFLECT_STRUCT(lsPublishDiagnosticsParams, uri, diagnostics);
|
||||
|
||||
struct lsShowMessageParams {
|
||||
lsMessageType type = lsMessageType::Error;
|
||||
MessageType type = MessageType::Error;
|
||||
std::string message;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsShowMessageParams, type, message);
|
||||
@ -418,3 +349,4 @@ inline uint16_t operator&(Role lhs, Role rhs) {
|
||||
inline Role operator|(Role lhs, Role rhs) {
|
||||
return Role(uint16_t(lhs) | uint16_t(rhs));
|
||||
}
|
||||
} // namespace ccls
|
@ -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);
|
@ -4,7 +4,7 @@
|
||||
#include "log.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
#include "serializers/json.h"
|
||||
#include "test.h"
|
||||
#include "working_files.h"
|
||||
@ -28,7 +28,9 @@ using namespace ccls;
|
||||
using namespace llvm;
|
||||
using namespace llvm::cl;
|
||||
|
||||
namespace ccls {
|
||||
std::string g_init_options;
|
||||
}
|
||||
|
||||
namespace {
|
||||
OptionCategory C("ccls options");
|
||||
@ -92,7 +94,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (opt_test_index != "!") {
|
||||
language_server = false;
|
||||
if (!RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
|
||||
if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput()))
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
#include "match.h"
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
#include "pipeline.hh"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
std::optional<Matcher> Matcher::Create(const std::string &search) {
|
||||
try {
|
||||
Matcher m;
|
||||
@ -19,7 +19,7 @@ std::optional<Matcher> Matcher::Create(const std::string &search) {
|
||||
return m;
|
||||
} catch (const std::exception &e) {
|
||||
lsShowMessageParams params;
|
||||
params.type = lsMessageType::Error;
|
||||
params.type = MessageType::Error;
|
||||
params.message =
|
||||
"failed to parse EMCAScript regex " + search + " : " + e.what();
|
||||
pipeline::Notify(window_showMessage, params);
|
||||
@ -62,3 +62,4 @@ bool GroupMatch::IsMatch(const std::string &value,
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ccls {
|
||||
struct Matcher {
|
||||
static std::optional<Matcher> Create(const std::string &search);
|
||||
|
||||
@ -28,4 +29,5 @@ struct GroupMatch {
|
||||
|
||||
std::vector<Matcher> whitelist;
|
||||
std::vector<Matcher> blacklist;
|
||||
};
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace ccls {
|
||||
// Like std::optional, but the stored data is responsible for containing the
|
||||
// empty state. T should define a function `bool T::Valid()`.
|
||||
template <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 !(*this == o); }
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -1,23 +1,55 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
|
||||
#include "log.hh"
|
||||
#include "match.h"
|
||||
#include "pipeline.hh"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
#include "serializers/json.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
#include <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 {
|
||||
|
||||
struct CclsSemanticHighlightSymbol {
|
||||
int id = 0;
|
||||
lsSymbolKind parentKind;
|
||||
@ -43,6 +75,7 @@ struct CclsSetSkippedRangesParams {
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges);
|
||||
|
||||
|
||||
struct ScanLineEvent {
|
||||
lsPosition pos;
|
||||
lsPosition end_pos; // Second key when there is a tie for insertion events.
|
||||
@ -61,55 +94,161 @@ struct ScanLineEvent {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
MessageHandler::MessageHandler() {
|
||||
// Dynamically allocate |message_handlers|, otherwise there will be static
|
||||
// initialization order races.
|
||||
if (!message_handlers)
|
||||
message_handlers = new std::vector<MessageHandler *>();
|
||||
message_handlers->push_back(this);
|
||||
void MessageHandler::Bind(const char *method, void (MessageHandler::*handler)(Reader &)) {
|
||||
method2notification[method] = [this, handler](Reader &reader) {
|
||||
(this->*handler)(reader);
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<MessageHandler *> *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,
|
||||
const std::string &absolute_path,
|
||||
QueryFile **out_query_file, int *out_file_id) {
|
||||
*out_query_file = nullptr;
|
||||
void MessageHandler::Bind(const char *method,
|
||||
void (MessageHandler::*handler)(Reader &,
|
||||
ReplyOnce &)) {
|
||||
method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) {
|
||||
(this->*handler)(reader, reply);
|
||||
};
|
||||
}
|
||||
|
||||
auto it = db->name2file_id.find(LowerPathIfInsensitive(absolute_path));
|
||||
template <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(¶m);
|
||||
if (msg.id.Valid()) {
|
||||
ReplyOnce reply{msg.id};
|
||||
auto it = method2request.find(msg.method);
|
||||
if (it != method2request.end()) {
|
||||
try {
|
||||
it->second(reader, reply);
|
||||
} catch (...) {
|
||||
LOG_S(ERROR) << "failed to process request " << msg.method;
|
||||
}
|
||||
} else {
|
||||
lsResponseError err;
|
||||
err.code = lsErrorCodes::MethodNotFound;
|
||||
err.message = "unknown request " + msg.method;
|
||||
reply.Error(err);
|
||||
}
|
||||
} else {
|
||||
auto it = method2notification.find(msg.method);
|
||||
if (it != method2notification.end())
|
||||
try {
|
||||
it->second(reader);
|
||||
} catch (...) {
|
||||
LOG_S(ERROR) << "failed to process " << msg.method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueryFile *MessageHandler::FindFile(ReplyOnce &reply,
|
||||
const std::string &path,
|
||||
int *out_file_id) {
|
||||
QueryFile *ret = nullptr;
|
||||
auto it = db->name2file_id.find(LowerPathIfInsensitive(path));
|
||||
if (it != db->name2file_id.end()) {
|
||||
QueryFile &file = db->files[it->second];
|
||||
if (file.def) {
|
||||
*out_query_file = &file;
|
||||
ret = &file;
|
||||
if (out_file_id)
|
||||
*out_file_id = it->second;
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_file_id)
|
||||
*out_file_id = -1;
|
||||
|
||||
bool has_entry = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(project->mutex_);
|
||||
for (auto &[root, folder] : project->root2folder)
|
||||
has_entry |= folder.path2entry_index.count(absolute_path);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
if (reply.id.Valid()) {
|
||||
bool has_entry = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(project->mutex_);
|
||||
for (auto &[root, folder] : project->root2folder)
|
||||
has_entry |= folder.path2entry_index.count(path);
|
||||
}
|
||||
lsResponseError err;
|
||||
if (has_entry) {
|
||||
err.code = lsErrorCodes::ServerNotInitialized;
|
||||
err.message = absolute_path + " is being indexed";
|
||||
err.message = path + " is being indexed";
|
||||
} else {
|
||||
err.code = lsErrorCodes::InternalError;
|
||||
err.message = "unable to find " + absolute_path;
|
||||
err.message = "unable to find " + path;
|
||||
}
|
||||
pipeline::ReplyError(*id, err);
|
||||
reply.Error(err);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
||||
@ -310,3 +449,4 @@ void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
||||
params.symbols.push_back(std::move(entry.second));
|
||||
pipeline::Notify("$ccls/publishSemanticHighlight", params);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -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);
|
265
src/message_handler.hh
Normal file
265
src/message_handler.hh
Normal file
@ -0,0 +1,265 @@
|
||||
/* Copyright 2017-2018 ccls Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp.hh"
|
||||
#include "query.h"
|
||||
|
||||
#include <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
|
@ -2,18 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
|
||||
namespace {
|
||||
|
||||
MethodType kMethodType = "$ccls/call";
|
||||
|
||||
enum class CallType : uint8_t {
|
||||
Direct = 0,
|
||||
Base = 1,
|
||||
@ -26,34 +24,24 @@ bool operator&(CallType lhs, CallType rhs) {
|
||||
return uint8_t(lhs) & uint8_t(rhs);
|
||||
}
|
||||
|
||||
struct In_cclsCall : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Param : TextDocumentPositionParam {
|
||||
// If id is specified, expand a node; otherwise textDocument+position should
|
||||
// be specified for building the root and |levels| of nodes below.
|
||||
Usr usr;
|
||||
std::string id;
|
||||
|
||||
struct Params {
|
||||
// If id is specified, expand a node; otherwise textDocument+position should
|
||||
// be specified for building the root and |levels| of nodes below.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
|
||||
Usr usr;
|
||||
std::string id;
|
||||
|
||||
// true: callee tree (functions called by this function); false: caller tree
|
||||
// (where this function is called)
|
||||
bool callee = false;
|
||||
// Base: include base functions; All: include both base and derived
|
||||
// functions.
|
||||
CallType callType = CallType::All;
|
||||
bool qualified = true;
|
||||
int levels = 1;
|
||||
bool hierarchy = false;
|
||||
};
|
||||
Params params;
|
||||
// true: callee tree (functions called by this function); false: caller tree
|
||||
// (where this function is called)
|
||||
bool callee = false;
|
||||
// Base: include base functions; All: include both base and derived
|
||||
// functions.
|
||||
CallType callType = CallType::All;
|
||||
bool qualified = true;
|
||||
int levels = 1;
|
||||
bool hierarchy = false;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_cclsCall::Params, textDocument, position, id, callee,
|
||||
callType, qualified, levels, hierarchy);
|
||||
MAKE_REFLECT_STRUCT(In_cclsCall, id, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsCall);
|
||||
MAKE_REFLECT_STRUCT(Param, textDocument, position, id, callee, callType,
|
||||
qualified, levels, hierarchy);
|
||||
|
||||
struct Out_cclsCall {
|
||||
Usr usr;
|
||||
@ -163,70 +151,64 @@ bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Handler_cclsCall : BaseMessageHandler<In_cclsCall> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
std::optional<Out_cclsCall> BuildInitial(MessageHandler *m, Usr root_usr,
|
||||
bool callee, CallType call_type,
|
||||
bool qualified, int levels) {
|
||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
std::optional<Out_cclsCall> BuildInitial(Usr root_usr, bool callee,
|
||||
CallType call_type, bool qualified,
|
||||
int levels) {
|
||||
const auto *def = db->Func(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
Out_cclsCall entry;
|
||||
entry.id = std::to_string(root_usr);
|
||||
entry.usr = root_usr;
|
||||
entry.callType = CallType::Direct;
|
||||
if (def->spell) {
|
||||
if (std::optional<lsLocation> loc =
|
||||
GetLsLocation(db, working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
Expand(this, &entry, callee, call_type, qualified, levels);
|
||||
return entry;
|
||||
Out_cclsCall entry;
|
||||
entry.id = std::to_string(root_usr);
|
||||
entry.usr = root_usr;
|
||||
entry.callType = CallType::Direct;
|
||||
if (def->spell) {
|
||||
if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
|
||||
void Run(In_cclsCall *request) override {
|
||||
auto ¶ms = request->params;
|
||||
std::optional<Out_cclsCall> result;
|
||||
if (params.id.size()) {
|
||||
try {
|
||||
params.usr = std::stoull(params.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(params.usr);
|
||||
result->usr = params.usr;
|
||||
result->callType = CallType::Direct;
|
||||
if (db->HasFunc(params.usr))
|
||||
Expand(this, &*result, params.callee, params.callType, params.qualified,
|
||||
params.levels);
|
||||
} else {
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *working_file =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, params.position)) {
|
||||
if (sym.kind == SymbolKind::Func) {
|
||||
result = BuildInitial(sym.usr, params.callee, params.callType,
|
||||
params.qualified, params.levels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.hierarchy)
|
||||
pipeline::Reply(request->id, result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
pipeline::Reply(request->id, out);
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsCall);
|
||||
|
||||
Expand(m, &entry, callee, call_type, qualified, levels);
|
||||
return entry;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_call(Reader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
std::optional<Out_cclsCall> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
param.usr = std::stoull(param.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
result->callType = CallType::Direct;
|
||||
if (db->HasFunc(param.usr))
|
||||
Expand(this, &*result, param.callee, param.callType, param.qualified,
|
||||
param.levels);
|
||||
} else {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *working_file =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, param.position)) {
|
||||
if (sym.kind == SymbolKind::Func) {
|
||||
result = BuildInitial(this, sym.usr, param.callee, param.callType,
|
||||
param.qualified, param.levels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
reply(out);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,24 +13,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges,
|
||||
dependencies);
|
||||
|
||||
namespace {
|
||||
MethodType cclsInfo = "$ccls/info", fileInfo = "$ccls/fileInfo";
|
||||
|
||||
struct In_cclsInfo : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return cclsInfo; }
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_cclsInfo, id);
|
||||
REGISTER_IN_MESSAGE(In_cclsInfo);
|
||||
|
||||
struct Out_cclsInfo {
|
||||
struct DB {
|
||||
int files, funcs, types, vars;
|
||||
@ -46,51 +38,33 @@ MAKE_REFLECT_STRUCT(Out_cclsInfo::DB, files, funcs, types, vars);
|
||||
MAKE_REFLECT_STRUCT(Out_cclsInfo::Pipeline, pendingIndexRequests);
|
||||
MAKE_REFLECT_STRUCT(Out_cclsInfo::Project, entries);
|
||||
MAKE_REFLECT_STRUCT(Out_cclsInfo, db, pipeline, project);
|
||||
|
||||
struct Handler_cclsInfo : BaseMessageHandler<In_cclsInfo> {
|
||||
MethodType GetMethodType() const override { return cclsInfo; }
|
||||
void Run(In_cclsInfo *request) override {
|
||||
Out_cclsInfo result;
|
||||
result.db.files = db->files.size();
|
||||
result.db.funcs = db->funcs.size();
|
||||
result.db.types = db->types.size();
|
||||
result.db.vars = db->vars.size();
|
||||
result.pipeline.pendingIndexRequests = pipeline::pending_index_requests;
|
||||
result.project.entries = 0;
|
||||
for (auto &[_, folder] : project->root2folder)
|
||||
result.project.entries += folder.entries.size();
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsInfo);
|
||||
|
||||
struct In_cclsFileInfo : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return fileInfo; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_cclsFileInfo::Params, textDocument);
|
||||
MAKE_REFLECT_STRUCT(In_cclsFileInfo, id, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsFileInfo);
|
||||
|
||||
struct Handler_cclsFileInfo : BaseMessageHandler<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;
|
||||
|
||||
QueryFile::Def result;
|
||||
// Expose some fields of |QueryFile::Def|.
|
||||
result.path = file->def->path;
|
||||
result.args = file->def->args;
|
||||
result.language = file->def->language;
|
||||
result.includes = file->def->includes;
|
||||
result.skipped_ranges = file->def->skipped_ranges;
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsFileInfo);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_info(EmptyParam &, ReplyOnce &reply) {
|
||||
Out_cclsInfo result;
|
||||
result.db.files = db->files.size();
|
||||
result.db.funcs = db->funcs.size();
|
||||
result.db.types = db->types.size();
|
||||
result.db.vars = db->vars.size();
|
||||
result.pipeline.pendingIndexRequests = pipeline::pending_index_requests;
|
||||
result.project.entries = 0;
|
||||
for (auto &[_, folder] : project->root2folder)
|
||||
result.project.entries += folder.entries.size();
|
||||
reply(result);
|
||||
}
|
||||
|
||||
void MessageHandler::ccls_fileInfo(TextDocumentParam ¶m, ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
QueryFile::Def result;
|
||||
// Expose some fields of |QueryFile::Def|.
|
||||
result.path = file->def->path;
|
||||
result.args = file->def->args;
|
||||
result.language = file->def->language;
|
||||
result.includes = file->def->includes;
|
||||
result.skipped_ranges = file->def->skipped_ranges;
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -2,41 +2,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "$ccls/inheritance",
|
||||
implementation = "textDocument/implementation";
|
||||
struct Param : TextDocumentPositionParam {
|
||||
// If id+kind are specified, expand a node; otherwise textDocument+position
|
||||
// should be specified for building the root and |levels| of nodes below.
|
||||
Usr usr;
|
||||
std::string id;
|
||||
SymbolKind kind = SymbolKind::Invalid;
|
||||
|
||||
struct In_cclsInheritance : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Params {
|
||||
// If id+kind are specified, expand a node; otherwise textDocument+position
|
||||
// should be specified for building the root and |levels| of nodes below.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
|
||||
Usr usr;
|
||||
std::string id;
|
||||
SymbolKind kind = SymbolKind::Invalid;
|
||||
|
||||
// true: derived classes/functions; false: base classes/functions
|
||||
bool derived = false;
|
||||
bool qualified = true;
|
||||
int levels = 1;
|
||||
bool hierarchy = false;
|
||||
} params;
|
||||
// true: derived classes/functions; false: base classes/functions
|
||||
bool derived = false;
|
||||
bool qualified = true;
|
||||
int levels = 1;
|
||||
bool hierarchy = false;
|
||||
};
|
||||
|
||||
MAKE_REFLECT_STRUCT(In_cclsInheritance::Params, textDocument, position, id,
|
||||
kind, derived, qualified, levels, hierarchy);
|
||||
MAKE_REFLECT_STRUCT(In_cclsInheritance, id, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsInheritance);
|
||||
MAKE_REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified,
|
||||
levels, hierarchy);
|
||||
|
||||
struct Out_cclsInheritance {
|
||||
Usr usr;
|
||||
@ -118,85 +107,68 @@ bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
|
||||
m->db->Type(entry->usr));
|
||||
}
|
||||
|
||||
struct Handler_cclsInheritance : BaseMessageHandler<In_cclsInheritance> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
std::optional<Out_cclsInheritance> BuildInitial(MessageHandler *m, SymbolRef sym, bool derived,
|
||||
bool qualified, int levels) {
|
||||
Out_cclsInheritance entry;
|
||||
entry.id = std::to_string(sym.usr);
|
||||
entry.usr = sym.usr;
|
||||
entry.kind = sym.kind;
|
||||
Expand(m, &entry, derived, qualified, levels);
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::optional<Out_cclsInheritance> BuildInitial(SymbolRef sym, bool derived,
|
||||
bool qualified, int levels) {
|
||||
Out_cclsInheritance entry;
|
||||
entry.id = std::to_string(sym.usr);
|
||||
entry.usr = sym.usr;
|
||||
entry.kind = sym.kind;
|
||||
Expand(this, &entry, derived, qualified, levels);
|
||||
return entry;
|
||||
}
|
||||
void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
|
||||
std::optional<Out_cclsInheritance> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
param.usr = std::stoull(param.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
result->kind = param.kind;
|
||||
if (!(((param.kind == SymbolKind::Func && m->db->HasFunc(param.usr)) ||
|
||||
(param.kind == SymbolKind::Type && m->db->HasType(param.usr))) &&
|
||||
Expand(m, &*result, param.derived, param.qualified,
|
||||
param.levels)))
|
||||
result.reset();
|
||||
} else {
|
||||
QueryFile *file = m->FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = m->working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
void Run(In_cclsInheritance *request) override {
|
||||
auto ¶ms = request->params;
|
||||
std::optional<Out_cclsInheritance> result;
|
||||
if (params.id.size()) {
|
||||
try {
|
||||
params.usr = std::stoull(params.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position))
|
||||
if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) {
|
||||
result = BuildInitial(m, sym, param.derived, param.qualified,
|
||||
param.levels);
|
||||
break;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(params.usr);
|
||||
result->usr = params.usr;
|
||||
result->kind = params.kind;
|
||||
if (!(((params.kind == SymbolKind::Func && db->HasFunc(params.usr)) ||
|
||||
(params.kind == SymbolKind::Type && db->HasType(params.usr))) &&
|
||||
Expand(this, &*result, params.derived, params.qualified,
|
||||
params.levels)))
|
||||
result.reset();
|
||||
} else {
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position))
|
||||
if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) {
|
||||
result = BuildInitial(sym, params.derived, params.qualified,
|
||||
params.levels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.hierarchy)
|
||||
pipeline::Reply(request->id, result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
pipeline::Reply(request->id, out);
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsInheritance);
|
||||
|
||||
struct In_textDocumentImplementation : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return implementation; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentImplementation, id, params);
|
||||
REGISTER_IN_MESSAGE(In_textDocumentImplementation);
|
||||
|
||||
struct Handler_textDocumentImplementation
|
||||
: BaseMessageHandler<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);
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
reply(out);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_textDocumentImplementation);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_inheritance(Reader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
Inheritance(this, param, reply);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_implementation(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
Param param1;
|
||||
param1.textDocument = param.textDocument;
|
||||
param1.position = param.position;
|
||||
param1.derived = true;
|
||||
Inheritance(this, param1, reply);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "clang_tu.hh"
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
@ -12,38 +12,26 @@
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
MethodType kMethodType = "$ccls/member";
|
||||
struct Param : TextDocumentPositionParam {
|
||||
// If id is specified, expand a node; otherwise textDocument+position should
|
||||
// be specified for building the root and |levels| of nodes below.
|
||||
Usr usr;
|
||||
std::string id;
|
||||
|
||||
struct In_cclsMember : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
struct Params {
|
||||
// If id is specified, expand a node; otherwise textDocument+position should
|
||||
// be specified for building the root and |levels| of nodes below.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
|
||||
// Type
|
||||
Usr usr;
|
||||
std::string id;
|
||||
|
||||
bool qualified = false;
|
||||
int levels = 1;
|
||||
// If SymbolKind::Func and the point is at a type, list member functions
|
||||
// instead of member variables.
|
||||
SymbolKind kind = SymbolKind::Var;
|
||||
bool hierarchy = false;
|
||||
} params;
|
||||
bool qualified = false;
|
||||
int levels = 1;
|
||||
// If SymbolKind::Func and the point is at a type, list member functions
|
||||
// instead of member variables.
|
||||
SymbolKind kind = SymbolKind::Var;
|
||||
bool hierarchy = false;
|
||||
};
|
||||
|
||||
MAKE_REFLECT_STRUCT(In_cclsMember::Params, textDocument, position, id,
|
||||
qualified, levels, kind, hierarchy);
|
||||
MAKE_REFLECT_STRUCT(In_cclsMember, id, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsMember);
|
||||
MAKE_REFLECT_STRUCT(Param, textDocument, position, id, qualified, levels, kind,
|
||||
hierarchy);
|
||||
|
||||
struct Out_cclsMember {
|
||||
Usr usr;
|
||||
@ -221,106 +209,99 @@ bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
std::optional<Out_cclsMember> BuildInitial(SymbolKind kind, Usr root_usr,
|
||||
bool qualified, int levels,
|
||||
SymbolKind memberKind) {
|
||||
switch (kind) {
|
||||
default:
|
||||
std::optional<Out_cclsMember> BuildInitial(MessageHandler *m, SymbolKind kind,
|
||||
Usr root_usr, bool qualified,
|
||||
int levels, SymbolKind memberKind) {
|
||||
switch (kind) {
|
||||
default:
|
||||
return {};
|
||||
case SymbolKind::Func: {
|
||||
const auto *def = m->db->Func(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
case SymbolKind::Func: {
|
||||
const auto *def = db->Func(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
Out_cclsMember entry;
|
||||
// Not type, |id| is invalid.
|
||||
entry.name = def->Name(qualified);
|
||||
if (def->spell) {
|
||||
if (std::optional<lsLocation> loc =
|
||||
GetLsLocation(db, working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
for (Usr usr : def->vars) {
|
||||
auto &var = db->Var(usr);
|
||||
if (var.def.size())
|
||||
DoField(this, &entry, var, -1, qualified, levels - 1);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
const auto *def = db->Type(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
Out_cclsMember entry;
|
||||
entry.id = std::to_string(root_usr);
|
||||
entry.usr = root_usr;
|
||||
if (def->spell) {
|
||||
if (std::optional<lsLocation> loc =
|
||||
GetLsLocation(db, working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
Expand(this, &entry, qualified, levels, memberKind);
|
||||
return entry;
|
||||
Out_cclsMember entry;
|
||||
// Not type, |id| is invalid.
|
||||
entry.name = def->Name(qualified);
|
||||
if (def->spell) {
|
||||
if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
for (Usr usr : def->vars) {
|
||||
auto &var = m->db->Var(usr);
|
||||
if (var.def.size())
|
||||
DoField(m, &entry, var, -1, qualified, levels - 1);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
const auto *def = m->db->Type(root_usr).AnyDef();
|
||||
if (!def)
|
||||
return {};
|
||||
|
||||
void Run(In_cclsMember *request) override {
|
||||
auto ¶ms = request->params;
|
||||
std::optional<Out_cclsMember> result;
|
||||
if (params.id.size()) {
|
||||
try {
|
||||
params.usr = std::stoull(params.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(params.usr);
|
||||
result->usr = params.usr;
|
||||
// entry.name is empty as it is known by the client.
|
||||
if (!(db->HasType(params.usr) && Expand(this, &*result, params.qualified,
|
||||
params.levels, params.kind)))
|
||||
result.reset();
|
||||
} else {
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(wfile, file, params.position)) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Func:
|
||||
case SymbolKind::Type:
|
||||
result = BuildInitial(sym.kind, sym.usr, params.qualified,
|
||||
params.levels, params.kind);
|
||||
break;
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
if (def && def->type)
|
||||
result = BuildInitial(SymbolKind::Type, def->type, params.qualified,
|
||||
params.levels, params.kind);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
Out_cclsMember entry;
|
||||
entry.id = std::to_string(root_usr);
|
||||
entry.usr = root_usr;
|
||||
if (def->spell) {
|
||||
if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
|
||||
entry.location = *loc;
|
||||
}
|
||||
Expand(m, &entry, qualified, levels, memberKind);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_member(Reader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
std::optional<Out_cclsMember> result;
|
||||
if (param.id.size()) {
|
||||
try {
|
||||
param.usr = std::stoull(param.id);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
result.emplace();
|
||||
result->id = std::to_string(param.usr);
|
||||
result->usr = param.usr;
|
||||
// entry.name is empty as it is known by the client.
|
||||
if (!(db->HasType(param.usr) && Expand(this, &*result, param.qualified,
|
||||
param.levels, param.kind)))
|
||||
result.reset();
|
||||
} else {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(wfile, file, param.position)) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Func:
|
||||
case SymbolKind::Type:
|
||||
result = BuildInitial(this, sym.kind, sym.usr, param.qualified,
|
||||
param.levels, param.kind);
|
||||
break;
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
if (def && def->type)
|
||||
result = BuildInitial(this, SymbolKind::Type, def->type, param.qualified,
|
||||
param.levels, param.kind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.hierarchy)
|
||||
pipeline::Reply(request->id, result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
pipeline::Reply(request->id, out);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsMember);
|
||||
|
||||
} // namespace
|
||||
if (param.hierarchy)
|
||||
reply(result);
|
||||
else {
|
||||
auto out = FlattenHierarchy(result);
|
||||
reply(out);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,25 +13,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "$ccls/navigate";
|
||||
|
||||
struct In_CclsNavigate : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
std::string direction;
|
||||
} params;
|
||||
struct Param {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
std::string direction;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_CclsNavigate::Params, textDocument, position, direction);
|
||||
MAKE_REFLECT_STRUCT(In_CclsNavigate, id, params);
|
||||
REGISTER_IN_MESSAGE(In_CclsNavigate);
|
||||
MAKE_REFLECT_STRUCT(Param, textDocument, position, direction);
|
||||
|
||||
Maybe<Range> FindParent(QueryFile *file, Position pos) {
|
||||
Maybe<Range> parent;
|
||||
@ -44,75 +36,72 @@ Maybe<Range> FindParent(QueryFile *file, Position pos) {
|
||||
parent = sym.extent;
|
||||
return parent;
|
||||
}
|
||||
|
||||
struct Handler_CclsNavigate : BaseMessageHandler<In_CclsNavigate> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void Run(In_CclsNavigate *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
|
||||
WorkingFile *wfile =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
lsPosition ls_pos = request->params.position;
|
||||
if (wfile && wfile->index_lines.size())
|
||||
if (auto line = wfile->GetIndexPosFromBufferPos(
|
||||
ls_pos.line, &ls_pos.character, false))
|
||||
ls_pos.line = *line;
|
||||
Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
|
||||
|
||||
Maybe<Range> res;
|
||||
switch (params.direction[0]) {
|
||||
case 'D': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && pos < sym.extent.start &&
|
||||
(!parent || sym.extent.end <= parent->end) &&
|
||||
(!res || sym.extent.start < res->start))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos &&
|
||||
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start
|
||||
: res->end < sym.extent.end)))
|
||||
res = sym.extent;
|
||||
break;
|
||||
case 'R': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
if (parent && parent->start.line == pos.line && pos < parent->end) {
|
||||
pos = parent->end;
|
||||
if (pos.column)
|
||||
pos.column--;
|
||||
}
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start &&
|
||||
(!res ||
|
||||
(sym.extent.start == res->start ? res->end < sym.extent.end
|
||||
: sym.extent.start < res->start)))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
default:
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos &&
|
||||
pos < sym.extent.end && (!res || res->start < sym.extent.start))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
std::vector<lsLocation> result;
|
||||
if (res)
|
||||
if (auto ls_range = GetLsRange(wfile, *res)) {
|
||||
lsLocation &ls_loc = result.emplace_back();
|
||||
ls_loc.uri = params.textDocument.uri;
|
||||
ls_loc.range = *ls_range;
|
||||
}
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_CclsNavigate);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_navigate(Reader &reader,
|
||||
ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsPosition ls_pos = param.position;
|
||||
if (wfile && wfile->index_lines.size())
|
||||
if (auto line = wfile->GetIndexPosFromBufferPos(ls_pos.line,
|
||||
&ls_pos.character, false))
|
||||
ls_pos.line = *line;
|
||||
Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character};
|
||||
|
||||
Maybe<Range> res;
|
||||
switch (param.direction[0]) {
|
||||
case 'D': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && pos < sym.extent.start &&
|
||||
(!parent || sym.extent.end <= parent->end) &&
|
||||
(!res || sym.extent.start < res->start))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.end <= pos &&
|
||||
(!res || (res->end == sym.extent.end ? sym.extent.start < res->start
|
||||
: res->end < sym.extent.end)))
|
||||
res = sym.extent;
|
||||
break;
|
||||
case 'R': {
|
||||
Maybe<Range> parent = FindParent(file, pos);
|
||||
if (parent && parent->start.line == pos.line && pos < parent->end) {
|
||||
pos = parent->end;
|
||||
if (pos.column)
|
||||
pos.column--;
|
||||
}
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && pos < sym.extent.start &&
|
||||
(!res ||
|
||||
(sym.extent.start == res->start ? res->end < sym.extent.end
|
||||
: sym.extent.start < res->start)))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
default:
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() && sym.extent.start < pos &&
|
||||
pos < sym.extent.end && (!res || res->start < sym.extent.start))
|
||||
res = sym.extent;
|
||||
break;
|
||||
}
|
||||
std::vector<lsLocation> result;
|
||||
if (res)
|
||||
if (auto ls_range = GetLsRange(wfile, *res)) {
|
||||
lsLocation &ls_loc = result.emplace_back();
|
||||
ls_loc.uri = param.textDocument.uri;
|
||||
ls_loc.range = *ls_range;
|
||||
}
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -3,80 +3,34 @@
|
||||
|
||||
#include "clang_complete.hh"
|
||||
#include "match.h"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "$ccls/reload";
|
||||
|
||||
struct In_cclsReload : public NotificationMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Params {
|
||||
bool dependencies = true;
|
||||
std::vector<std::string> whitelist;
|
||||
std::vector<std::string> blacklist;
|
||||
} params;
|
||||
struct Param {
|
||||
bool dependencies = true;
|
||||
std::vector<std::string> whitelist;
|
||||
std::vector<std::string> blacklist;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_cclsReload::Params, dependencies, whitelist, blacklist);
|
||||
MAKE_REFLECT_STRUCT(In_cclsReload, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsReload);
|
||||
|
||||
struct Handler_cclsReload : BaseMessageHandler<In_cclsReload> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void Run(In_cclsReload *request) override {
|
||||
const auto ¶ms = request->params;
|
||||
// Send index requests for every file.
|
||||
if (params.whitelist.empty() && params.blacklist.empty()) {
|
||||
vfs->Clear();
|
||||
db->clear();
|
||||
project->Index(working_files, lsRequestId());
|
||||
clang_complete->FlushAllSessions();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
GroupMatch matcher(params.whitelist, params.blacklist);
|
||||
|
||||
std::queue<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);
|
||||
}
|
||||
|
||||
while (!q.empty()) {
|
||||
const QueryFile *file = q.front();
|
||||
q.pop();
|
||||
need_index.insert(file->def->path);
|
||||
|
||||
if (request->params.dependencies)
|
||||
for (const std::string &path : graph[file->def->path]) {
|
||||
auto it = path_to_file.find(path);
|
||||
if (it != path_to_file.end()) {
|
||||
q.push(it->second);
|
||||
path_to_file.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsReload);
|
||||
MAKE_REFLECT_STRUCT(Param, dependencies, whitelist, blacklist);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_reload(Reader &reader) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
// Send index requests for every file.
|
||||
if (param.whitelist.empty() && param.blacklist.empty()) {
|
||||
vfs->Clear();
|
||||
db->clear();
|
||||
project->Index(working_files, lsRequestId());
|
||||
clang_complete->FlushAllSessions();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,63 +1,50 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "$ccls/vars";
|
||||
|
||||
struct In_cclsVars : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Params : lsTextDocumentPositionParams {
|
||||
// 1: field
|
||||
// 2: local
|
||||
// 4: parameter
|
||||
unsigned kind = ~0u;
|
||||
} params;
|
||||
struct Param : TextDocumentPositionParam {
|
||||
// 1: field
|
||||
// 2: local
|
||||
// 4: parameter
|
||||
unsigned kind = ~0u;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_cclsVars::Params, textDocument, position, kind);
|
||||
MAKE_REFLECT_STRUCT(In_cclsVars, id, params);
|
||||
REGISTER_IN_MESSAGE(In_cclsVars);
|
||||
|
||||
struct Handler_cclsVars : BaseMessageHandler<In_cclsVars> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
void Run(In_cclsVars *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
|
||||
WorkingFile *working_file =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
std::vector<lsLocation> result;
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, params.position)) {
|
||||
Usr usr = sym.usr;
|
||||
switch (sym.kind) {
|
||||
default:
|
||||
break;
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
if (!def || !def->type)
|
||||
continue;
|
||||
usr = def->type;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case SymbolKind::Type:
|
||||
result = GetLsLocations(
|
||||
db, working_files,
|
||||
GetVarDeclarations(db, db->Type(usr).instances, params.kind));
|
||||
break;
|
||||
}
|
||||
}
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_cclsVars);
|
||||
MAKE_REFLECT_STRUCT(Param, textDocument, position, kind);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::ccls_vars(Reader &reader, ReplyOnce &reply) {
|
||||
Param param;
|
||||
Reflect(reader, param);
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *working_file = working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
std::vector<lsLocation> result;
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, param.position)) {
|
||||
Usr usr = sym.usr;
|
||||
switch (sym.kind) {
|
||||
default:
|
||||
break;
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
if (!def || !def->type)
|
||||
continue;
|
||||
usr = def->type;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case SymbolKind::Type:
|
||||
result = GetLsLocations(
|
||||
db, working_files,
|
||||
GetVarDeclarations(db, db->Type(usr).instances, param.kind));
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -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
|
@ -5,20 +5,21 @@
|
||||
#include "filesystem.hh"
|
||||
#include "include_complete.h"
|
||||
#include "log.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.h"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "serializers/json.h"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
using namespace llvm;
|
||||
|
||||
// TODO Cleanup global variables
|
||||
@ -26,8 +27,6 @@ extern std::string g_init_options;
|
||||
|
||||
namespace {
|
||||
|
||||
MethodType kMethodType = "initialize";
|
||||
|
||||
// Code Lens options.
|
||||
struct lsCodeLensOptions {
|
||||
// Code lens has a resolve provider as well.
|
||||
@ -316,7 +315,7 @@ struct lsInitializeParams {
|
||||
// The initial trace setting. If omitted trace is disabled ('off').
|
||||
lsTrace trace = lsTrace::Off;
|
||||
|
||||
std::vector<lsWorkspaceFolder> workspaceFolders;
|
||||
std::vector<WorkspaceFolder> workspaceFolders;
|
||||
};
|
||||
|
||||
void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
|
||||
@ -336,14 +335,6 @@ void Reflect(Reader &reader, lsInitializeParams::lsTrace &value) {
|
||||
MAKE_REFLECT_STRUCT(lsInitializeParams, rootUri, initializationOptions,
|
||||
capabilities, trace, workspaceFolders);
|
||||
|
||||
struct In_InitializeRequest : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
lsInitializeParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_InitializeRequest, id, params);
|
||||
REGISTER_IN_MESSAGE(In_InitializeRequest);
|
||||
|
||||
struct lsInitializeResult {
|
||||
lsServerCapabilities capabilities;
|
||||
};
|
||||
@ -361,120 +352,124 @@ void *Indexer(void *arg_) {
|
||||
h->working_files);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
void Run(In_InitializeRequest *request) override {
|
||||
auto ¶ms = request->params;
|
||||
if (!params.rootUri)
|
||||
return;
|
||||
std::string project_path = NormalizePath(params.rootUri->GetPath());
|
||||
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
|
||||
<< params.rootUri->raw_uri;
|
||||
|
||||
{
|
||||
g_config = new Config(params.initializationOptions);
|
||||
rapidjson::Document reader;
|
||||
reader.Parse(g_init_options.c_str());
|
||||
if (!reader.HasParseError()) {
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
Reflect(json_reader, *g_config);
|
||||
} catch (std::invalid_argument &) {
|
||||
// This will not trigger because parse error is handled in
|
||||
// MessageRegistry::Parse in lsp.cc
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||
JsonWriter json_writer(&writer);
|
||||
Reflect(json_writer, *g_config);
|
||||
LOG_S(INFO) << "initializationOptions: " << output.GetString();
|
||||
|
||||
if (g_config->cacheDirectory.size()) {
|
||||
g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory);
|
||||
EnsureEndsInSlash(g_config->cacheDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// Client capabilities
|
||||
const auto &capabilities = params.capabilities;
|
||||
g_config->client.snippetSupport &=
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport;
|
||||
g_config->client.hierarchicalDocumentSymbolSupport &=
|
||||
capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport;
|
||||
|
||||
// Ensure there is a resource directory.
|
||||
if (g_config->clang.resourceDir.empty())
|
||||
g_config->clang.resourceDir = GetDefaultResourceDirectory();
|
||||
DoPathMapping(g_config->clang.resourceDir);
|
||||
LOG_S(INFO) << "Using -resource-dir=" << g_config->clang.resourceDir;
|
||||
|
||||
// Send initialization before starting indexers, so we don't send a
|
||||
// status update too early.
|
||||
{
|
||||
lsInitializeResult result;
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
|
||||
// Set project root.
|
||||
EnsureEndsInSlash(project_path);
|
||||
g_config->fallbackFolder = project_path;
|
||||
for (const lsWorkspaceFolder &wf : request->params.workspaceFolders) {
|
||||
std::string path = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(path);
|
||||
g_config->workspaceFolders.push_back(path);
|
||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
|
||||
}
|
||||
if (request->params.workspaceFolders.empty())
|
||||
g_config->workspaceFolders.push_back(project_path);
|
||||
if (g_config->cacheDirectory.size())
|
||||
for (const std::string &folder : g_config->workspaceFolders) {
|
||||
// Create two cache directories for files inside and outside of the
|
||||
// project.
|
||||
std::string escaped =
|
||||
EscapeFileName(folder.substr(0, folder.size() - 1));
|
||||
sys::fs::create_directories(g_config->cacheDirectory + escaped);
|
||||
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
|
||||
}
|
||||
|
||||
idx::Init();
|
||||
for (const std::string &folder : g_config->workspaceFolders)
|
||||
project->Load(folder);
|
||||
|
||||
// Start indexer threads. Start this after loading the project, as that
|
||||
// may take a long time. Indexer threads will emit status/progress
|
||||
// reports.
|
||||
if (g_config->index.threads == 0)
|
||||
g_config->index.threads = std::thread::hardware_concurrency();
|
||||
|
||||
LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
|
||||
for (int i = 0; i < g_config->index.threads; i++)
|
||||
SpawnThread(Indexer, new std::pair<MessageHandler *, int>{this, i});
|
||||
|
||||
// Start scanning include directories before dispatching project
|
||||
// files, because that takes a long time.
|
||||
include_complete->Rescan();
|
||||
|
||||
LOG_S(INFO) << "dispatch initial index requests";
|
||||
project->Index(working_files, request->id);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_Initialize);
|
||||
} // namespace
|
||||
|
||||
void StandaloneInitialize(const std::string &root, Project &project,
|
||||
WorkingFiles &wfiles, VFS &vfs,
|
||||
IncludeComplete &complete) {
|
||||
Handler_Initialize handler;
|
||||
handler.project = &project;
|
||||
handler.working_files = &wfiles;
|
||||
handler.vfs = &vfs;
|
||||
handler.include_complete = &complete;
|
||||
void Initialize(MessageHandler *m, lsInitializeParams ¶m, ReplyOnce &reply) {
|
||||
std::string project_path = NormalizePath(param.rootUri->GetPath());
|
||||
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
|
||||
<< param.rootUri->raw_uri;
|
||||
|
||||
In_InitializeRequest request;
|
||||
request.params.rootUri = lsDocumentUri::FromPath(root);
|
||||
handler.Run(&request);
|
||||
{
|
||||
g_config = new Config(param.initializationOptions);
|
||||
rapidjson::Document reader;
|
||||
reader.Parse(g_init_options.c_str());
|
||||
if (!reader.HasParseError()) {
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
Reflect(json_reader, *g_config);
|
||||
} catch (std::invalid_argument &) {
|
||||
// This will not trigger because parse error is handled in
|
||||
// MessageRegistry::Parse in lsp.cc
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson::StringBuffer output;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
||||
JsonWriter json_writer(&writer);
|
||||
Reflect(json_writer, *g_config);
|
||||
LOG_S(INFO) << "initializationOptions: " << output.GetString();
|
||||
|
||||
if (g_config->cacheDirectory.size()) {
|
||||
g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory);
|
||||
EnsureEndsInSlash(g_config->cacheDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// Client capabilities
|
||||
const auto &capabilities = param.capabilities;
|
||||
g_config->client.snippetSupport &=
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport;
|
||||
g_config->client.hierarchicalDocumentSymbolSupport &=
|
||||
capabilities.textDocument.documentSymbol
|
||||
.hierarchicalDocumentSymbolSupport;
|
||||
|
||||
// Ensure there is a resource directory.
|
||||
if (g_config->clang.resourceDir.empty())
|
||||
g_config->clang.resourceDir = GetDefaultResourceDirectory();
|
||||
DoPathMapping(g_config->clang.resourceDir);
|
||||
LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir;
|
||||
|
||||
// Send initialization before starting indexers, so we don't send a
|
||||
// status update too early.
|
||||
{
|
||||
lsInitializeResult result;
|
||||
reply(result);
|
||||
}
|
||||
|
||||
// Set project root.
|
||||
EnsureEndsInSlash(project_path);
|
||||
g_config->fallbackFolder = project_path;
|
||||
for (const WorkspaceFolder &wf : param.workspaceFolders) {
|
||||
std::string path = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(path);
|
||||
g_config->workspaceFolders.push_back(path);
|
||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << path;
|
||||
}
|
||||
if (param.workspaceFolders.empty())
|
||||
g_config->workspaceFolders.push_back(project_path);
|
||||
if (g_config->cacheDirectory.size())
|
||||
for (const std::string &folder : g_config->workspaceFolders) {
|
||||
// Create two cache directories for files inside and outside of the
|
||||
// project.
|
||||
std::string escaped = EscapeFileName(folder.substr(0, folder.size() - 1));
|
||||
sys::fs::create_directories(g_config->cacheDirectory + escaped);
|
||||
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
|
||||
}
|
||||
|
||||
idx::Init();
|
||||
for (const std::string &folder : g_config->workspaceFolders)
|
||||
m->project->Load(folder);
|
||||
|
||||
// Start indexer threads. Start this after loading the project, as that
|
||||
// may take a long time. Indexer threads will emit status/progress
|
||||
// reports.
|
||||
if (g_config->index.threads == 0)
|
||||
g_config->index.threads = std::thread::hardware_concurrency();
|
||||
|
||||
LOG_S(INFO) << "start " << g_config->index.threads << " indexers";
|
||||
for (int i = 0; i < g_config->index.threads; i++)
|
||||
SpawnThread(Indexer, new std::pair<MessageHandler *, int>{m, i});
|
||||
|
||||
// Start scanning include directories before dispatching project
|
||||
// files, because that takes a long time.
|
||||
m->include_complete->Rescan();
|
||||
|
||||
LOG_S(INFO) << "dispatch initial index requests";
|
||||
m->project->Index(m->working_files, reply.id);
|
||||
}
|
||||
|
||||
void MessageHandler::initialize(Reader &reader, ReplyOnce &reply) {
|
||||
lsInitializeParams param;
|
||||
Reflect(reader, param);
|
||||
if (!param.rootUri)
|
||||
return;
|
||||
Initialize(this, param, reply);
|
||||
}
|
||||
|
||||
void StandaloneInitialize(MessageHandler &handler, const std::string &root) {
|
||||
lsInitializeParams param;
|
||||
param.rootUri = lsDocumentUri::FromPath(root);
|
||||
ReplyOnce reply;
|
||||
Initialize(&handler, param, reply);
|
||||
}
|
||||
|
||||
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
||||
JsonNull result;
|
||||
reply(result);
|
||||
}
|
||||
|
||||
void MessageHandler::exit(EmptyParam &) {
|
||||
// FIXME cancel threads
|
||||
::exit(0);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -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
|
234
src/messages/textDocument_code.cc
Normal file
234
src/messages/textDocument_code.cc
Normal file
@ -0,0 +1,234 @@
|
||||
/* Copyright 2017-2018 ccls Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
#include "serializers/json.h"
|
||||
|
||||
#include <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 ¶m,
|
||||
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 ¶m,
|
||||
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
|
@ -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 ¶ms = 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
|
@ -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 ¶ms = 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 ¶ms = request->params;
|
||||
if (params.arguments.empty())
|
||||
return;
|
||||
rapidjson::Document reader;
|
||||
reader.Parse(params.arguments[0].c_str());
|
||||
JsonReader json_reader{&reader};
|
||||
if (params.command == ccls_xref) {
|
||||
Cmd_xref cmd;
|
||||
Reflect(json_reader, cmd);
|
||||
std::vector<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
|
@ -5,7 +5,7 @@
|
||||
#include "fuzzy_match.h"
|
||||
#include "include_complete.h"
|
||||
#include "log.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "working_files.h"
|
||||
|
||||
@ -14,62 +14,23 @@
|
||||
|
||||
#include <regex>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/completion";
|
||||
|
||||
// How a completion was triggered
|
||||
enum class lsCompletionTriggerKind {
|
||||
// Completion was triggered by typing an identifier (24x7 code
|
||||
// complete), manual invocation (e.g Ctrl+Space) or via API.
|
||||
Invoked = 1,
|
||||
// Completion was triggered by a trigger character specified by
|
||||
// the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
|
||||
TriggerCharacter = 2,
|
||||
// Completion was re-triggered as the current completion list is incomplete.
|
||||
TriggerForIncompleteCompletions = 3,
|
||||
};
|
||||
MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind);
|
||||
|
||||
// Contains additional information about the context in which a completion
|
||||
// request is triggered.
|
||||
struct lsCompletionContext {
|
||||
// How the completion was triggered.
|
||||
lsCompletionTriggerKind triggerKind = lsCompletionTriggerKind::Invoked;
|
||||
|
||||
// The trigger character (a single character) that has trigger code complete.
|
||||
// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
|
||||
std::optional<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 {
|
||||
// This list it not complete. Further typing should result in recomputing
|
||||
// this list.
|
||||
bool isIncomplete = false;
|
||||
// The completion 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);
|
||||
|
||||
namespace {
|
||||
void DecorateIncludePaths(const std::smatch &match,
|
||||
std::vector<lsCompletionItem> *items) {
|
||||
std::string spaces_after_include = " ";
|
||||
@ -473,139 +434,129 @@ public:
|
||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
struct Handler_TextDocumentCompletion
|
||||
: BaseMessageHandler<In_TextDocumentComplete> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void MessageHandler::textDocument_completion(lsCompletionParams ¶m,
|
||||
ReplyOnce &reply) {
|
||||
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
|
||||
lsCompletionList result;
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
WorkingFile *file = working_files->GetFileByFilename(path);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Run(In_TextDocumentComplete *request) override {
|
||||
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
|
||||
// It shouldn't be possible, but sometimes vscode will send queries out
|
||||
// of order, ie, we get completion request before buffer content update.
|
||||
std::string buffer_line;
|
||||
if (param.position.line >= 0 &&
|
||||
param.position.line < file->buffer_lines.size())
|
||||
buffer_line = file->buffer_lines[param.position.line];
|
||||
|
||||
const auto ¶ms = request->params;
|
||||
lsCompletionList result;
|
||||
// Check for - and : before completing -> or ::, since vscode does not
|
||||
// support multi-character trigger characters.
|
||||
if (param.context.triggerKind == lsCompletionTriggerKind::TriggerCharacter &&
|
||||
param.context.triggerCharacter) {
|
||||
bool did_fail_check = false;
|
||||
|
||||
std::string path = params.textDocument.uri.GetPath();
|
||||
WorkingFile *file = working_files->GetFileByFilename(path);
|
||||
if (!file) {
|
||||
pipeline::Reply(request->id, result);
|
||||
std::string character = *param.context.triggerCharacter;
|
||||
int preceding_index = param.position.character - 2;
|
||||
|
||||
// If the character is '"', '<' or '/', make sure that the line starts
|
||||
// with '#'.
|
||||
if (character == "\"" || character == "<" || character == "/") {
|
||||
size_t i = 0;
|
||||
while (i < buffer_line.size() && isspace(buffer_line[i]))
|
||||
++i;
|
||||
if (i >= buffer_line.size() || buffer_line[i] != '#')
|
||||
did_fail_check = true;
|
||||
}
|
||||
// If the character is > or : and we are at the start of the line, do not
|
||||
// show completion results.
|
||||
else if ((character == ">" || character == ":") && preceding_index < 0) {
|
||||
did_fail_check = true;
|
||||
}
|
||||
// If the character is > but - does not preced it, or if it is : and :
|
||||
// does not preced it, do not show completion results.
|
||||
else if (preceding_index >= 0 &&
|
||||
preceding_index < (int)buffer_line.size()) {
|
||||
char preceding = buffer_line[preceding_index];
|
||||
did_fail_check = (preceding != '-' && character == ">") ||
|
||||
(preceding != ':' && character == ":");
|
||||
}
|
||||
|
||||
if (did_fail_check) {
|
||||
reply(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// It shouldn't be possible, but sometimes vscode will send queries out
|
||||
// of order, ie, we get completion request before buffer content update.
|
||||
std::string buffer_line;
|
||||
if (params.position.line >= 0 &&
|
||||
params.position.line < file->buffer_lines.size())
|
||||
buffer_line = file->buffer_lines[params.position.line];
|
||||
std::string completion_text;
|
||||
lsPosition end_pos = param.position;
|
||||
lsPosition begin_pos = file->FindStableCompletionSource(
|
||||
param.position, &completion_text, &end_pos);
|
||||
|
||||
// Check for - and : before completing -> or ::, since vscode does not
|
||||
// support multi-character trigger characters.
|
||||
if (params.context.triggerKind ==
|
||||
lsCompletionTriggerKind::TriggerCharacter &&
|
||||
params.context.triggerCharacter) {
|
||||
bool did_fail_check = false;
|
||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||
|
||||
std::string character = *params.context.triggerCharacter;
|
||||
int preceding_index = params.position.character - 2;
|
||||
|
||||
// If the character is '"', '<' or '/', make sure that the line starts
|
||||
// with '#'.
|
||||
if (character == "\"" || character == "<" || character == "/") {
|
||||
size_t i = 0;
|
||||
while (i < buffer_line.size() && isspace(buffer_line[i]))
|
||||
++i;
|
||||
if (i >= buffer_line.size() || buffer_line[i] != '#')
|
||||
did_fail_check = true;
|
||||
}
|
||||
// If the character is > or : and we are at the start of the line, do not
|
||||
// show completion results.
|
||||
else if ((character == ">" || character == ":") && preceding_index < 0) {
|
||||
did_fail_check = true;
|
||||
}
|
||||
// If the character is > but - does not preced it, or if it is : and :
|
||||
// does not preced it, do not show completion results.
|
||||
else if (preceding_index >= 0 &&
|
||||
preceding_index < (int)buffer_line.size()) {
|
||||
char preceding = buffer_line[preceding_index];
|
||||
did_fail_check = (preceding != '-' && character == ">") ||
|
||||
(preceding != ':' && character == ":");
|
||||
}
|
||||
|
||||
if (did_fail_check) {
|
||||
pipeline::Reply(request->id, result);
|
||||
return;
|
||||
}
|
||||
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
|
||||
lsCompletionList result;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(
|
||||
include_complete->completion_items_mutex, std::defer_lock);
|
||||
if (include_complete->is_scanning)
|
||||
lock.lock();
|
||||
std::string quote = preprocess.match[5];
|
||||
for (auto &item : include_complete->completion_items)
|
||||
if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
|
||||
result.items.push_back(item);
|
||||
}
|
||||
begin_pos.character = 0;
|
||||
end_pos.character = (int)buffer_line.size();
|
||||
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
|
||||
buffer_line);
|
||||
DecorateIncludePaths(preprocess.match, &result.items);
|
||||
reply(result);
|
||||
} else {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
CompletionManager::OnComplete callback =
|
||||
[completion_text, path, begin_pos, end_pos, reply,
|
||||
buffer_line](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||
lsCompletionList result;
|
||||
result.items = Consumer->ls_items;
|
||||
|
||||
std::string completion_text;
|
||||
lsPosition end_pos = params.position;
|
||||
lsPosition begin_pos = file->FindStableCompletionSource(
|
||||
params.position, &completion_text, &end_pos);
|
||||
FilterCandidates(result, completion_text, begin_pos, end_pos,
|
||||
buffer_line);
|
||||
reply(result);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_items;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||
|
||||
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
|
||||
lsCompletionList result;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(
|
||||
include_complete->completion_items_mutex, std::defer_lock);
|
||||
if (include_complete->is_scanning)
|
||||
lock.lock();
|
||||
std::string quote = preprocess.match[5];
|
||||
for (auto &item : include_complete->completion_items)
|
||||
if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
|
||||
result.items.push_back(item);
|
||||
}
|
||||
begin_pos.character = 0;
|
||||
end_pos.character = (int)buffer_line.size();
|
||||
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
|
||||
buffer_line);
|
||||
DecorateIncludePaths(preprocess.match, &result.items);
|
||||
pipeline::Reply(request->id, result);
|
||||
} else {
|
||||
std::string path = params.textDocument.uri.GetPath();
|
||||
CompletionManager::OnComplete callback =
|
||||
[completion_text, path, begin_pos, end_pos,
|
||||
id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||
lsCompletionList result;
|
||||
result.items = Consumer->ls_items;
|
||||
|
||||
FilterCandidates(result, completion_text, begin_pos, end_pos,
|
||||
buffer_line);
|
||||
pipeline::Reply(id, result);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_items;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
clang::CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeBriefComments = true;
|
||||
CCOpts.IncludeCodePatterns = preprocess.ok; // if there is a #
|
||||
clang::CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeBriefComments = true;
|
||||
CCOpts.IncludeCodePatterns = preprocess.ok; // if there is a #
|
||||
#if LLVM_VERSION_MAJOR >= 7
|
||||
CCOpts.IncludeFixIts = true;
|
||||
CCOpts.IncludeFixIts = true;
|
||||
#endif
|
||||
CCOpts.IncludeMacros = true;
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
CompletionConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_items = cache.result; });
|
||||
callback(&Consumer);
|
||||
} else {
|
||||
clang_complete->completion_request_.PushBack(
|
||||
CCOpts.IncludeMacros = true;
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
CompletionConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_items = cache.result; });
|
||||
callback(&Consumer);
|
||||
} else {
|
||||
clang_complete->completion_request_.PushBack(
|
||||
std::make_unique<CompletionManager::CompletionRequest>(
|
||||
request->id, params.textDocument, begin_pos,
|
||||
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
|
||||
callback));
|
||||
}
|
||||
reply.id, param.textDocument, begin_pos,
|
||||
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
|
||||
callback));
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCompletion);
|
||||
|
||||
} // namespace
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,26 +1,15 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/definition";
|
||||
|
||||
struct In_TextDocumentDefinition : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDefinition, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentDefinition);
|
||||
|
||||
std::vector<Use> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Var: {
|
||||
@ -42,133 +31,173 @@ std::vector<Use> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) {
|
||||
return GetNonDefDeclarations(db, sym);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct Handler_TextDocumentDefinition
|
||||
: BaseMessageHandler<In_TextDocumentDefinition> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void Run(In_TextDocumentDefinition *request) override {
|
||||
auto ¶ms = request->params;
|
||||
int file_id;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file, &file_id))
|
||||
return;
|
||||
void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
int file_id;
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
std::vector<lsLocation> result;
|
||||
Maybe<Use> on_def;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsPosition &ls_pos = params.position;
|
||||
std::vector<lsLocation> result;
|
||||
Maybe<Use> on_def;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsPosition &ls_pos = param.position;
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
|
||||
// Special cases which are handled:
|
||||
// - symbol has declaration but no definition (ie, pure virtual)
|
||||
// - goto declaration while in definition of recursive type
|
||||
std::vector<Use> uses;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (def.spell) {
|
||||
Use spell = *def.spell;
|
||||
if (spell.file_id == file_id &&
|
||||
spell.range.Contains(ls_pos.line, ls_pos.character)) {
|
||||
on_def = spell;
|
||||
uses.clear();
|
||||
return false;
|
||||
}
|
||||
uses.push_back(spell);
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
|
||||
// Special cases which are handled:
|
||||
// - symbol has declaration but no definition (ie, pure virtual)
|
||||
// - goto declaration while in definition of recursive type
|
||||
std::vector<Use> uses;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (def.spell) {
|
||||
Use spell = *def.spell;
|
||||
if (spell.file_id == file_id &&
|
||||
spell.range.Contains(ls_pos.line, ls_pos.character)) {
|
||||
on_def = spell;
|
||||
uses.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// |uses| is empty if on a declaration/definition, otherwise it includes
|
||||
// all declarations/definitions.
|
||||
if (uses.empty()) {
|
||||
for (Use use : GetNonDefDeclarationTargets(db, sym))
|
||||
if (!(use.file_id == file_id &&
|
||||
use.range.Contains(ls_pos.line, ls_pos.character)))
|
||||
uses.push_back(use);
|
||||
// There is no declaration but the cursor is on a definition.
|
||||
if (uses.empty() && on_def)
|
||||
uses.push_back(*on_def);
|
||||
uses.push_back(spell);
|
||||
}
|
||||
auto locs = GetLsLocations(db, working_files, uses);
|
||||
result.insert(result.end(), locs.begin(), locs.end());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (result.size()) {
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
} else {
|
||||
Maybe<Range> range;
|
||||
// Check #include
|
||||
for (const IndexInclude &include : file->def->includes) {
|
||||
if (include.line == ls_pos.line) {
|
||||
result.push_back(
|
||||
lsLocation{lsDocumentUri::FromPath(include.resolved_path)});
|
||||
range = {{0, 0}, {0, 0}};
|
||||
// |uses| is empty if on a declaration/definition, otherwise it includes
|
||||
// all declarations/definitions.
|
||||
if (uses.empty()) {
|
||||
for (Use use : GetNonDefDeclarationTargets(db, sym))
|
||||
if (!(use.file_id == file_id &&
|
||||
use.range.Contains(ls_pos.line, ls_pos.character)))
|
||||
uses.push_back(use);
|
||||
// There is no declaration but the cursor is on a definition.
|
||||
if (uses.empty() && on_def)
|
||||
uses.push_back(*on_def);
|
||||
}
|
||||
auto locs = GetLsLocations(db, working_files, uses);
|
||||
result.insert(result.end(), locs.begin(), locs.end());
|
||||
}
|
||||
|
||||
if (result.size()) {
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
} else {
|
||||
Maybe<Range> range;
|
||||
// Check #include
|
||||
for (const IndexInclude &include : file->def->includes) {
|
||||
if (include.line == ls_pos.line) {
|
||||
result.push_back(
|
||||
lsLocation{lsDocumentUri::FromPath(include.resolved_path)});
|
||||
range = {{0, 0}, {0, 0}};
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Find the best match of the identifier at point.
|
||||
if (!range) {
|
||||
lsPosition position = param.position;
|
||||
const std::string &buffer = wfile->buffer_content;
|
||||
std::string_view query = LexIdentifierAroundPos(position, buffer);
|
||||
std::string_view short_query = query;
|
||||
{
|
||||
auto pos = query.rfind(':');
|
||||
if (pos != std::string::npos)
|
||||
short_query = query.substr(pos + 1);
|
||||
}
|
||||
|
||||
// For symbols whose short/detailed names contain |query| as a
|
||||
// substring, we use the tuple <length difference, negative position,
|
||||
// not in the same file, line distance> to find the best match.
|
||||
std::tuple<int, int, bool, int> best_score{INT_MAX, 0, true, 0};
|
||||
SymbolIdx best_sym;
|
||||
best_sym.kind = SymbolKind::Invalid;
|
||||
auto fn = [&](SymbolIdx sym) {
|
||||
std::string_view short_name = db->GetSymbolName(sym, false),
|
||||
name = short_query.size() < query.size()
|
||||
? db->GetSymbolName(sym, true)
|
||||
: short_name;
|
||||
if (short_name != short_query)
|
||||
return;
|
||||
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
|
||||
std::tuple<int, int, bool, int> score{
|
||||
int(name.size() - short_query.size()), 0, dr->file_id != file_id,
|
||||
std::abs(dr->range.start.line - position.line)};
|
||||
// Update the score with qualified name if the qualified name
|
||||
// occurs in |name|.
|
||||
auto pos = name.rfind(query);
|
||||
if (pos != std::string::npos) {
|
||||
std::get<0>(score) = int(name.size() - query.size());
|
||||
std::get<1>(score) = -int(pos);
|
||||
}
|
||||
if (score < best_score) {
|
||||
best_score = score;
|
||||
best_sym = sym;
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto &func : db->funcs)
|
||||
fn({func.usr, SymbolKind::Func});
|
||||
for (auto &type : db->types)
|
||||
fn({type.usr, SymbolKind::Type});
|
||||
for (auto &var : db->vars)
|
||||
if (var.def.size() && !var.def[0].is_local())
|
||||
fn({var.usr, SymbolKind::Var});
|
||||
|
||||
if (best_sym.kind != SymbolKind::Invalid) {
|
||||
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
|
||||
assert(dr);
|
||||
if (auto loc = GetLsLocation(db, working_files, *dr))
|
||||
result.push_back(*loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply(result);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_typeDefinition(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *working_file = working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
std::vector<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, param.position)) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Var: {
|
||||
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
|
||||
if (def && def->type)
|
||||
Add(db->Type(def->type));
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Type: {
|
||||
for (auto &def : db->GetType(sym).def)
|
||||
if (def.alias_of) {
|
||||
Add(db->Type(def.alias_of));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Find the best match of the identifier at point.
|
||||
if (!range) {
|
||||
lsPosition position = request->params.position;
|
||||
const std::string &buffer = wfile->buffer_content;
|
||||
std::string_view query = LexIdentifierAroundPos(position, buffer);
|
||||
std::string_view short_query = query;
|
||||
{
|
||||
auto pos = query.rfind(':');
|
||||
if (pos != std::string::npos)
|
||||
short_query = query.substr(pos + 1);
|
||||
}
|
||||
|
||||
// For symbols whose short/detailed names contain |query| as a
|
||||
// substring, we use the tuple <length difference, negative position,
|
||||
// not in the same file, line distance> to find the best match.
|
||||
std::tuple<int, int, bool, int> best_score{INT_MAX, 0, true, 0};
|
||||
SymbolIdx best_sym;
|
||||
best_sym.kind = SymbolKind::Invalid;
|
||||
auto fn = [&](SymbolIdx sym) {
|
||||
std::string_view short_name = db->GetSymbolName(sym, false),
|
||||
name = short_query.size() < query.size()
|
||||
? db->GetSymbolName(sym, true)
|
||||
: short_name;
|
||||
if (short_name != short_query)
|
||||
return;
|
||||
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym)) {
|
||||
std::tuple<int, int, bool, int> score{
|
||||
int(name.size() - short_query.size()), 0,
|
||||
dr->file_id != file_id,
|
||||
std::abs(dr->range.start.line - position.line)};
|
||||
// Update the score with qualified name if the qualified name
|
||||
// occurs in |name|.
|
||||
auto pos = name.rfind(query);
|
||||
if (pos != std::string::npos) {
|
||||
std::get<0>(score) = int(name.size() - query.size());
|
||||
std::get<1>(score) = -int(pos);
|
||||
}
|
||||
if (score < best_score) {
|
||||
best_score = score;
|
||||
best_sym = sym;
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto &func : db->funcs)
|
||||
fn({func.usr, SymbolKind::Func});
|
||||
for (auto &type : db->types)
|
||||
fn({type.usr, SymbolKind::Type});
|
||||
for (auto &var : db->vars)
|
||||
if (var.def.size() && !var.def[0].is_local())
|
||||
fn({var.usr, SymbolKind::Var});
|
||||
|
||||
if (best_sym.kind != SymbolKind::Invalid) {
|
||||
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
|
||||
assert(dr);
|
||||
if (auto loc = GetLsLocation(db, working_files, *dr))
|
||||
result.push_back(*loc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDefinition);
|
||||
} // namespace
|
||||
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -15,148 +15,56 @@ limitations under the License.
|
||||
|
||||
#include "clang_complete.hh"
|
||||
#include "include_complete.h"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "working_files.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace {
|
||||
MethodType didChange = "textDocument/didChange";
|
||||
MethodType didClose = "textDocument/didClose";
|
||||
MethodType didOpen = "textDocument/didOpen";
|
||||
MethodType didSave = "textDocument/didSave";
|
||||
namespace ccls {
|
||||
void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
working_files->OnChange(param);
|
||||
if (g_config->index.onChange)
|
||||
pipeline::Index(path, {}, IndexMode::OnChange);
|
||||
clang_complete->NotifyView(path);
|
||||
if (g_config->diagnostics.onChange >= 0)
|
||||
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
|
||||
}
|
||||
|
||||
struct In_TextDocumentDidChange : public NotificationMessage {
|
||||
MethodType GetMethodType() const override { return didChange; }
|
||||
lsTextDocumentDidChangeParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDidChange, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentDidChange);
|
||||
void MessageHandler::textDocument_didClose(TextDocumentParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
working_files->OnClose(param.textDocument);
|
||||
clang_complete->OnClose(path);
|
||||
}
|
||||
|
||||
struct Handler_TextDocumentDidChange
|
||||
: BaseMessageHandler<In_TextDocumentDidChange> {
|
||||
MethodType GetMethodType() const override { return didChange; }
|
||||
void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
WorkingFile *working_file = working_files->OnOpen(param.textDocument);
|
||||
if (std::optional<std::string> cached_file_contents =
|
||||
pipeline::LoadIndexedContent(path))
|
||||
working_file->SetIndexContent(*cached_file_contents);
|
||||
|
||||
void Run(In_TextDocumentDidChange *request) override {
|
||||
const auto ¶ms = request->params;
|
||||
std::string path = params.textDocument.uri.GetPath();
|
||||
working_files->OnChange(params);
|
||||
if (g_config->index.onChange)
|
||||
pipeline::Index(path, {}, IndexMode::OnChange);
|
||||
clang_complete->NotifyView(path);
|
||||
if (g_config->diagnostics.onChange >= 0)
|
||||
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
|
||||
ReplyOnce reply;
|
||||
QueryFile *file = FindFile(reply, path);
|
||||
if (file) {
|
||||
EmitSkippedRanges(working_file, *file);
|
||||
EmitSemanticHighlight(db, working_file, *file);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);
|
||||
include_complete->AddFile(working_file->filename);
|
||||
|
||||
struct In_TextDocumentDidClose : public NotificationMessage {
|
||||
MethodType GetMethodType() const override { return didClose; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDidClose::Params, textDocument);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDidClose, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentDidClose);
|
||||
|
||||
struct Handler_TextDocumentDidClose
|
||||
: BaseMessageHandler<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);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);
|
||||
|
||||
struct In_TextDocumentDidOpen : public NotificationMessage {
|
||||
MethodType GetMethodType() const override { return didOpen; }
|
||||
|
||||
struct Params {
|
||||
lsTextDocumentItem textDocument;
|
||||
|
||||
// ccls extension
|
||||
// If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project
|
||||
// entry (e.g. loaded from compile_commands.json or .ccls).
|
||||
std::vector<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 ¶ms = 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 =
|
||||
pipeline::LoadIndexedContent(path))
|
||||
working_file->SetIndexContent(*cached_file_contents);
|
||||
|
||||
QueryFile *file = nullptr;
|
||||
FindFileOrFail(db, project, std::nullopt, path, &file);
|
||||
if (file) {
|
||||
EmitSkippedRanges(working_file, *file);
|
||||
EmitSemanticHighlight(db, working_file, *file);
|
||||
}
|
||||
|
||||
include_complete->AddFile(working_file->filename);
|
||||
std::vector<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
|
||||
// pending index request.
|
||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||
if ((lang.first != LanguageId::Unknown && !lang.second) ||
|
||||
!pipeline::pending_index_requests)
|
||||
pipeline::Index(path, args, IndexMode::Normal);
|
||||
|
||||
clang_complete->NotifyView(path);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
|
||||
|
||||
struct In_TextDocumentDidSave : public NotificationMessage {
|
||||
MethodType GetMethodType() const override { return didSave; }
|
||||
|
||||
struct Params {
|
||||
// The document that was saved.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
|
||||
// Optional the content when saved. Depends on the includeText value
|
||||
// when the save notifcation was requested.
|
||||
// std::string text;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentDidSave);
|
||||
|
||||
struct Handler_TextDocumentDidSave
|
||||
: BaseMessageHandler<In_TextDocumentDidSave> {
|
||||
MethodType GetMethodType() const override { return didSave; }
|
||||
|
||||
void Run(In_TextDocumentDidSave *request) override {
|
||||
const auto ¶ms = request->params;
|
||||
const std::string &path = params.textDocument.uri.GetPath();
|
||||
// Submit new index request if it is not a header file or there is no
|
||||
// pending index request.
|
||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||
if ((lang.first != LanguageId::Unknown && !lang.second) ||
|
||||
!pipeline::pending_index_requests)
|
||||
pipeline::Index(path, {}, IndexMode::Normal);
|
||||
clang_complete->NotifySave(path);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidSave);
|
||||
} // namespace
|
||||
|
||||
clang_complete->NotifyView(path);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||
const std::string &path = param.textDocument.uri.GetPath();
|
||||
pipeline::Index(path, {}, IndexMode::Normal);
|
||||
clang_complete->NotifySave(path);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,21 +1,19 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);
|
||||
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType documentHighlight = "textDocument/documentHighlight",
|
||||
documentLink = "textDocument/documentLink",
|
||||
documentSymbol = "textDocument/documentSymbol";
|
||||
MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName);
|
||||
|
||||
struct lsDocumentHighlight {
|
||||
namespace {
|
||||
struct DocumentHighlight {
|
||||
enum Kind { Text = 1, Read = 2, Write = 3 };
|
||||
|
||||
lsRange range;
|
||||
@ -24,109 +22,81 @@ struct lsDocumentHighlight {
|
||||
// ccls extension
|
||||
Role role = Role::None;
|
||||
|
||||
bool operator<(const lsDocumentHighlight &o) const {
|
||||
bool operator<(const DocumentHighlight &o) const {
|
||||
return !(range == o.range) ? range < o.range : kind < o.kind;
|
||||
}
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
|
||||
MAKE_REFLECT_STRUCT(DocumentHighlight, range, kind, role);
|
||||
} // namespace
|
||||
|
||||
struct In_TextDocumentDocumentHighlight : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return documentHighlight; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentDocumentHighlight, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentDocumentHighlight);
|
||||
void MessageHandler::textDocument_documentHighlight(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
int file_id;
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
struct Handler_TextDocumentDocumentHighlight
|
||||
: BaseMessageHandler<In_TextDocumentDocumentHighlight> {
|
||||
MethodType GetMethodType() const override { return documentHighlight; }
|
||||
void Run(In_TextDocumentDocumentHighlight *request) override {
|
||||
int file_id;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file,
|
||||
&file_id))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
std::vector<DocumentHighlight> result;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
std::vector<lsDocumentHighlight> result;
|
||||
|
||||
std::vector<SymbolRef> syms =
|
||||
FindSymbolsAtLocation(wfile, file, request->params.position, true);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0)
|
||||
continue;
|
||||
Usr usr = sym.usr;
|
||||
SymbolKind kind = sym.kind;
|
||||
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
|
||||
return usr == sym1.usr && kind == sym1.kind;
|
||||
}))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
|
||||
lsDocumentHighlight highlight;
|
||||
highlight.range = loc->range;
|
||||
if (sym.role & Role::Write)
|
||||
highlight.kind = lsDocumentHighlight::Write;
|
||||
else if (sym.role & Role::Read)
|
||||
highlight.kind = lsDocumentHighlight::Read;
|
||||
else
|
||||
highlight.kind = lsDocumentHighlight::Text;
|
||||
highlight.role = sym.role;
|
||||
result.push_back(highlight);
|
||||
}
|
||||
std::vector<SymbolRef> syms =
|
||||
FindSymbolsAtLocation(wfile, file, param.position, true);
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0)
|
||||
continue;
|
||||
Usr usr = sym.usr;
|
||||
SymbolKind kind = sym.kind;
|
||||
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
|
||||
return usr == sym1.usr && kind == sym1.kind;
|
||||
}))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
|
||||
DocumentHighlight highlight;
|
||||
highlight.range = loc->range;
|
||||
if (sym.role & Role::Write)
|
||||
highlight.kind = DocumentHighlight::Write;
|
||||
else if (sym.role & Role::Read)
|
||||
highlight.kind = DocumentHighlight::Read;
|
||||
else
|
||||
highlight.kind = DocumentHighlight::Text;
|
||||
highlight.role = sym.role;
|
||||
result.push_back(highlight);
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDocumentHighlight);
|
||||
|
||||
struct In_textDocumentDocumentLink : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return documentLink; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentDocumentLink, id, params);
|
||||
REGISTER_IN_MESSAGE(In_textDocumentDocumentLink);
|
||||
std::sort(result.begin(), result.end());
|
||||
reply(result);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct lsDocumentLink {
|
||||
lsRange range;
|
||||
lsDocumentUri target;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsDocumentLink, range, target);
|
||||
} // namespace
|
||||
|
||||
struct Handler_textDocumentDocumentLink
|
||||
: BaseMessageHandler<In_textDocumentDocumentLink> {
|
||||
MethodType GetMethodType() const override { return documentLink; }
|
||||
void Run(In_textDocumentDocumentLink *request) override {
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
std::vector<lsDocumentLink> result;
|
||||
for (const IndexInclude &include : file->def->includes)
|
||||
result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}},
|
||||
lsDocumentUri::FromPath(include.resolved_path)});
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
std::vector<lsDocumentLink> result;
|
||||
for (const IndexInclude &include : file->def->includes)
|
||||
result.push_back({lsRange{{include.line, 0}, {include.line + 1, 0}},
|
||||
lsDocumentUri::FromPath(include.resolved_path)});
|
||||
reply(result);
|
||||
} // namespace ccls
|
||||
|
||||
namespace {
|
||||
struct DocumentSymbolParam : TextDocumentParam {
|
||||
// false: outline; true: all symbols
|
||||
bool all = false;
|
||||
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
|
||||
int startLine = -1;
|
||||
int endLine = -1;
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentLink);
|
||||
|
||||
struct In_textDocumentDocumentSymbol : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return documentSymbol; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
// false: outline; true: all symbols
|
||||
bool all = false;
|
||||
// If >= 0, return Range[] instead of SymbolInformation[] to reduce output.
|
||||
int startLine = -1;
|
||||
int endLine = -1;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol::Params, textDocument, all,
|
||||
startLine, endLine);
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentDocumentSymbol, id, params);
|
||||
REGISTER_IN_MESSAGE(In_textDocumentDocumentSymbol);
|
||||
MAKE_REFLECT_STRUCT(DocumentSymbolParam, textDocument, all, startLine, endLine);
|
||||
|
||||
struct lsDocumentSymbol {
|
||||
std::string name;
|
||||
@ -155,131 +125,126 @@ template<>
|
||||
bool Ignore(const QueryVar::Def *def) {
|
||||
return !def || def->is_local();
|
||||
}
|
||||
|
||||
struct Handler_textDocumentDocumentSymbol
|
||||
: BaseMessageHandler<In_textDocumentDocumentSymbol> {
|
||||
MethodType GetMethodType() const override { return documentSymbol; }
|
||||
void Run(In_textDocumentDocumentSymbol *request) override {
|
||||
auto ¶ms = request->params;
|
||||
|
||||
QueryFile *file;
|
||||
int file_id;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file, &file_id))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
|
||||
if (params.startLine >= 0) {
|
||||
std::vector<lsRange> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && (params.all || sym.extent.Valid()) &&
|
||||
params.startLine <= sym.range.start.line &&
|
||||
sym.range.start.line <= params.endLine)
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id))
|
||||
result.push_back(loc->range);
|
||||
std::sort(result.begin(), result.end());
|
||||
pipeline::Reply(request->id, result);
|
||||
} else if (g_config->client.hierarchicalDocumentSymbolSupport) {
|
||||
std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds;
|
||||
std::vector<std::pair<std::vector<const void *>, lsDocumentSymbol *>>
|
||||
funcs, types;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !sym.extent.Valid())
|
||||
continue;
|
||||
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||
if (!r.second)
|
||||
continue;
|
||||
auto &ds = r.first->second;
|
||||
ds = std::make_unique<lsDocumentSymbol>();
|
||||
std::vector<const void *> def_ptrs;
|
||||
WithEntity(db, sym, [&, sym = sym](const auto &entity) {
|
||||
auto *def = entity.AnyDef();
|
||||
if (!def)
|
||||
return;
|
||||
ds->name = def->Name(false);
|
||||
ds->detail = def->Name(true);
|
||||
if (auto ls_range = GetLsRange(wfile, sym.range)) {
|
||||
ds->selectionRange = *ls_range;
|
||||
ds->range = ds->selectionRange;
|
||||
if (sym.extent.Valid())
|
||||
if (auto ls_range1 = GetLsRange(wfile, sym.extent))
|
||||
ds->range = *ls_range1;
|
||||
}
|
||||
|
||||
for (auto &def : entity.def)
|
||||
if (def.file_id == file_id && !Ignore(&def)) {
|
||||
ds->kind = def.kind;
|
||||
if (def.spell || def.kind == lsSymbolKind::Namespace)
|
||||
def_ptrs.push_back(&def);
|
||||
}
|
||||
});
|
||||
if (def_ptrs.empty() || !(params.all || sym.role & Role::Definition ||
|
||||
ds->kind == lsSymbolKind::Namespace)) {
|
||||
ds.reset();
|
||||
continue;
|
||||
}
|
||||
if (sym.kind == SymbolKind::Func)
|
||||
funcs.emplace_back(std::move(def_ptrs), ds.get());
|
||||
else if (sym.kind == SymbolKind::Type)
|
||||
types.emplace_back(std::move(def_ptrs), ds.get());
|
||||
}
|
||||
|
||||
for (auto &[def_ptrs, ds] : funcs)
|
||||
for (const void *def_ptr : def_ptrs)
|
||||
for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (auto &[def_ptrs, ds] : types)
|
||||
for (const void *def_ptr : def_ptrs) {
|
||||
auto *def = (const QueryType::Def *)def_ptr;
|
||||
for (Usr usr1 : def->funcs) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Func});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (Usr usr1 : def->types) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Type});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (auto [usr1, _] : def->vars) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
}
|
||||
std::vector<std::unique_ptr<lsDocumentSymbol>> result;
|
||||
for (auto &[_, ds] : sym2ds)
|
||||
if (ds)
|
||||
result.push_back(std::move(ds));
|
||||
pipeline::Reply(request->id, result);
|
||||
} else {
|
||||
std::vector<lsSymbolInformation> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !sym.extent.Valid() ||
|
||||
!(params.all || sym.role & Role::Definition))
|
||||
continue;
|
||||
if (std::optional<lsSymbolInformation> info =
|
||||
GetSymbolInfo(db, sym, false)) {
|
||||
if ((sym.kind == SymbolKind::Type &&
|
||||
Ignore(db->GetType(sym).AnyDef())) ||
|
||||
(sym.kind == SymbolKind::Var &&
|
||||
Ignore(db->GetVar(sym).AnyDef())))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
|
||||
info->location = *loc;
|
||||
result.push_back(*info);
|
||||
}
|
||||
}
|
||||
}
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_textDocumentDocumentSymbol);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_documentSymbol(Reader &reader,
|
||||
ReplyOnce &reply) {
|
||||
DocumentSymbolParam param;
|
||||
Reflect(reader, param);
|
||||
|
||||
int file_id;
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
|
||||
if (param.startLine >= 0) {
|
||||
std::vector<lsRange> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && (param.all || sym.extent.Valid()) &&
|
||||
param.startLine <= sym.range.start.line &&
|
||||
sym.range.start.line <= param.endLine)
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id))
|
||||
result.push_back(loc->range);
|
||||
std::sort(result.begin(), result.end());
|
||||
reply(result);
|
||||
} else if (g_config->client.hierarchicalDocumentSymbolSupport) {
|
||||
std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds;
|
||||
std::vector<std::pair<std::vector<const void *>, lsDocumentSymbol *>> funcs,
|
||||
types;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !sym.extent.Valid())
|
||||
continue;
|
||||
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||
if (!r.second)
|
||||
continue;
|
||||
auto &ds = r.first->second;
|
||||
ds = std::make_unique<lsDocumentSymbol>();
|
||||
std::vector<const void *> def_ptrs;
|
||||
WithEntity(db, sym, [&, sym = sym](const auto &entity) {
|
||||
auto *def = entity.AnyDef();
|
||||
if (!def)
|
||||
return;
|
||||
ds->name = def->Name(false);
|
||||
ds->detail = def->Name(true);
|
||||
if (auto ls_range = GetLsRange(wfile, sym.range)) {
|
||||
ds->selectionRange = *ls_range;
|
||||
ds->range = ds->selectionRange;
|
||||
if (sym.extent.Valid())
|
||||
if (auto ls_range1 = GetLsRange(wfile, sym.extent))
|
||||
ds->range = *ls_range1;
|
||||
}
|
||||
|
||||
for (auto &def : entity.def)
|
||||
if (def.file_id == file_id && !Ignore(&def)) {
|
||||
ds->kind = def.kind;
|
||||
if (def.spell || def.kind == lsSymbolKind::Namespace)
|
||||
def_ptrs.push_back(&def);
|
||||
}
|
||||
});
|
||||
if (def_ptrs.empty() || !(param.all || sym.role & Role::Definition ||
|
||||
ds->kind == lsSymbolKind::Namespace)) {
|
||||
ds.reset();
|
||||
continue;
|
||||
}
|
||||
if (sym.kind == SymbolKind::Func)
|
||||
funcs.emplace_back(std::move(def_ptrs), ds.get());
|
||||
else if (sym.kind == SymbolKind::Type)
|
||||
types.emplace_back(std::move(def_ptrs), ds.get());
|
||||
}
|
||||
|
||||
for (auto &[def_ptrs, ds] : funcs)
|
||||
for (const void *def_ptr : def_ptrs)
|
||||
for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (auto &[def_ptrs, ds] : types)
|
||||
for (const void *def_ptr : def_ptrs) {
|
||||
auto *def = (const QueryType::Def *)def_ptr;
|
||||
for (Usr usr1 : def->funcs) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Func});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (Usr usr1 : def->types) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Type});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
for (auto [usr1, _] : def->vars) {
|
||||
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
|
||||
if (it != sym2ds.end() && it->second)
|
||||
ds->children.push_back(std::move(it->second));
|
||||
}
|
||||
}
|
||||
std::vector<std::unique_ptr<lsDocumentSymbol>> result;
|
||||
for (auto &[_, ds] : sym2ds)
|
||||
if (ds)
|
||||
result.push_back(std::move(ds));
|
||||
reply(result);
|
||||
} else {
|
||||
std::vector<lsSymbolInformation> result;
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||
if (refcnt <= 0 || !sym.extent.Valid() ||
|
||||
!(param.all || sym.role & Role::Definition))
|
||||
continue;
|
||||
if (std::optional<lsSymbolInformation> info =
|
||||
GetSymbolInfo(db, sym, false)) {
|
||||
if ((sym.kind == SymbolKind::Type &&
|
||||
Ignore(db->GetType(sym).AnyDef())) ||
|
||||
(sym.kind == SymbolKind::Var && Ignore(db->GetVar(sym).AnyDef())))
|
||||
continue;
|
||||
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
|
||||
info->location = *loc;
|
||||
result.push_back(*info);
|
||||
}
|
||||
}
|
||||
}
|
||||
reply(result);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,61 +13,43 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "query_utils.h"
|
||||
#include "working_files.h"
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType foldingRange = "textDocument/foldingRange";
|
||||
|
||||
struct In_textDocumentFoldingRange : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return foldingRange; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentFoldingRange::Params, textDocument);
|
||||
MAKE_REFLECT_STRUCT(In_textDocumentFoldingRange, id, params);
|
||||
REGISTER_IN_MESSAGE(In_textDocumentFoldingRange);
|
||||
|
||||
struct FoldingRange {
|
||||
int startLine, startCharacter, endLine, endCharacter;
|
||||
std::string kind = "region";
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, kind);
|
||||
|
||||
struct Handler_textDocumentFoldingRange
|
||||
: BaseMessageHandler<In_textDocumentFoldingRange> {
|
||||
MethodType GetMethodType() const override { return foldingRange; }
|
||||
void Run(In_textDocumentFoldingRange *request) override {
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file,
|
||||
nullptr))
|
||||
return;
|
||||
WorkingFile *wfile =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::vector<FoldingRange> result;
|
||||
std::optional<lsRange> ls_range;
|
||||
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() &&
|
||||
(sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) &&
|
||||
(ls_range = GetLsRange(wfile, sym.extent))) {
|
||||
FoldingRange &fold = result.emplace_back();
|
||||
fold.startLine = ls_range->start.line;
|
||||
fold.startCharacter = ls_range->start.character;
|
||||
fold.endLine = ls_range->end.line;
|
||||
fold.endCharacter = ls_range->end.character;
|
||||
}
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_textDocumentFoldingRange);
|
||||
MAKE_REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine,
|
||||
endCharacter, kind);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::vector<FoldingRange> result;
|
||||
std::optional<lsRange> ls_range;
|
||||
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.Valid() &&
|
||||
(sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) &&
|
||||
(ls_range = GetLsRange(wfile, sym.extent))) {
|
||||
FoldingRange &fold = result.emplace_back();
|
||||
fold.startLine = ls_range->start.line;
|
||||
fold.startCharacter = ls_range->start.character;
|
||||
fold.endLine = ls_range->end.line;
|
||||
fold.endCharacter = ls_range->end.character;
|
||||
}
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -13,29 +13,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
#include <clang/Tooling/Core/Replacement.h>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
const MethodType formatting = "textDocument/formatting",
|
||||
onTypeFormatting = "textDocument/onTypeFormatting",
|
||||
rangeFormatting = "textDocument/rangeFormatting";
|
||||
|
||||
struct lsFormattingOptions {
|
||||
// Size of a tab in spaces.
|
||||
int tabSize;
|
||||
// Prefer spaces over tabs.
|
||||
bool insertSpaces;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces);
|
||||
|
||||
llvm::Expected<tooling::Replacements>
|
||||
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
|
||||
StringRef Code(code.data(), code.size()), File(file.data(), file.size());
|
||||
@ -79,116 +67,59 @@ ReplacementsToEdits(std::string_view code, const tooling::Replacements &Repls) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Format(WorkingFile *wfile, tooling::Range range, lsRequestId id) {
|
||||
void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) {
|
||||
std::string_view code = wfile->buffer_content;
|
||||
auto ReplsOrErr =
|
||||
FormatCode(code, wfile->filename, range);
|
||||
auto ReplsOrErr = FormatCode(code, wfile->filename, range);
|
||||
if (ReplsOrErr) {
|
||||
auto result = ReplacementsToEdits(code, *ReplsOrErr);
|
||||
pipeline::Reply(id, result);
|
||||
reply(result);
|
||||
} else {
|
||||
lsResponseError err;
|
||||
err.code = lsErrorCodes::UnknownErrorCode;
|
||||
err.message = llvm::toString(ReplsOrErr.takeError());
|
||||
pipeline::ReplyError(id, err);
|
||||
reply.Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
struct In_TextDocumentFormatting : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return formatting; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsFormattingOptions options;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentFormatting::Params, textDocument, options);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentFormatting, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentFormatting);
|
||||
|
||||
struct Handler_TextDocumentFormatting
|
||||
: BaseMessageHandler<In_TextDocumentFormatting> {
|
||||
MethodType GetMethodType() const override { return formatting; }
|
||||
void Run(In_TextDocumentFormatting *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
Format(wfile, {0, (unsigned)wfile->buffer_content.size()}, request->id);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting);
|
||||
|
||||
struct In_TextDocumentOnTypeFormatting : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return onTypeFormatting; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
std::string ch;
|
||||
lsFormattingOptions options;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentOnTypeFormatting::Params, textDocument,
|
||||
position, ch, options);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentOnTypeFormatting, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentOnTypeFormatting);
|
||||
|
||||
struct Handler_TextDocumentOnTypeFormatting
|
||||
: BaseMessageHandler<In_TextDocumentOnTypeFormatting> {
|
||||
MethodType GetMethodType() const override { return onTypeFormatting; }
|
||||
void Run(In_TextDocumentOnTypeFormatting *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::string_view code = wfile->buffer_content;
|
||||
int pos = GetOffsetForPosition(params.position, code);
|
||||
auto lbrace = code.find_last_of('{', pos);
|
||||
if (lbrace == std::string::npos)
|
||||
lbrace = pos;
|
||||
Format(wfile, {(unsigned)lbrace, unsigned(pos - lbrace)}, request->id);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting);
|
||||
|
||||
struct In_TextDocumentRangeFormatting : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return rangeFormatting; }
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsRange range;
|
||||
lsFormattingOptions options;
|
||||
} params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting::Params, textDocument, range,
|
||||
options);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentRangeFormatting);
|
||||
|
||||
struct Handler_TextDocumentRangeFormatting
|
||||
: BaseMessageHandler<In_TextDocumentRangeFormatting> {
|
||||
MethodType GetMethodType() const override { return rangeFormatting; }
|
||||
|
||||
void Run(In_TextDocumentRangeFormatting *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::string_view code = wfile->buffer_content;
|
||||
int begin = GetOffsetForPosition(params.range.start, code),
|
||||
end = GetOffsetForPosition(params.range.end, code);
|
||||
Format(wfile, {(unsigned)begin, unsigned(end - begin)}, request->id);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRangeFormatting);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
Format(reply, wfile, {0, (unsigned)wfile->buffer_content.size()});
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_onTypeFormatting(
|
||||
DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::string_view code = wfile->buffer_content;
|
||||
int pos = GetOffsetForPosition(param.position, code);
|
||||
auto lbrace = code.find_last_of('{', pos);
|
||||
if (lbrace == std::string::npos)
|
||||
lbrace = pos;
|
||||
Format(reply, wfile, {(unsigned)lbrace, unsigned(pos - lbrace)});
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_rangeFormatting(
|
||||
DocumentRangeFormattingParam ¶m, ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!wfile)
|
||||
return;
|
||||
std::string_view code = wfile->buffer_content;
|
||||
int begin = GetOffsetForPosition(param.range.start, code),
|
||||
end = GetOffsetForPosition(param.range.end, code);
|
||||
Format(reply, wfile, {(unsigned)begin, unsigned(end - begin)});
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,13 +1,33 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/hover";
|
||||
struct lsMarkedString {
|
||||
std::optional<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) {
|
||||
switch (lang) {
|
||||
@ -57,51 +77,34 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
|
||||
});
|
||||
return {hover, ls_comments};
|
||||
}
|
||||
|
||||
struct In_TextDocumentHover : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentHover, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentHover);
|
||||
|
||||
struct lsHover {
|
||||
std::vector<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 ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsHover result;
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) {
|
||||
std::optional<lsRange> ls_range = GetLsRange(
|
||||
working_files->GetFileByFilename(file->def->path), sym.range);
|
||||
if (!ls_range)
|
||||
continue;
|
||||
|
||||
auto [hover, comments] = GetHover(db, file->def->language, sym, file->id);
|
||||
if (comments || hover) {
|
||||
result.range = *ls_range;
|
||||
if (comments)
|
||||
result.contents.push_back(*comments);
|
||||
if (hover)
|
||||
result.contents.push_back(*hover);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentHover);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
Hover result;
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
|
||||
std::optional<lsRange> ls_range = GetLsRange(
|
||||
working_files->GetFileByFilename(file->def->path), sym.range);
|
||||
if (!ls_range)
|
||||
continue;
|
||||
|
||||
auto [hover, comments] = GetHover(db, file->def->language, sym, file->id);
|
||||
if (comments || hover) {
|
||||
result.range = *ls_range;
|
||||
if (comments)
|
||||
result.contents.push_back(*comments);
|
||||
if (hover)
|
||||
result.contents.push_back(*hover);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,20 +1,15 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/references";
|
||||
|
||||
struct In_TextDocumentReferences : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct lsReferenceContext {
|
||||
struct ReferenceParam : public TextDocumentPositionParam {
|
||||
struct Context {
|
||||
bool base = true;
|
||||
// Exclude references with any |Role| bits set.
|
||||
Role excludeRole = Role::None;
|
||||
@ -22,116 +17,99 @@ struct In_TextDocumentReferences : public RequestMessage {
|
||||
bool includeDeclaration = false;
|
||||
// Include references with all |Role| bits set.
|
||||
Role role = Role::None;
|
||||
};
|
||||
struct Params {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsPosition position;
|
||||
lsReferenceContext context;
|
||||
};
|
||||
|
||||
Params params;
|
||||
} context;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext, base,
|
||||
excludeRole, includeDeclaration, role);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params, textDocument, position,
|
||||
context);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentReferences, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentReferences);
|
||||
MAKE_REFLECT_STRUCT(ReferenceParam::Context, base, excludeRole,
|
||||
includeDeclaration, role);
|
||||
MAKE_REFLECT_STRUCT(ReferenceParam, textDocument, position, context);
|
||||
} // namespace
|
||||
|
||||
struct Handler_TextDocumentReferences
|
||||
: BaseMessageHandler<In_TextDocumentReferences> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) {
|
||||
ReferenceParam param;
|
||||
Reflect(reader, param);
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
|
||||
if (!file)
|
||||
return;
|
||||
std::vector<lsLocation> result;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
void Run(In_TextDocumentReferences *request) override {
|
||||
auto ¶ms = request->params;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
std::vector<lsLocation> result;
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
if (!file) {
|
||||
pipeline::Reply(request->id, result);
|
||||
return;
|
||||
}
|
||||
std::unordered_set<Use> seen_uses;
|
||||
int line = param.position.line;
|
||||
|
||||
std::unordered_set<Use> seen_uses;
|
||||
int line = params.position.line;
|
||||
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) {
|
||||
// Found symbol. Return references.
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(sym.usr);
|
||||
std::vector<Usr> stack{sym.usr};
|
||||
if (sym.kind != SymbolKind::Func)
|
||||
params.context.base = false;
|
||||
while (stack.size()) {
|
||||
sym.usr = stack.back();
|
||||
stack.pop_back();
|
||||
auto fn = [&](Use use, lsSymbolKind parent_kind) {
|
||||
if (Role(use.role & params.context.role) == params.context.role &&
|
||||
!(use.role & params.context.excludeRole) &&
|
||||
seen_uses.insert(use).second)
|
||||
if (auto loc = GetLsLocation(db, working_files, use)) {
|
||||
result.push_back(*loc);
|
||||
}
|
||||
};
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
lsSymbolKind parent_kind = lsSymbolKind::Unknown;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
|
||||
// Found symbol. Return references.
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(sym.usr);
|
||||
std::vector<Usr> stack{sym.usr};
|
||||
if (sym.kind != SymbolKind::Func)
|
||||
param.context.base = false;
|
||||
while (stack.size()) {
|
||||
sym.usr = stack.back();
|
||||
stack.pop_back();
|
||||
auto fn = [&](Use use, lsSymbolKind parent_kind) {
|
||||
if (Role(use.role & param.context.role) == param.context.role &&
|
||||
!(use.role & param.context.excludeRole) &&
|
||||
seen_uses.insert(use).second)
|
||||
if (auto loc = GetLsLocation(db, working_files, use)) {
|
||||
result.push_back(*loc);
|
||||
}
|
||||
};
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
lsSymbolKind parent_kind = lsSymbolKind::Unknown;
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell) {
|
||||
parent_kind = GetSymbolKind(db, sym);
|
||||
if (param.context.base)
|
||||
for (Usr usr : def.GetBases())
|
||||
if (!seen.count(usr)) {
|
||||
seen.insert(usr);
|
||||
stack.push_back(usr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (Use use : entity.uses)
|
||||
fn(use, parent_kind);
|
||||
if (param.context.includeDeclaration) {
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell) {
|
||||
parent_kind = GetSymbolKind(db, sym);
|
||||
if (params.context.base)
|
||||
for (Usr usr : def.GetBases())
|
||||
if (!seen.count(usr)) {
|
||||
seen.insert(usr);
|
||||
stack.push_back(usr);
|
||||
}
|
||||
if (def.spell)
|
||||
fn(*def.spell, parent_kind);
|
||||
for (Use use : entity.declarations)
|
||||
fn(use, parent_kind);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.empty()) {
|
||||
// |path| is the #include line. If the cursor is not on such line but line
|
||||
// = 0,
|
||||
// use the current filename.
|
||||
std::string path;
|
||||
if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1)
|
||||
path = file->def->path;
|
||||
for (const IndexInclude &include : file->def->includes)
|
||||
if (include.line == param.position.line) {
|
||||
path = include.resolved_path;
|
||||
break;
|
||||
}
|
||||
if (path.size())
|
||||
for (QueryFile &file1 : db->files)
|
||||
if (file1.def)
|
||||
for (const IndexInclude &include : file1.def->includes)
|
||||
if (include.resolved_path == path) {
|
||||
// Another file |file1| has the same include line.
|
||||
lsLocation &loc = result.emplace_back();
|
||||
loc.uri = lsDocumentUri::FromPath(file1.def->path);
|
||||
loc.range.start.line = loc.range.end.line = include.line;
|
||||
break;
|
||||
}
|
||||
for (Use use : entity.uses)
|
||||
fn(use, parent_kind);
|
||||
if (params.context.includeDeclaration) {
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell)
|
||||
fn(*def.spell, parent_kind);
|
||||
for (Use use : entity.declarations)
|
||||
fn(use, parent_kind);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.empty()) {
|
||||
// |path| is the #include line. If the cursor is not on such line but line
|
||||
// = 0,
|
||||
// use the current filename.
|
||||
std::string path;
|
||||
if (line == 0 || line >= (int)wfile->buffer_lines.size() - 1)
|
||||
path = file->def->path;
|
||||
for (const IndexInclude &include : file->def->includes)
|
||||
if (include.line == params.position.line) {
|
||||
path = include.resolved_path;
|
||||
break;
|
||||
}
|
||||
if (path.size())
|
||||
for (QueryFile &file1 : db->files)
|
||||
if (file1.def)
|
||||
for (const IndexInclude &include : file1.def->includes)
|
||||
if (include.resolved_path == path) {
|
||||
// Another file |file1| has the same include line.
|
||||
lsLocation &loc = result.emplace_back();
|
||||
loc.uri = lsDocumentUri::FromPath(file1.def->path);
|
||||
loc.range.start.line = loc.range.end.line = include.line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)result.size() >= g_config->xref.maxNum)
|
||||
result.resize(g_config->xref.maxNum);
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentReferences);
|
||||
} // namespace
|
||||
|
||||
if ((int)result.size() >= g_config->xref.maxNum)
|
||||
result.resize(g_config->xref.maxNum);
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,14 +1,11 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.h"
|
||||
using namespace ccls;
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/rename";
|
||||
|
||||
lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files,
|
||||
SymbolRef sym, const std::string &new_text) {
|
||||
std::unordered_map<int, lsTextDocumentEdit> path_to_edit;
|
||||
@ -50,49 +47,21 @@ lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files,
|
||||
edit.documentChanges.push_back(changes.second);
|
||||
return edit;
|
||||
}
|
||||
|
||||
struct In_TextDocumentRename : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
struct Params {
|
||||
// The document to format.
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
|
||||
// The position at which this request was sent.
|
||||
lsPosition position;
|
||||
|
||||
// The new name of the symbol. If the given name is not valid the
|
||||
// request must return a [ResponseError](#ResponseError) with an
|
||||
// appropriate message set.
|
||||
std::string newName;
|
||||
};
|
||||
Params params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentRename::Params, textDocument, position,
|
||||
newName);
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentRename, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentRename);
|
||||
|
||||
struct Handler_TextDocumentRename : BaseMessageHandler<In_TextDocumentRename> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void Run(In_TextDocumentRename *request) override {
|
||||
int file_id;
|
||||
QueryFile *file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file,
|
||||
&file_id))
|
||||
return;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsWorkspaceEdit result;
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(wfile, file, request->params.position)) {
|
||||
result =
|
||||
BuildWorkspaceEdit(db, working_files, sym, request->params.newName);
|
||||
break;
|
||||
}
|
||||
|
||||
pipeline::Reply(request->id, result);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRename);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) {
|
||||
int file_id;
|
||||
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath(), &file_id);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||
lsWorkspaceEdit result;
|
||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, param.position)) {
|
||||
result = BuildWorkspaceEdit(db, working_files, sym, param.newName);
|
||||
break;
|
||||
}
|
||||
|
||||
reply(result);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -2,56 +2,35 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "clang_complete.hh"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
|
||||
#include <clang/Sema/Sema.h>
|
||||
|
||||
using namespace ccls;
|
||||
namespace ccls {
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/signatureHelp";
|
||||
|
||||
// Represents a parameter of a callable-signature. A parameter can
|
||||
// have a label and a doc-comment.
|
||||
struct lsParameterInformation {
|
||||
struct ParameterInformation {
|
||||
std::string label;
|
||||
// Not available in clang
|
||||
// std::optional<std::string> documentation;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsParameterInformation, label);
|
||||
|
||||
// Represents the signature of something callable. A signature
|
||||
// can have a label, like a function-name, a doc-comment, and
|
||||
// a set of parameters.
|
||||
struct lsSignatureInformation {
|
||||
struct SignatureInformation {
|
||||
std::string label;
|
||||
std::optional<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 {
|
||||
std::vector<lsSignatureInformation> signatures;
|
||||
std::vector<SignatureInformation> signatures;
|
||||
int activeSignature = 0;
|
||||
int activeParameter = 0;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(ParameterInformation, label);
|
||||
MAKE_REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
|
||||
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature,
|
||||
activeParameter);
|
||||
|
||||
struct In_TextDocumentSignatureHelp : public RequestMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
lsTextDocumentPositionParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentSignatureHelp, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentSignatureHelp);
|
||||
|
||||
std::string BuildOptional(const CodeCompletionString &CCS,
|
||||
std::vector<lsParameterInformation> &ls_params) {
|
||||
std::vector<ParameterInformation> &ls_params) {
|
||||
std::string ret;
|
||||
for (const auto &Chunk : CCS) {
|
||||
switch (Chunk.Kind) {
|
||||
@ -67,7 +46,7 @@ std::string BuildOptional(const CodeCompletionString &CCS,
|
||||
// the code-completion location within a function call, message send,
|
||||
// macro invocation, etc.
|
||||
ret += Chunk.Text;
|
||||
ls_params.push_back(lsParameterInformation{Chunk.Text});
|
||||
ls_params.push_back({Chunk.Text});
|
||||
break;
|
||||
}
|
||||
case CodeCompletionString::CK_VerticalSpace:
|
||||
@ -113,7 +92,7 @@ public:
|
||||
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
|
||||
|
||||
const char *ret_type = nullptr;
|
||||
lsSignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
||||
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
||||
#if LLVM_VERSION_MAJOR >= 8
|
||||
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
|
||||
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
|
||||
@ -126,7 +105,7 @@ public:
|
||||
case CodeCompletionString::CK_Placeholder:
|
||||
case CodeCompletionString::CK_CurrentParameter: {
|
||||
ls_sig.label += Chunk.Text;
|
||||
ls_sig.parameters.push_back(lsParameterInformation{Chunk.Text});
|
||||
ls_sig.parameters.push_back({Chunk.Text});
|
||||
break;
|
||||
}
|
||||
case CodeCompletionString::CK_Optional:
|
||||
@ -145,7 +124,7 @@ public:
|
||||
}
|
||||
std::sort(
|
||||
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
||||
[](const lsSignatureInformation &l, const lsSignatureInformation &r) {
|
||||
[](const SignatureInformation &l, const SignatureInformation &r) {
|
||||
if (l.parameters.size() != r.parameters.size())
|
||||
return l.parameters.size() < r.parameters.size();
|
||||
if (l.label.size() != r.label.size())
|
||||
@ -157,55 +136,50 @@ public:
|
||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||
};
|
||||
|
||||
struct Handler_TextDocumentSignatureHelp
|
||||
: BaseMessageHandler<In_TextDocumentSignatureHelp> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
void Run(In_TextDocumentSignatureHelp *request) override {
|
||||
static CompleteConsumerCache<lsSignatureHelp> cache;
|
||||
|
||||
const auto ¶ms = request->params;
|
||||
std::string path = params.textDocument.uri.GetPath();
|
||||
lsPosition begin_pos = params.position;
|
||||
if (WorkingFile *file = working_files->GetFileByFilename(path)) {
|
||||
std::string completion_text;
|
||||
lsPosition end_pos = params.position;
|
||||
begin_pos = file->FindStableCompletionSource(
|
||||
request->params.position, &completion_text, &end_pos);
|
||||
}
|
||||
|
||||
CompletionManager::OnComplete callback =
|
||||
[id = request->id, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
|
||||
pipeline::Reply(id, Consumer->ls_sighelp);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_sighelp;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeGlobals = false;
|
||||
CCOpts.IncludeMacros = false;
|
||||
CCOpts.IncludeBriefComments = false;
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
SignatureHelpConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
|
||||
callback(&Consumer);
|
||||
} else {
|
||||
clang_complete->completion_request_.PushBack(
|
||||
std::make_unique<CompletionManager::CompletionRequest>(
|
||||
request->id, params.textDocument, params.position,
|
||||
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
|
||||
callback));
|
||||
}
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentSignatureHelp);
|
||||
} // namespace
|
||||
|
||||
void MessageHandler::textDocument_signatureHelp(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
static CompleteConsumerCache<lsSignatureHelp> cache;
|
||||
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
lsPosition begin_pos = param.position;
|
||||
if (WorkingFile *file = working_files->GetFileByFilename(path)) {
|
||||
std::string completion_text;
|
||||
lsPosition end_pos = param.position;
|
||||
begin_pos = file->FindStableCompletionSource(param.position,
|
||||
&completion_text, &end_pos);
|
||||
}
|
||||
|
||||
CompletionManager::OnComplete callback =
|
||||
[reply, path, begin_pos](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
|
||||
reply(Consumer->ls_sighelp);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
cache.path = path;
|
||||
cache.position = begin_pos;
|
||||
cache.result = Consumer->ls_sighelp;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
CodeCompleteOptions CCOpts;
|
||||
CCOpts.IncludeGlobals = false;
|
||||
CCOpts.IncludeMacros = false;
|
||||
CCOpts.IncludeBriefComments = false;
|
||||
if (cache.IsCacheValid(path, begin_pos)) {
|
||||
SignatureHelpConsumer Consumer(CCOpts, true);
|
||||
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
|
||||
callback(&Consumer);
|
||||
} else {
|
||||
clang_complete->completion_request_.PushBack(
|
||||
std::make_unique<CompletionManager::CompletionRequest>(
|
||||
reply.id, param.textDocument, param.position,
|
||||
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
|
||||
callback));
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -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
|
189
src/messages/workspace.cc
Normal file
189
src/messages/workspace.cc
Normal file
@ -0,0 +1,189 @@
|
||||
/* Copyright 2017-2018 ccls Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "clang_complete.hh"
|
||||
#include "fuzzy_match.h"
|
||||
#include "log.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.h"
|
||||
|
||||
#include <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 ¶m) {
|
||||
for (auto &event : param.changes) {
|
||||
std::string path = event.uri.GetPath();
|
||||
IndexMode mode = working_files->GetFileByFilename(path)
|
||||
? IndexMode::Normal
|
||||
: IndexMode::NonInteractive;
|
||||
switch (event.type) {
|
||||
case FileChangeType::Created:
|
||||
case FileChangeType::Changed: {
|
||||
pipeline::Index(path, {}, mode);
|
||||
if (mode == IndexMode::Normal)
|
||||
clang_complete->NotifySave(path);
|
||||
else
|
||||
clang_complete->OnClose(path);
|
||||
break;
|
||||
}
|
||||
case FileChangeType::Deleted:
|
||||
pipeline::Index(path, {}, mode);
|
||||
clang_complete->OnClose(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageHandler::workspace_didChangeWorkspaceFolders(
|
||||
DidChangeWorkspaceFoldersParam ¶m) {
|
||||
for (const WorkspaceFolder &wf : param.event.removed) {
|
||||
std::string root = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(root);
|
||||
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
|
||||
auto it = llvm::find(g_config->workspaceFolders, root);
|
||||
if (it != g_config->workspaceFolders.end()) {
|
||||
g_config->workspaceFolders.erase(it);
|
||||
{
|
||||
// auto &folder = project->root2folder[path];
|
||||
// FIXME delete
|
||||
}
|
||||
project->root2folder.erase(root);
|
||||
}
|
||||
}
|
||||
for (const WorkspaceFolder &wf : param.event.added) {
|
||||
std::string root = wf.uri.GetPath();
|
||||
EnsureEndsInSlash(root);
|
||||
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
|
||||
g_config->workspaceFolders.push_back(root);
|
||||
project->Load(root);
|
||||
}
|
||||
|
||||
project->Index(working_files, lsRequestId());
|
||||
|
||||
clang_complete->FlushAllSessions();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Lookup |symbol| in |db| and insert the value into |result|.
|
||||
bool AddSymbol(
|
||||
DB *db, WorkingFiles *working_files, SymbolIdx sym, bool use_detailed,
|
||||
std::vector<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 ¶m,
|
||||
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
|
@ -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
|
@ -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
|
128
src/pipeline.cc
128
src/pipeline.cc
@ -7,15 +7,16 @@
|
||||
#include "config.h"
|
||||
#include "include_complete.h"
|
||||
#include "log.hh"
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
#include "match.h"
|
||||
#include "message_handler.h"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.h"
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
#include "query_utils.h"
|
||||
#include "serializers/json.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include <llvm/Support/Process.h>
|
||||
@ -31,9 +32,7 @@ using namespace llvm;
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
void StandaloneInitialize(const std::string &, Project &, WorkingFiles &, VFS &,
|
||||
IncludeComplete &);
|
||||
|
||||
namespace ccls {
|
||||
void VFS::Clear() {
|
||||
std::lock_guard lock(mutex);
|
||||
state.clear();
|
||||
@ -55,7 +54,10 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace ccls::pipeline {
|
||||
struct MessageHandler;
|
||||
void StandaloneInitialize(MessageHandler &, const std::string &root);
|
||||
|
||||
namespace pipeline {
|
||||
|
||||
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0),
|
||||
pending_index_requests = ATOMIC_VAR_INIT(0);
|
||||
@ -74,7 +76,7 @@ struct Index_Request {
|
||||
MultiQueueWaiter *main_waiter;
|
||||
MultiQueueWaiter *indexer_waiter;
|
||||
MultiQueueWaiter *stdout_waiter;
|
||||
ThreadedQueue<std::unique_ptr<InMessage>> *on_request;
|
||||
ThreadedQueue<InMessage> *on_request;
|
||||
ThreadedQueue<Index_Request> *index_request;
|
||||
ThreadedQueue<IndexUpdate> *on_indexed;
|
||||
ThreadedQueue<std::string> *for_stdout;
|
||||
@ -345,7 +347,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
|
||||
|
||||
void Init() {
|
||||
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);
|
||||
|
||||
indexer_waiter = new MultiQueueWaiter;
|
||||
@ -402,35 +404,53 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
|
||||
void LaunchStdin() {
|
||||
std::thread([]() {
|
||||
set_thread_name("stdin");
|
||||
std::string str;
|
||||
while (true) {
|
||||
std::unique_ptr<InMessage> message;
|
||||
std::optional<std::string> error =
|
||||
MessageRegistry::instance()->ReadMessageFromStdin(&message);
|
||||
|
||||
// Message parsing can fail if we don't recognize the method.
|
||||
if (error) {
|
||||
// The message may be partially deserialized.
|
||||
// Emit an error ResponseMessage if |id| is available.
|
||||
if (message) {
|
||||
lsRequestId id = message->GetRequestId();
|
||||
if (id.Valid()) {
|
||||
lsResponseError err;
|
||||
err.code = lsErrorCodes::InvalidParams;
|
||||
err.message = std::move(*error);
|
||||
ReplyError(id, err);
|
||||
}
|
||||
constexpr std::string_view kContentLength("Content-Length: ");
|
||||
int len = 0;
|
||||
str.clear();
|
||||
while (true) {
|
||||
int c = getchar();
|
||||
if (c == EOF)
|
||||
return;
|
||||
if (c == '\n') {
|
||||
if (str.empty())
|
||||
break;
|
||||
if (!str.compare(0, kContentLength.size(), kContentLength))
|
||||
len = atoi(str.c_str() + kContentLength.size());
|
||||
str.clear();
|
||||
} else if (c != '\r') {
|
||||
str += c;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache |method_id| so we can access it after moving |message|.
|
||||
MethodType method_type = message->GetMethodType();
|
||||
str.resize(len);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
int c = getchar();
|
||||
if (c == EOF)
|
||||
return;
|
||||
str[i] = c;
|
||||
}
|
||||
|
||||
on_request->PushBack(std::move(message));
|
||||
auto message = std::make_unique<char[]>(len);
|
||||
std::copy(str.begin(), str.end(), message.get());
|
||||
auto document = std::make_unique<rapidjson::Document>();
|
||||
document->Parse(message.get(), len);
|
||||
assert(!document->HasParseError());
|
||||
|
||||
// If the message was to exit then querydb will take care of the actual
|
||||
// exit. Stop reading from stdin since it might be detached.
|
||||
if (method_type == kMethodType_Exit)
|
||||
JsonReader reader{document.get()};
|
||||
if (!reader.HasMember("jsonrpc") ||
|
||||
std::string(reader["jsonrpc"]->GetString()) != "2.0")
|
||||
return;
|
||||
lsRequestId id;
|
||||
std::string method;
|
||||
ReflectMember(reader, "id", id);
|
||||
ReflectMember(reader, "method", method);
|
||||
auto param = std::make_unique<rapidjson::Value>();
|
||||
on_request->PushBack(
|
||||
{id, std::move(method), std::move(message), std::move(document)});
|
||||
|
||||
if (method == "exit")
|
||||
break;
|
||||
}
|
||||
})
|
||||
@ -479,31 +499,20 @@ void MainLoop() {
|
||||
DB db;
|
||||
|
||||
// Setup shared references.
|
||||
for (MessageHandler *handler : *MessageHandler::message_handlers) {
|
||||
handler->db = &db;
|
||||
handler->project = &project;
|
||||
handler->vfs = &vfs;
|
||||
handler->working_files = &working_files;
|
||||
handler->clang_complete = &clang_complete;
|
||||
handler->include_complete = &include_complete;
|
||||
}
|
||||
MessageHandler handler;
|
||||
handler.db = &db;
|
||||
handler.project = &project;
|
||||
handler.vfs = &vfs;
|
||||
handler.working_files = &working_files;
|
||||
handler.clang_complete = &clang_complete;
|
||||
handler.include_complete = &include_complete;
|
||||
|
||||
bool has_indexed = false;
|
||||
while (true) {
|
||||
std::vector<std::unique_ptr<InMessage>> messages = on_request->DequeueAll();
|
||||
std::vector<InMessage> messages = on_request->DequeueAll();
|
||||
bool did_work = messages.size();
|
||||
for (auto &message : messages) {
|
||||
// TODO: Consider using std::unordered_map to lookup the handler
|
||||
for (MessageHandler *handler : *MessageHandler::message_handlers) {
|
||||
if (handler->GetMethodType() == message->GetMethodType()) {
|
||||
handler->Run(std::move(message));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message)
|
||||
LOG_S(ERROR) << "No handler for " << message->GetMethodType();
|
||||
}
|
||||
for (InMessage &message : messages)
|
||||
handler.Run(message);
|
||||
|
||||
bool indexed = false;
|
||||
for (int i = 20; i--;) {
|
||||
@ -532,7 +541,14 @@ void Standalone(const std::string &root) {
|
||||
WorkingFiles wfiles;
|
||||
VFS vfs;
|
||||
IncludeComplete complete(&project);
|
||||
StandaloneInitialize(root, project, wfiles, vfs, complete);
|
||||
|
||||
MessageHandler handler;
|
||||
handler.project = &project;
|
||||
handler.working_files = &wfiles;
|
||||
handler.vfs = &vfs;
|
||||
handler.include_complete = &complete;
|
||||
|
||||
StandaloneInitialize(handler, root);
|
||||
bool tty = sys::Process::StandardOutIsDisplayed();
|
||||
|
||||
if (tty) {
|
||||
@ -622,5 +638,5 @@ void Reply(lsRequestId id, const std::function<void(Writer &)> &fn) {
|
||||
void ReplyError(lsRequestId id, const std::function<void(Writer &)> &fn) {
|
||||
Reply(id, "error", fn);
|
||||
}
|
||||
|
||||
} // namespace ccls::pipeline
|
||||
} // namespace pipeline
|
||||
} // namespace ccls
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
#include "query.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -12,9 +12,9 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ccls {
|
||||
struct CompletionManager;
|
||||
struct GroupMatch;
|
||||
struct VFS;
|
||||
struct Project;
|
||||
struct WorkingFiles;
|
||||
|
||||
@ -32,7 +32,6 @@ struct VFS {
|
||||
bool Stamp(const std::string &path, int64_t ts, int step);
|
||||
};
|
||||
|
||||
namespace ccls {
|
||||
enum class IndexMode {
|
||||
NonInteractive,
|
||||
OnChange,
|
||||
|
@ -3,12 +3,13 @@
|
||||
|
||||
#include "position.h"
|
||||
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace ccls {
|
||||
Position Position::FromString(const std::string &encoded) {
|
||||
char *p = const_cast<char *>(encoded.c_str());
|
||||
int16_t line = int16_t(strtol(p, &p, 10)) - 1;
|
||||
@ -100,3 +101,4 @@ void Reflect(Writer &visitor, Range &value) {
|
||||
Reflect(visitor, value.end.column);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace ccls {
|
||||
struct Position {
|
||||
int16_t line = -1;
|
||||
int16_t column = -1;
|
||||
@ -30,7 +31,6 @@ struct Position {
|
||||
}
|
||||
bool operator<=(const Position &o) const { return !(o < *this); }
|
||||
};
|
||||
MAKE_HASHABLE(Position, t.line, t.column);
|
||||
|
||||
struct Range {
|
||||
Position start;
|
||||
@ -52,20 +52,6 @@ struct Range {
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<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
|
||||
class Reader;
|
||||
class Writer;
|
||||
@ -73,3 +59,20 @@ void Reflect(Reader &visitor, Position &value);
|
||||
void Reflect(Writer &visitor, Position &value);
|
||||
void Reflect(Reader &visitor, Range &value);
|
||||
void Reflect(Writer &visitor, Range &value);
|
||||
} // namespace ccls
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<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);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "project.h"
|
||||
#include "project.hh"
|
||||
|
||||
#include "filesystem.hh"
|
||||
#include "log.hh"
|
||||
@ -31,10 +31,10 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using namespace ccls;
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
namespace ccls {
|
||||
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
|
||||
using namespace clang::driver;
|
||||
auto I = types::lookupTypeForExtension(
|
||||
@ -473,3 +473,4 @@ void Project::Index(WorkingFiles *wfiles, lsRequestId id) {
|
||||
// trigger refreshing semantic highlight for all working files.
|
||||
pipeline::Index("", {}, IndexMode::NonInteractive);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
@ -12,6 +12,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ccls {
|
||||
struct WorkingFiles;
|
||||
|
||||
std::pair<LanguageId, bool> lookupExtension(std::string_view filename);
|
||||
@ -64,3 +65,4 @@ struct Project {
|
||||
|
||||
void Index(WorkingFiles *wfiles, lsRequestId id);
|
||||
};
|
||||
} // namespace ccls
|
@ -4,7 +4,7 @@
|
||||
#include "query.h"
|
||||
|
||||
#include "indexer.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
#include "serializers/json.h"
|
||||
|
||||
#include <cassert>
|
||||
@ -15,8 +15,8 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
||||
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Use &use) {
|
||||
if (use.file_id == -1)
|
||||
use.file_id = file_id;
|
||||
@ -456,3 +456,4 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace ccls
|
||||
|
20
src/query.h
20
src/query.h
@ -4,23 +4,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "indexer.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <llvm/ADT/DenseMap.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<ExtentRef> {
|
||||
static inline ExtentRef getEmptyKey() { return {}; }
|
||||
static inline ExtentRef getTombstoneKey() { return {{Range(), Usr(-1)}}; }
|
||||
static unsigned getHashValue(ExtentRef sym) {
|
||||
return std::hash<ExtentRef>()(sym);
|
||||
template <> struct DenseMapInfo<ccls::ExtentRef> {
|
||||
static inline ccls::ExtentRef getEmptyKey() { return {}; }
|
||||
static inline ccls::ExtentRef getTombstoneKey() {
|
||||
return {{ccls::Range(), ccls::Usr(-1)}};
|
||||
}
|
||||
static bool isEqual(ExtentRef l, ExtentRef r) { return l == r; }
|
||||
static unsigned getHashValue(ccls::ExtentRef sym) {
|
||||
return std::hash<ccls::ExtentRef>()(sym);
|
||||
}
|
||||
static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; }
|
||||
};
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace ccls {
|
||||
struct QueryFile {
|
||||
struct Def {
|
||||
std::string path;
|
||||
@ -181,3 +184,4 @@ struct DB {
|
||||
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
|
||||
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <limits.h>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
||||
// Computes roughly how long |range| is.
|
||||
@ -330,3 +331,4 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
|
||||
return symbols;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace ccls {
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
|
||||
|
||||
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
|
||||
@ -90,3 +91,4 @@ void EachDefinedFunc(DB *db, const std::vector<Usr> &usrs, Fn &&fn) {
|
||||
fn(obj);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include "filesystem.hh"
|
||||
#include "indexer.h"
|
||||
@ -15,11 +15,11 @@
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace ccls;
|
||||
using namespace llvm;
|
||||
|
||||
bool gTestOutputMode = false;
|
||||
|
||||
namespace ccls {
|
||||
Reader::~Reader() {}
|
||||
BinaryReader::~BinaryReader() {}
|
||||
JsonReader::~JsonReader() {}
|
||||
@ -350,7 +350,6 @@ void Reflect(Writer &visitor, SerializeFormat &value) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace ccls {
|
||||
static BumpPtrAllocator Alloc;
|
||||
static DenseSet<CachedHashStringRef> Strings;
|
||||
static std::mutex AllocMutex;
|
||||
|
@ -23,6 +23,7 @@ class CachedHashStringRef;
|
||||
class StringRef;
|
||||
}
|
||||
|
||||
namespace ccls {
|
||||
enum class SerializeFormat { Binary, Json };
|
||||
|
||||
struct JsonNull {};
|
||||
@ -91,12 +92,12 @@ struct IndexFile;
|
||||
#define MAKE_REFLECT_TYPE_PROXY2(type, as_type) \
|
||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(Reader &visitor, type &value) { \
|
||||
as_type value0; \
|
||||
::Reflect(visitor, value0); \
|
||||
::ccls::Reflect(visitor, value0); \
|
||||
value = static_cast<type>(value0); \
|
||||
} \
|
||||
LLVM_ATTRIBUTE_UNUSED inline void Reflect(Writer &visitor, type &value) { \
|
||||
auto value0 = static_cast<as_type>(value); \
|
||||
::Reflect(visitor, value0); \
|
||||
::ccls::Reflect(visitor, value0); \
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
namespace ccls {
|
||||
const char *Intern(llvm::StringRef str);
|
||||
llvm::CachedHashStringRef InternH(llvm::StringRef str);
|
||||
std::string Serialize(SerializeFormat format, IndexFile &file);
|
@ -3,10 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace ccls {
|
||||
class BinaryReader : public Reader {
|
||||
const char *p_;
|
||||
|
||||
@ -123,3 +124,4 @@ public:
|
||||
void EndObject() override {}
|
||||
void Key(const char *name) override {}
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
|
||||
namespace ccls {
|
||||
class JsonReader : public Reader {
|
||||
rapidjson::GenericValue<rapidjson::UTF8<>> *m_;
|
||||
std::vector<const char *> path_;
|
||||
@ -104,3 +105,4 @@ public:
|
||||
void EndObject() override { m_->EndObject(); }
|
||||
void Key(const char *name) override { m_->Key(name); }
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "indexer.h"
|
||||
#include "pipeline.hh"
|
||||
#include "platform.h"
|
||||
#include "serializer.h"
|
||||
#include "serializer.hh"
|
||||
#include "utils.h"
|
||||
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
@ -32,6 +32,7 @@ using namespace llvm;
|
||||
|
||||
extern bool gTestOutputMode;
|
||||
|
||||
namespace ccls {
|
||||
std::string ToString(const rapidjson::Document &document) {
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||
@ -361,3 +362,4 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
|
||||
|
||||
return success;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -5,4 +5,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ccls {
|
||||
bool RunIndexTests(const std::string &filter_path, bool enable_update);
|
||||
}
|
||||
|
@ -13,17 +13,18 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
struct BaseThreadQueue {
|
||||
virtual bool IsEmpty() = 0;
|
||||
virtual ~BaseThreadQueue() = default;
|
||||
};
|
||||
|
||||
// std::lock accepts two or more arguments. We define an overload for one
|
||||
// argument.
|
||||
namespace std {
|
||||
template <typename Lockable> void lock(Lockable &l) { l.lock(); }
|
||||
} // namespace std
|
||||
|
||||
namespace ccls {
|
||||
struct BaseThreadQueue {
|
||||
virtual bool IsEmpty() = 0;
|
||||
virtual ~BaseThreadQueue() = default;
|
||||
};
|
||||
|
||||
template <typename... Queue> struct MultiQueueLock {
|
||||
MultiQueueLock(Queue... lockable) : tuple_{lockable...} { lock(); }
|
||||
~MultiQueueLock() { unlock(); }
|
||||
@ -161,3 +162,4 @@ private:
|
||||
MultiQueueWaiter *waiter_;
|
||||
std::unique_ptr<MultiQueueWaiter> owned_waiter_;
|
||||
};
|
||||
} // namespace ccls
|
||||
|
@ -21,6 +21,7 @@ using namespace llvm;
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ccls {
|
||||
uint64_t HashUsr(std::string_view s) {
|
||||
union {
|
||||
uint64_t ret;
|
||||
@ -162,3 +163,4 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text,
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory() { return DEFAULT_RESOURCE_DIRECTORY; }
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace llvm {
|
||||
class StringRef;
|
||||
}
|
||||
|
||||
namespace ccls {
|
||||
uint64_t HashUsr(std::string_view s);
|
||||
uint64_t HashUsr(llvm::StringRef s);
|
||||
|
||||
@ -69,10 +70,11 @@ inline void hash_combine(std::size_t &seed, const T &v, Rest... rest) {
|
||||
template <> struct hash<type> { \
|
||||
std::size_t operator()(const type &t) const { \
|
||||
std::size_t ret = 0; \
|
||||
hash_combine(ret, __VA_ARGS__); \
|
||||
ccls::hash_combine(ret, __VA_ARGS__); \
|
||||
return ret; \
|
||||
} \
|
||||
}; \
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory();
|
||||
} // namespace ccls
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
||||
// When finding a best match of buffer line and index line, limit the max edit
|
||||
@ -456,7 +457,7 @@ WorkingFile *WorkingFiles::OnOpen(const lsTextDocumentItem &open) {
|
||||
return files[files.size() - 1].get();
|
||||
}
|
||||
|
||||
void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) {
|
||||
void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) {
|
||||
std::lock_guard<std::mutex> lock(files_mutex);
|
||||
|
||||
std::string filename = change.textDocument.uri.GetPath();
|
||||
@ -471,7 +472,7 @@ void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams &change) {
|
||||
if (change.textDocument.version)
|
||||
file->version = *change.textDocument.version;
|
||||
|
||||
for (const lsTextDocumentContentChangeEvent &diff : change.contentChanges) {
|
||||
for (const TextDocumentContentChangeEvent &diff : change.contentChanges) {
|
||||
// Per the spec replace everything if the rangeLength and range are not set.
|
||||
// See https://github.com/Microsoft/language-server-protocol/issues/9.
|
||||
if (!diff.range) {
|
||||
@ -563,3 +564,4 @@ std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
|
||||
return content.substr(start, end - start);
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lsp.h"
|
||||
#include "lsp.hh"
|
||||
#include "utils.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace ccls {
|
||||
struct WorkingFile {
|
||||
int version = 0;
|
||||
std::string filename;
|
||||
@ -105,7 +106,7 @@ struct WorkingFiles {
|
||||
const std::function<void(WorkingFile *file)> &action);
|
||||
|
||||
WorkingFile *OnOpen(const lsTextDocumentItem &open);
|
||||
void OnChange(const lsTextDocumentDidChangeParams &change);
|
||||
void OnChange(const TextDocumentDidChangeParam &change);
|
||||
void OnClose(const lsTextDocumentIdentifier &close);
|
||||
|
||||
// If |filter_paths| is non-empty, only files which contain any of the given
|
||||
@ -123,3 +124,4 @@ int GetOffsetForPosition(lsPosition position, std::string_view content);
|
||||
|
||||
std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
std::string_view content);
|
||||
} // namespace ccls
|
||||
|
Loading…
Reference in New Issue
Block a user