Refactor message handler and namespace ccls

This commit is contained in:
Fangrui Song 2018-10-28 10:49:31 -07:00
parent 05929b8afb
commit 67eb1e6b23
73 changed files with 2455 additions and 3126 deletions

View File

@ -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
)

View File

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

View File

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

View File

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

View File

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

View File

@ -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('>');

View File

@ -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);
}

View File

@ -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;

View File

@ -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") {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)

View File

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

View File

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

View File

@ -1,122 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "lsp.h"
// The kind of a completion entry.
enum class lsCompletionItemKind {
Text = 1,
Method = 2,
Function = 3,
Constructor = 4,
Field = 5,
Variable = 6,
Class = 7,
Interface = 8,
Module = 9,
Property = 10,
Unit = 11,
Value = 12,
Enum = 13,
Keyword = 14,
Snippet = 15,
Color = 16,
File = 17,
Reference = 18,
Folder = 19,
EnumMember = 20,
Constant = 21,
Struct = 22,
Event = 23,
Operator = 24,
TypeParameter = 25,
};
MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind);
// Defines whether the insert text in a completion item should be interpreted as
// plain text or a snippet.
enum class lsInsertTextFormat {
// The primary text to be inserted is treated as a plain string.
PlainText = 1,
// The primary text to be inserted is treated as a snippet.
//
// A snippet can define tab stops and placeholders with `$1`, `$2`
// and `${3:foo}`. `$0` defines the final tab stop, it defaults to
// the end of the snippet. Placeholders with equal identifiers are linked,
// that is typing in one will update others too.
//
// See also:
// https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
Snippet = 2
};
MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat);
struct lsCompletionItem {
// A set of function parameters. Used internally for signature help. Not sent
// to vscode.
std::vector<std::string> parameters_;
// The label of this completion item. By default
// also the text that is inserted when selecting
// this completion.
std::string label;
// The kind of this completion item. Based of the kind
// an icon is chosen by the editor.
lsCompletionItemKind kind = lsCompletionItemKind::Text;
// A human-readable string with additional information
// about this item, like type or symbol information.
std::string detail;
// A human-readable string that represents a doc-comment.
std::optional<std::string> documentation;
// Internal information to order candidates.
int score_;
unsigned priority_;
// Use <> or "" by default as include path.
bool use_angle_brackets_ = false;
// A string that shoud be used when comparing this item
// with other items. When `falsy` the label is used.
std::string sortText;
// A string that should be used when filtering a set of
// completion items. When `falsy` the label is used.
std::optional<std::string> filterText;
// A string that should be inserted a document when selecting
// this completion. When `falsy` the label is used.
std::string insertText;
// The format of the insert text. The format applies to both the `insertText`
// property and the `newText` property of a provided `textEdit`.
lsInsertTextFormat insertTextFormat = lsInsertTextFormat::PlainText;
// An edit which is applied to a document when selecting this completion. When
// an edit is provided the value of `insertText` is ignored.
//
// *Note:* The range of the edit must be a single line range and it must
// contain the position at which completion has been requested.
lsTextEdit textEdit;
// An std::optional array of additional text edits that are applied when
// selecting this completion. Edits must not overlap with the main edit
// nor with themselves.
std::vector<lsTextEdit> additionalTextEdits;
// An std::optional command that is executed *after* inserting this
// completion. *Note* that additional modifications to the current document
// should be described with the additionalTextEdits-property. Command command;
// An data entry field that is preserved on a completion item between
// a completion and a completion resolve request.
// data ? : any
};
MAKE_REFLECT_STRUCT(lsCompletionItem, label, kind, detail, documentation,
sortText, filterText, insertText, insertTextFormat,
textEdit, additionalTextEdits);

View File

@ -4,7 +4,7 @@
#include "log.hh"
#include "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;
}

View File

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

View File

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

View File

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

View File

@ -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(&param);
if (msg.id.Valid()) {
ReplyOnce reply{msg.id};
auto it = method2request.find(msg.method);
if (it != method2request.end()) {
try {
it->second(reader, reply);
} catch (...) {
LOG_S(ERROR) << "failed to process request " << msg.method;
}
} else {
lsResponseError err;
err.code = lsErrorCodes::MethodNotFound;
err.message = "unknown request " + msg.method;
reply.Error(err);
}
} else {
auto it = method2notification.find(msg.method);
if (it != method2notification.end())
try {
it->second(reader);
} catch (...) {
LOG_S(ERROR) << "failed to process " << msg.method;
}
}
}
QueryFile *MessageHandler::FindFile(ReplyOnce &reply,
const std::string &path,
int *out_file_id) {
QueryFile *ret = nullptr;
auto it = db->name2file_id.find(LowerPathIfInsensitive(path));
if (it != db->name2file_id.end()) {
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

View File

@ -1,71 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "lsp.h"
#include "query.h"
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
struct CompletionManager;
struct Config;
struct GroupMatch;
struct VFS;
struct IncludeComplete;
struct MultiQueueWaiter;
struct Project;
struct DB;
struct WorkingFile;
struct WorkingFiles;
// Usage:
//
// struct FooHandler : MessageHandler {
// // ...
// };
// REGISTER_MESSAGE_HANDLER(FooHandler);
//
// Then there will be a global FooHandler instance in
// |MessageHandler::message_handlers|.
#define REGISTER_MESSAGE_HANDLER(type) \
static type type##message_handler_instance_;
struct MessageHandler {
DB *db = nullptr;
Project *project = nullptr;
VFS *vfs = nullptr;
WorkingFiles *working_files = nullptr;
CompletionManager *clang_complete = nullptr;
IncludeComplete *include_complete = nullptr;
virtual MethodType GetMethodType() const = 0;
virtual void Run(std::unique_ptr<InMessage> message) = 0;
static std::vector<MessageHandler *> *message_handlers;
protected:
MessageHandler();
};
template <typename TMessage> struct BaseMessageHandler : MessageHandler {
virtual void Run(TMessage *message) = 0;
// MessageHandler:
void Run(std::unique_ptr<InMessage> message) override {
Run(static_cast<TMessage *>(message.get()));
}
};
bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id,
const std::string &absolute_path,
QueryFile **out_query_file, int *out_file_id = nullptr);
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file);
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);

265
src/message_handler.hh Normal file
View 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

View File

@ -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 &params = 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

View File

@ -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 &param, 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

View File

@ -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 &param, 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 &params = 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 &param, ReplyOnce &reply) {
Param param1;
param1.textDocument = param.textDocument;
param1.position = param.position;
param1.derived = true;
Inheritance(this, param1, reply);
}
} // namespace ccls

View File

@ -3,7 +3,7 @@
#include "clang_tu.hh"
#include "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 &params = 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

View File

@ -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 &params = 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

View File

@ -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 &params = 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

View File

@ -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 &params = 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

View File

@ -1,19 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
namespace {
struct In_Exit : public NotificationMessage {
MethodType GetMethodType() const override { return kMethodType_Exit; }
};
MAKE_REFLECT_EMPTY_STRUCT(In_Exit);
REGISTER_IN_MESSAGE(In_Exit);
struct Handler_Exit : MessageHandler {
MethodType GetMethodType() const override { return kMethodType_Exit; }
void Run(std::unique_ptr<InMessage> request) override { exit(0); }
};
REGISTER_MESSAGE_HANDLER(Handler_Exit);
} // namespace

View File

@ -5,20 +5,21 @@
#include "filesystem.hh"
#include "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 &params = request->params;
if (!params.rootUri)
return;
std::string project_path = NormalizePath(params.rootUri->GetPath());
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
<< 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 &param, 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

View File

@ -1,25 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
#include "pipeline.hh"
using namespace ccls;
namespace {
MethodType kMethodType = "shutdown";
struct In_Shutdown : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
};
MAKE_REFLECT_STRUCT(In_Shutdown, id);
REGISTER_IN_MESSAGE(In_Shutdown);
struct Handler_Shutdown : BaseMessageHandler<In_Shutdown> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_Shutdown *request) override {
JsonNull result;
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_Shutdown);
} // namespace

View File

@ -0,0 +1,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 &param,
ReplyOnce &reply) {
WorkingFile *wfile =
working_files->GetFileByFilename(param.textDocument.uri.GetPath());
if (!wfile) {
return;
}
std::vector<CodeAction> result;
std::vector<lsDiagnostic> diagnostics;
working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; });
for (lsDiagnostic &diag : diagnostics)
if (diag.fixits_.size()) {
CodeAction &cmd = result.emplace_back();
cmd.title = "FixIt: " + diag.message;
auto &edit = cmd.edit.documentChanges.emplace_back();
edit.textDocument.uri = param.textDocument.uri;
edit.textDocument.version = wfile->version;
edit.edits = diag.fixits_;
}
reply(result);
}
namespace {
struct Cmd_xref {
Usr usr;
SymbolKind kind;
std::string field;
};
struct lsCommand {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
struct lsCodeLens {
lsRange range;
std::optional<lsCommand> command;
};
MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field);
MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments);
MAKE_REFLECT_STRUCT(lsCodeLens, range, command);
template <typename T>
std::string ToString(T &v) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer);
Reflect(json_writer, v);
return output.GetString();
}
struct CommonCodeLensParams {
std::vector<lsCodeLens> *result;
DB *db;
WorkingFile *wfile;
};
} // namespace
void MessageHandler::textDocument_codeLens(TextDocumentParam &param,
ReplyOnce &reply) {
std::vector<lsCodeLens> result;
std::string path = param.textDocument.uri.GetPath();
QueryFile *file = FindFile(reply, path);
WorkingFile *wfile =
file ? working_files->GetFileByFilename(file->def->path) : nullptr;
if (!wfile) {
return;
}
auto Add = [&](const char *singular, Cmd_xref show, Range range, int num,
bool force_display = false) {
if (!num && !force_display)
return;
std::optional<lsRange> ls_range = GetLsRange(wfile, range);
if (!ls_range)
return;
lsCodeLens &code_lens = result.emplace_back();
code_lens.range = *ls_range;
code_lens.command = lsCommand();
code_lens.command->command = std::string(ccls_xref);
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
code_lens.command->title =
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
code_lens.command->arguments.push_back(ToString(show));
};
std::unordered_set<Range> seen;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second)
continue;
switch (sym.kind) {
case SymbolKind::Func: {
QueryFunc &func = db->GetFunc(sym);
const QueryFunc::Def *def = func.AnyDef();
if (!def)
continue;
std::vector<Use> base_uses = GetUsesForAllBases(db, func);
std::vector<Use> derived_uses = GetUsesForAllDerived(db, func);
Add("ref", {sym.usr, SymbolKind::Func, "uses"}, sym.range,
func.uses.size(), base_uses.empty());
if (base_uses.size())
Add("b.ref", {sym.usr, SymbolKind::Func, "bases uses"}, sym.range,
base_uses.size());
if (derived_uses.size())
Add("d.ref", {sym.usr, SymbolKind::Func, "derived uses"}, sym.range,
derived_uses.size());
if (base_uses.empty())
Add("base", {sym.usr, SymbolKind::Func, "bases"}, sym.range,
def->bases.size());
Add("derived", {sym.usr, SymbolKind::Func, "derived"}, sym.range,
func.derived.size());
break;
}
case SymbolKind::Type: {
QueryType &type = db->GetType(sym);
Add("ref", {sym.usr, SymbolKind::Type, "uses"}, sym.range,
type.uses.size(), true);
Add("derived", {sym.usr, SymbolKind::Type, "derived"}, sym.range,
type.derived.size());
Add("var", {sym.usr, SymbolKind::Type, "instances"}, sym.range,
type.instances.size());
break;
}
case SymbolKind::Var: {
QueryVar &var = db->GetVar(sym);
const QueryVar::Def *def = var.AnyDef();
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
continue;
Add("ref", {sym.usr, SymbolKind::Var, "uses"}, sym.range, var.uses.size(),
def->kind != lsSymbolKind::Macro);
break;
}
case SymbolKind::File:
case SymbolKind::Invalid:
llvm_unreachable("");
};
}
reply(result);
}
void MessageHandler::workspace_executeCommand(Reader &reader,
ReplyOnce &reply) {
lsCommand param;
Reflect(reader, param);
if (param.arguments.empty()) {
return;
}
rapidjson::Document reader1;
reader1.Parse(param.arguments[0].c_str());
JsonReader json_reader{&reader1};
if (param.command == ccls_xref) {
Cmd_xref cmd;
Reflect(json_reader, cmd);
std::vector<lsLocation> result;
auto Map = [&](auto &&uses) {
for (auto &use : uses)
if (auto loc = GetLsLocation(db, working_files, use))
result.push_back(std::move(*loc));
};
switch (cmd.kind) {
case SymbolKind::Func: {
QueryFunc &func = db->Func(cmd.usr);
if (cmd.field == "bases") {
if (auto *def = func.AnyDef())
Map(GetFuncDeclarations(db, def->bases));
} else if (cmd.field == "bases uses") {
Map(GetUsesForAllBases(db, func));
} else if (cmd.field == "derived") {
Map(GetFuncDeclarations(db, func.derived));
} else if (cmd.field == "derived uses") {
Map(GetUsesForAllDerived(db, func));
} else if (cmd.field == "uses") {
Map(func.uses);
}
break;
}
case SymbolKind::Type: {
QueryType &type = db->Type(cmd.usr);
if (cmd.field == "derived") {
Map(GetTypeDeclarations(db, type.derived));
} else if (cmd.field == "instances") {
Map(GetVarDeclarations(db, type.instances, 7));
} else if (cmd.field == "uses") {
Map(type.uses);
}
break;
}
case SymbolKind::Var: {
QueryVar &var = db->Var(cmd.usr);
if (cmd.field == "uses")
Map(var.uses);
break;
}
default:
break;
}
reply(result);
}
}
} // namespace ccls

View File

@ -1,71 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
#include "pipeline.hh"
#include "working_files.h"
using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/codeAction";
struct In_TextDocumentCodeAction : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
// Contains additional diagnostic information about the context in which
// a code action is run.
struct lsCodeActionContext {
// An array of diagnostics.
std::vector<lsDiagnostic> diagnostics;
};
// Params for the CodeActionRequest
struct lsCodeActionParams {
// The document in which the command was invoked.
lsTextDocumentIdentifier textDocument;
// The range for which the command was invoked.
lsRange range;
// Context carrying additional information.
lsCodeActionContext context;
} params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionContext,
diagnostics);
MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionParams, textDocument,
range, context);
MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentCodeAction);
struct lsCodeAction {
std::string title;
const char *kind = "quickfix";
lsWorkspaceEdit edit;
};
MAKE_REFLECT_STRUCT(lsCodeAction, title, kind, edit);
struct Handler_TextDocumentCodeAction
: BaseMessageHandler<In_TextDocumentCodeAction> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentCodeAction *request) override {
const auto &params = request->params;
WorkingFile *wfile =
working_files->GetFileByFilename(params.textDocument.uri.GetPath());
if (!wfile)
return;
std::vector<lsCodeAction> result;
std::vector<lsDiagnostic> diagnostics;
working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; });
for (lsDiagnostic &diag : diagnostics)
if (diag.fixits_.size()) {
lsCodeAction &cmd = result.emplace_back();
cmd.title = "FixIt: " + diag.message;
auto &edit = cmd.edit.documentChanges.emplace_back();
edit.textDocument.uri = params.textDocument.uri;
edit.textDocument.version = wfile->version;
edit.edits = diag.fixits_;
}
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeAction);
} // namespace

View File

@ -1,219 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
#include "pipeline.hh"
#include "query_utils.h"
#include "serializers/json.h"
#include <llvm/Support/FormatVariadic.h>
#include <unordered_set>
using namespace ccls;
namespace {
const MethodType codeLens = "textDocument/codeLens",
executeCommand = "workspace/executeCommand";
struct lsCommand {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
MAKE_REFLECT_STRUCT(lsCommand, title, command, arguments);
struct lsCodeLens {
lsRange range;
std::optional<lsCommand> command;
};
MAKE_REFLECT_STRUCT(lsCodeLens, range, command);
struct Cmd_xref {
Usr usr;
SymbolKind kind;
std::string field;
};
MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field);
template <typename T>
std::string ToString(T &v) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer(&writer);
Reflect(json_writer, v);
return output.GetString();
}
struct CommonCodeLensParams {
std::vector<lsCodeLens> *result;
DB *db;
WorkingFile *wfile;
};
struct In_TextDocumentCodeLens : public RequestMessage {
MethodType GetMethodType() const override { return codeLens; }
struct Params {
lsTextDocumentIdentifier textDocument;
} params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens::Params, textDocument);
MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentCodeLens);
struct Handler_TextDocumentCodeLens
: BaseMessageHandler<In_TextDocumentCodeLens> {
MethodType GetMethodType() const override { return codeLens; }
void Run(In_TextDocumentCodeLens *request) override {
auto &params = request->params;
std::vector<lsCodeLens> result;
std::string path = params.textDocument.uri.GetPath();
QueryFile *file;
if (!FindFileOrFail(db, project, request->id, path, &file))
return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
auto Add = [&](const char *singular, Cmd_xref show, Range range, int num,
bool force_display = false) {
if (!num && !force_display)
return;
std::optional<lsRange> ls_range = GetLsRange(wfile, range);
if (!ls_range)
return;
lsCodeLens &code_lens = result.emplace_back();
code_lens.range = *ls_range;
code_lens.command = lsCommand();
code_lens.command->command = std::string(ccls_xref);
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
code_lens.command->title =
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
code_lens.command->arguments.push_back(ToString(show));
};
std::unordered_set<Range> seen;
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0 || !sym.extent.Valid() || !seen.insert(sym.range).second)
continue;
switch (sym.kind) {
case SymbolKind::Func: {
QueryFunc &func = db->GetFunc(sym);
const QueryFunc::Def *def = func.AnyDef();
if (!def)
continue;
std::vector<Use> base_uses = GetUsesForAllBases(db, func);
std::vector<Use> derived_uses = GetUsesForAllDerived(db, func);
Add("ref", {sym.usr, SymbolKind::Func, "uses"}, sym.range,
func.uses.size(), base_uses.empty());
if (base_uses.size())
Add("b.ref", {sym.usr, SymbolKind::Func, "bases uses"}, sym.range,
base_uses.size());
if (derived_uses.size())
Add("d.ref", {sym.usr, SymbolKind::Func, "derived uses"}, sym.range,
derived_uses.size());
if (base_uses.empty())
Add("base", {sym.usr, SymbolKind::Func, "bases"}, sym.range,
def->bases.size());
Add("derived", {sym.usr, SymbolKind::Func, "derived"}, sym.range,
func.derived.size());
break;
}
case SymbolKind::Type: {
QueryType &type = db->GetType(sym);
Add("ref", {sym.usr, SymbolKind::Type, "uses"}, sym.range, type.uses.size(),
true);
Add("derived", {sym.usr, SymbolKind::Type, "derived"}, sym.range,
type.derived.size());
Add("var", {sym.usr, SymbolKind::Type, "instances"}, sym.range,
type.instances.size());
break;
}
case SymbolKind::Var: {
QueryVar &var = db->GetVar(sym);
const QueryVar::Def *def = var.AnyDef();
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
continue;
Add("ref", {sym.usr, SymbolKind::Var, "uses"}, sym.range, var.uses.size(),
def->kind != lsSymbolKind::Macro);
break;
}
case SymbolKind::File:
case SymbolKind::Invalid:
llvm_unreachable("");
};
}
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens);
struct In_WorkspaceExecuteCommand : public RequestMessage {
MethodType GetMethodType() const override { return executeCommand; }
lsCommand params;
};
MAKE_REFLECT_STRUCT(In_WorkspaceExecuteCommand, id, params);
REGISTER_IN_MESSAGE(In_WorkspaceExecuteCommand);
struct Handler_WorkspaceExecuteCommand
: BaseMessageHandler<In_WorkspaceExecuteCommand> {
MethodType GetMethodType() const override { return executeCommand; }
void Run(In_WorkspaceExecuteCommand *request) override {
const auto &params = request->params;
if (params.arguments.empty())
return;
rapidjson::Document reader;
reader.Parse(params.arguments[0].c_str());
JsonReader json_reader{&reader};
if (params.command == ccls_xref) {
Cmd_xref cmd;
Reflect(json_reader, cmd);
std::vector<lsLocation> result;
auto Map = [&](auto &&uses) {
for (auto &use : uses)
if (auto loc = GetLsLocation(db, working_files, use))
result.push_back(std::move(*loc));
};
switch (cmd.kind) {
case SymbolKind::Func: {
QueryFunc &func = db->Func(cmd.usr);
if (cmd.field == "bases") {
if (auto *def = func.AnyDef())
Map(GetFuncDeclarations(db, def->bases));
} else if (cmd.field == "bases uses") {
Map(GetUsesForAllBases(db, func));
} else if (cmd.field == "derived") {
Map(GetFuncDeclarations(db, func.derived));
} else if (cmd.field == "derived uses") {
Map(GetUsesForAllDerived(db, func));
} else if (cmd.field == "uses") {
Map(func.uses);
}
break;
}
case SymbolKind::Type: {
QueryType &type = db->Type(cmd.usr);
if (cmd.field == "derived") {
Map(GetTypeDeclarations(db, type.derived));
} else if (cmd.field == "instances") {
Map(GetVarDeclarations(db, type.instances, 7));
} else if (cmd.field == "uses") {
Map(type.uses);
}
break;
}
case SymbolKind::Var: {
QueryVar &var = db->Var(cmd.usr);
if (cmd.field == "uses")
Map(var.uses);
break;
}
default:
break;
}
pipeline::Reply(request->id, result);
}
}
};
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceExecuteCommand);
} // namespace

View File

@ -5,7 +5,7 @@
#include "fuzzy_match.h"
#include "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 &param,
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 &params = 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

View File

@ -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 &params = 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 &param,
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 &param, ReplyOnce &reply) {
QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath());
if (!file)
return;
WorkingFile *working_file = working_files->GetFileByFilename(file->def->path);
std::vector<lsLocation> result;
auto Add = [&](const QueryType &type) {
for (const auto &def : type.def)
if (def.spell) {
if (auto ls_loc = GetLsLocation(db, working_files, *def.spell))
result.push_back(*ls_loc);
}
if (result.empty())
for (const DeclRef &dr : type.declarations)
if (auto ls_loc = GetLsLocation(db, working_files, dr))
result.push_back(*ls_loc);
};
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

View File

@ -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 &param) {
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 &param) {
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 &param) {
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 &params = 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 &params = request->params;
const std::string &path = params.textDocument.uri.GetPath();
WorkingFile *working_file = working_files->OnOpen(params.textDocument);
if (std::optional<std::string> cached_file_contents =
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 &params = 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 &param) {
const std::string &path = param.textDocument.uri.GetPath();
pipeline::Index(path, {}, IndexMode::Normal);
clang_complete->NotifySave(path);
}
} // namespace ccls

View File

@ -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 &param, 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 &param,
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 &params = 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

View File

@ -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 &param,
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

View File

@ -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 &params = 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 &params = 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 &params = 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 &param,
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 &param, 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 &param, 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

View File

@ -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 &params = 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 &param,
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

View File

@ -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 &params = 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

View File

@ -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 &param, 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

View File

@ -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 &params = 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 &param, 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

View File

@ -1,72 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
#include "pipeline.hh"
#include "query_utils.h"
using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/typeDefinition";
struct In_TextDocumentTypeDefinition : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentPositionParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentTypeDefinition, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentTypeDefinition);
struct Handler_TextDocumentTypeDefinition
: BaseMessageHandler<In_TextDocumentTypeDefinition> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentTypeDefinition *request) override {
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file,
nullptr))
return;
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
std::vector<lsLocation> result;
auto Add = [&](const QueryType &type) {
for (const auto &def : type.def)
if (def.spell) {
if (auto ls_loc = GetLsLocation(db, working_files, *def.spell))
result.push_back(*ls_loc);
}
if (result.empty())
for (const DeclRef &dr : type.declarations)
if (auto ls_loc = GetLsLocation(db, working_files, dr))
result.push_back(*ls_loc);
};
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) {
switch (sym.kind) {
case SymbolKind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef();
if (def && def->type)
Add(db->Type(def->type));
break;
}
case SymbolKind::Type: {
for (auto &def : db->GetType(sym).def)
if (def.alias_of) {
Add(db->Type(def.alias_of));
break;
}
break;
}
default:
break;
}
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentTypeDefinition);
} // namespace

189
src/messages/workspace.cc Normal file
View 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 &param) {
for (auto &event : param.changes) {
std::string path = event.uri.GetPath();
IndexMode mode = working_files->GetFileByFilename(path)
? IndexMode::Normal
: IndexMode::NonInteractive;
switch (event.type) {
case FileChangeType::Created:
case FileChangeType::Changed: {
pipeline::Index(path, {}, mode);
if (mode == IndexMode::Normal)
clang_complete->NotifySave(path);
else
clang_complete->OnClose(path);
break;
}
case FileChangeType::Deleted:
pipeline::Index(path, {}, mode);
clang_complete->OnClose(path);
break;
}
}
}
void MessageHandler::workspace_didChangeWorkspaceFolders(
DidChangeWorkspaceFoldersParam &param) {
for (const WorkspaceFolder &wf : param.event.removed) {
std::string root = wf.uri.GetPath();
EnsureEndsInSlash(root);
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
auto it = llvm::find(g_config->workspaceFolders, root);
if (it != g_config->workspaceFolders.end()) {
g_config->workspaceFolders.erase(it);
{
// auto &folder = project->root2folder[path];
// FIXME delete
}
project->root2folder.erase(root);
}
}
for (const WorkspaceFolder &wf : param.event.added) {
std::string root = wf.uri.GetPath();
EnsureEndsInSlash(root);
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
g_config->workspaceFolders.push_back(root);
project->Load(root);
}
project->Index(working_files, lsRequestId());
clang_complete->FlushAllSessions();
}
namespace {
// Lookup |symbol| in |db| and insert the value into |result|.
bool AddSymbol(
DB *db, WorkingFiles *working_files, SymbolIdx sym, bool use_detailed,
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> *result) {
std::optional<lsSymbolInformation> info = GetSymbolInfo(db, sym, true);
if (!info)
return false;
Use loc;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym))
loc = *dr;
else {
auto decls = GetNonDefDeclarations(db, sym);
if (decls.empty())
return false;
loc = decls[0];
}
std::optional<lsLocation> ls_location = GetLsLocation(db, working_files, loc);
if (!ls_location)
return false;
info->location = *ls_location;
result->emplace_back(*info, int(use_detailed), sym);
return true;
}
} // namespace
void MessageHandler::workspace_symbol(WorkspaceSymbolParam &param,
ReplyOnce &reply) {
std::vector<lsSymbolInformation> result;
std::string query = param.query;
// {symbol info, matching detailed_name or short_name, index}
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> cands;
bool sensitive = g_config->workspaceSymbol.caseSensitivity;
// Find subsequence matches.
std::string query_without_space;
query_without_space.reserve(query.size());
for (char c : query)
if (!isspace(c))
query_without_space += c;
auto Add = [&](SymbolIdx sym) {
std::string_view detailed_name = db->GetSymbolName(sym, true);
int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive);
return pos >= 0 &&
AddSymbol(db, working_files, sym,
detailed_name.find(':', pos) != std::string::npos,
&cands) &&
cands.size() >= g_config->workspaceSymbol.maxNum;
};
for (auto &func : db->funcs)
if (Add({func.usr, SymbolKind::Func}))
goto done_add;
for (auto &type : db->types)
if (Add({type.usr, SymbolKind::Type}))
goto done_add;
for (auto &var : db->vars)
if (var.def.size() && !var.def[0].is_local() &&
Add({var.usr, SymbolKind::Var}))
goto done_add;
done_add:
if (g_config->workspaceSymbol.sort && query.size() <= FuzzyMatcher::kMaxPat) {
// Sort results with a fuzzy matching algorithm.
int longest = 0;
for (auto &cand : cands)
longest = std::max(
longest, int(db->GetSymbolName(std::get<2>(cand), true).size()));
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
for (auto &cand : cands)
std::get<1>(cand) =
fuzzy.Match(db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)));
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
return std::get<1>(l) > std::get<1>(r);
});
result.reserve(cands.size());
for (auto &cand : cands) {
// Discard awful candidates.
if (std::get<1>(cand) <= FuzzyMatcher::kMinScore)
break;
result.push_back(std::get<0>(cand));
}
} else {
result.reserve(cands.size());
for (auto &cand : cands)
result.push_back(std::get<0>(cand));
}
reply(result);
}
} // namespace ccls

View File

@ -1,163 +0,0 @@
/* Copyright 2017-2018 ccls Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "clang_complete.hh"
#include "log.hh"
#include "message_handler.h"
#include "pipeline.hh"
#include "project.h"
#include "working_files.h"
#include <llvm/ADT/STLExtras.h>
using namespace ccls;
namespace {
MethodType didChangeConfiguration = "workspace/didChangeConfiguration",
didChangeWatchedFiles = "workspace/didChangeWatchedFiles",
didChangeWorkspaceFolders = "workspace/didChangeWorkspaceFolders";
struct lsDidChangeConfigurationParams {
bool placeholder;
};
MAKE_REFLECT_STRUCT(lsDidChangeConfigurationParams, placeholder);
struct In_workspaceDidChangeConfiguration : public NotificationMessage {
MethodType GetMethodType() const override { return didChangeConfiguration; }
lsDidChangeConfigurationParams params;
};
MAKE_REFLECT_STRUCT(In_workspaceDidChangeConfiguration, params);
REGISTER_IN_MESSAGE(In_workspaceDidChangeConfiguration);
struct Handler_workspaceDidChangeConfiguration
: BaseMessageHandler<In_workspaceDidChangeConfiguration> {
MethodType GetMethodType() const override { return didChangeConfiguration; }
void Run(In_workspaceDidChangeConfiguration *request) override {
for (const std::string &folder : g_config->workspaceFolders)
project->Load(folder);
project->Index(working_files, lsRequestId());
clang_complete->FlushAllSessions();
}
};
REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeConfiguration);
enum class lsFileChangeType {
Created = 1,
Changed = 2,
Deleted = 3,
};
MAKE_REFLECT_TYPE_PROXY(lsFileChangeType);
struct lsFileEvent {
lsDocumentUri uri;
lsFileChangeType type;
};
MAKE_REFLECT_STRUCT(lsFileEvent, uri, type);
struct lsDidChangeWatchedFilesParams {
std::vector<lsFileEvent> changes;
};
MAKE_REFLECT_STRUCT(lsDidChangeWatchedFilesParams, changes);
struct In_WorkspaceDidChangeWatchedFiles : public NotificationMessage {
MethodType GetMethodType() const override { return didChangeWatchedFiles; }
lsDidChangeWatchedFilesParams params;
};
MAKE_REFLECT_STRUCT(In_WorkspaceDidChangeWatchedFiles, params);
REGISTER_IN_MESSAGE(In_WorkspaceDidChangeWatchedFiles);
struct Handler_WorkspaceDidChangeWatchedFiles
: BaseMessageHandler<In_WorkspaceDidChangeWatchedFiles> {
MethodType GetMethodType() const override { return didChangeWatchedFiles; }
void Run(In_WorkspaceDidChangeWatchedFiles *request) override {
for (lsFileEvent &event : request->params.changes) {
std::string path = event.uri.GetPath();
IndexMode mode = working_files->GetFileByFilename(path)
? IndexMode::Normal
: IndexMode::NonInteractive;
switch (event.type) {
case lsFileChangeType::Created:
case lsFileChangeType::Changed: {
pipeline::Index(path, {}, mode);
if (mode == IndexMode::Normal)
clang_complete->NotifySave(path);
else
clang_complete->OnClose(path);
break;
}
case lsFileChangeType::Deleted:
pipeline::Index(path, {}, mode);
clang_complete->OnClose(path);
break;
}
}
}
};
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceDidChangeWatchedFiles);
struct lsWorkspaceFoldersChangeEvent {
std::vector<lsWorkspaceFolder> added, removed;
};
MAKE_REFLECT_STRUCT(lsWorkspaceFoldersChangeEvent, added, removed);
struct In_workspaceDidChangeWorkspaceFolders : public NotificationMessage {
MethodType GetMethodType() const override {
return didChangeWorkspaceFolders;
}
struct Params {
lsWorkspaceFoldersChangeEvent event;
} params;
};
MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders::Params, event);
MAKE_REFLECT_STRUCT(In_workspaceDidChangeWorkspaceFolders, params);
REGISTER_IN_MESSAGE(In_workspaceDidChangeWorkspaceFolders);
struct Handler_workspaceDidChangeWorkspaceFolders
: BaseMessageHandler<In_workspaceDidChangeWorkspaceFolders> {
MethodType GetMethodType() const override {
return didChangeWorkspaceFolders;
}
void Run(In_workspaceDidChangeWorkspaceFolders *request) override {
const auto &event = request->params.event;
for (const lsWorkspaceFolder &wf : event.removed) {
std::string root = wf.uri.GetPath();
EnsureEndsInSlash(root);
LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root;
auto it = llvm::find(g_config->workspaceFolders, root);
if (it != g_config->workspaceFolders.end()) {
g_config->workspaceFolders.erase(it);
{
// auto &folder = project->root2folder[path];
// FIXME delete
}
project->root2folder.erase(root);
}
}
for (const lsWorkspaceFolder &wf : event.added) {
std::string root = wf.uri.GetPath();
EnsureEndsInSlash(root);
LOG_S(INFO) << "add workspace folder " << wf.name << ": " << root;
g_config->workspaceFolders.push_back(root);
project->Load(root);
}
project->Index(working_files, lsRequestId());
clang_complete->FlushAllSessions();
}
};
REGISTER_MESSAGE_HANDLER(Handler_workspaceDidChangeWorkspaceFolders);
} // namespace

View File

@ -1,127 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "fuzzy_match.h"
#include "message_handler.h"
#include "pipeline.hh"
#include "query_utils.h"
#include <algorithm>
#include <ctype.h>
#include <functional>
#include <limits.h>
using namespace ccls;
namespace {
MethodType kMethodType = "workspace/symbol";
// Lookup |symbol| in |db| and insert the value into |result|.
bool AddSymbol(
DB *db, WorkingFiles *working_files, SymbolIdx sym, bool use_detailed,
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> *result) {
std::optional<lsSymbolInformation> info = GetSymbolInfo(db, sym, true);
if (!info)
return false;
Use loc;
if (Maybe<DeclRef> dr = GetDefinitionSpell(db, sym))
loc = *dr;
else {
auto decls = GetNonDefDeclarations(db, sym);
if (decls.empty())
return false;
loc = decls[0];
}
std::optional<lsLocation> ls_location = GetLsLocation(db, working_files, loc);
if (!ls_location)
return false;
info->location = *ls_location;
result->emplace_back(*info, int(use_detailed), sym);
return true;
}
struct In_WorkspaceSymbol : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
std::string query;
};
Params params;
};
MAKE_REFLECT_STRUCT(In_WorkspaceSymbol::Params, query);
MAKE_REFLECT_STRUCT(In_WorkspaceSymbol, id, params);
REGISTER_IN_MESSAGE(In_WorkspaceSymbol);
struct Handler_WorkspaceSymbol : BaseMessageHandler<In_WorkspaceSymbol> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_WorkspaceSymbol *request) override {
std::vector<lsSymbolInformation> result;
std::string query = request->params.query;
// {symbol info, matching detailed_name or short_name, index}
std::vector<std::tuple<lsSymbolInformation, int, SymbolIdx>> cands;
bool sensitive = g_config->workspaceSymbol.caseSensitivity;
// Find subsequence matches.
std::string query_without_space;
query_without_space.reserve(query.size());
for (char c : query)
if (!isspace(c))
query_without_space += c;
auto Add = [&](SymbolIdx sym) {
std::string_view detailed_name = db->GetSymbolName(sym, true);
int pos =
ReverseSubseqMatch(query_without_space, detailed_name, sensitive);
return pos >= 0 &&
AddSymbol(db, working_files, sym,
detailed_name.find(':', pos) != std::string::npos,
&cands) &&
cands.size() >= g_config->workspaceSymbol.maxNum;
};
for (auto &func : db->funcs)
if (Add({func.usr, SymbolKind::Func}))
goto done_add;
for (auto &type : db->types)
if (Add({type.usr, SymbolKind::Type}))
goto done_add;
for (auto &var : db->vars)
if (var.def.size() && !var.def[0].is_local() &&
Add({var.usr, SymbolKind::Var}))
goto done_add;
done_add:
if (g_config->workspaceSymbol.sort &&
query.size() <= FuzzyMatcher::kMaxPat) {
// Sort results with a fuzzy matching algorithm.
int longest = 0;
for (auto &cand : cands)
longest = std::max(
longest, int(db->GetSymbolName(std::get<2>(cand), true).size()));
FuzzyMatcher fuzzy(query, g_config->workspaceSymbol.caseSensitivity);
for (auto &cand : cands)
std::get<1>(cand) = fuzzy.Match(
db->GetSymbolName(std::get<2>(cand), std::get<1>(cand)));
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
return std::get<1>(l) > std::get<1>(r);
});
result.reserve(cands.size());
for (auto &cand : cands) {
// Discard awful candidates.
if (std::get<1>(cand) <= FuzzyMatcher::kMinScore)
break;
result.push_back(std::get<0>(cand));
}
} else {
result.reserve(cands.size());
for (auto &cand : cands)
result.push_back(std::get<0>(cand));
}
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceSymbol);
} // namespace

View File

@ -7,15 +7,16 @@
#include "config.h"
#include "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

View File

@ -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,

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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);

View 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

View File

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

View File

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

View File

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

View File

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

View File

@ -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; }
}

View File

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

View File

@ -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);
}
}

View File

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