Add pipeline::{Notify,Reply,ReplyError} and simplify message handling

Delete method.{cc,h}
Rename $ccls/setSkippedRanges to $ccls/publishSkippedRanges
Rename $ccls/publishSemanticHighlighting to $ccls/publishSemanticHighlight; stableId -> id
This commit is contained in:
Fangrui Song 2019-01-09 15:19:17 +08:00
parent 79352b451c
commit fc1db06538
48 changed files with 716 additions and 1157 deletions

View File

@ -187,7 +187,6 @@ target_sources(ccls PRIVATE
src/main.cc
src/include_complete.cc
src/indexer.cc
src/method.cc
src/log.cc
src/lsp.cc
src/match.cc
@ -225,12 +224,10 @@ target_sources(ccls PRIVATE
src/messages/textDocument_documentHighlight.cc
src/messages/textDocument_documentSymbol.cc
src/messages/textDocument_hover.cc
src/messages/textDocument_implementation.cc
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_didChangeWatchedFiles.cc
src/messages/workspace_symbol.cc
)

View File

@ -17,8 +17,8 @@ limitations under the License.
#include "clang_tu.hh"
#include "lru_cache.h"
#include "lsp.h"
#include "lsp_completion.h"
#include "lsp_diagnostic.h"
#include "project.h"
#include "threaded_queue.h"
#include "working_files.h"

View File

@ -21,19 +21,22 @@ limitations under the License.
#include <queue>
template <typename Node>
void FlattenHierarchy(const Node &root, Out_LocationList &out) {
std::vector<lsLocation> FlattenHierarchy(const std::optional<Node> &root) {
if (!root)
return {};
std::vector<lsLocation> ret;
std::queue<const Node *> q;
for (auto &entry : root.children)
for (auto &entry : root->children)
q.push(&entry);
while (q.size()) {
auto *entry = q.front();
q.pop();
if (entry->location.uri.raw_uri.size())
out.result.push_back({entry->location});
ret.push_back({entry->location});
for (auto &entry1 : entry->children)
q.push(&entry1);
}
std::sort(out.result.begin(), out.result.end());
out.result.erase(std::unique(out.result.begin(), out.result.end()),
out.result.end());
std::sort(ret.begin(), ret.end());
ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
return ret;
}

View File

@ -16,7 +16,6 @@ limitations under the License.
#pragma once
#include "lsp.h"
#include "lsp_diagnostic.h"
#include "maybe.h"
#include "position.h"
#include "serializer.h"

View File

@ -18,10 +18,45 @@ limitations under the License.
#include "log.hh"
#include "serializers/json.h"
#include <rapidjson/writer.h>
#include <rapidjson/document.h>
#include <stdio.h>
MethodType kMethodType_Exit = "exit";
void Reflect(Reader &visitor, lsRequestId &value) {
if (visitor.IsInt64()) {
value.type = lsRequestId::kInt;
value.value = int(visitor.GetInt64());
} else if (visitor.IsInt()) {
value.type = lsRequestId::kInt;
value.value = visitor.GetInt();
} else if (visitor.IsString()) {
value.type = lsRequestId::kString;
value.value = atoll(visitor.GetString());
} else {
value.type = lsRequestId::kNone;
value.value = -1;
}
}
void Reflect(Writer &visitor, lsRequestId &value) {
switch (value.type) {
case lsRequestId::kNone:
visitor.Null();
break;
case lsRequestId::kInt:
visitor.Int(value.value);
break;
case lsRequestId::kString:
auto s = std::to_string(value.value);
visitor.String(s.c_str(), s.length());
break;
}
}
InMessage::~InMessage() {}
MessageRegistry *MessageRegistry::instance_ = nullptr;
lsTextDocumentIdentifier
@ -145,29 +180,6 @@ MessageRegistry *MessageRegistry::instance() {
return instance_;
}
lsBaseOutMessage::~lsBaseOutMessage() = default;
void lsBaseOutMessage::Write(std::ostream &out) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
JsonWriter json_writer{&writer};
ReflectWriter(json_writer);
out << "Content-Length: " << output.GetSize() << "\r\n\r\n"
<< output.GetString();
out.flush();
}
void lsResponseError::Write(Writer &visitor) {
auto &value = *this;
int code2 = static_cast<int>(this->code);
visitor.StartObject();
REFLECT_MEMBER2("code", code2);
REFLECT_MEMBER(message);
visitor.EndObject();
}
lsDocumentUri lsDocumentUri::FromPath(const std::string &path) {
lsDocumentUri result;
result.SetPath(path);
@ -272,9 +284,3 @@ void Reflect(Writer &visitor, lsMarkedString &value) {
Reflect(visitor, value.value);
}
}
std::string Out_ShowLogMessage::method() {
if (display_type == DisplayType::Log)
return "window/logMessage";
return "window/showMessage";
}

156
src/lsp.h
View File

@ -16,13 +16,42 @@ limitations under the License.
#pragma once
#include "config.h"
#include "method.h"
#include "serializer.h"
#include "utils.h"
#include <iosfwd>
#include <unordered_map>
using MethodType = const char *;
extern MethodType kMethodType_Exit;
struct lsRequestId {
// The client can send the request id as an int or a string. We should output
// the same format we received.
enum Type { kNone, kInt, kString };
Type type = kNone;
int value = -1;
bool Valid() const { return type != kNone; }
};
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_;
@ -53,45 +82,38 @@ template <typename T> struct MessageRegistryRegister {
}
};
struct lsBaseOutMessage {
virtual ~lsBaseOutMessage();
virtual void ReflectWriter(Writer &) = 0;
enum class lsErrorCodes {
// Defined by JSON RPC
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
// Send the message to the language client by writing it to stdout.
void Write(std::ostream &out);
};
template <typename TDerived> struct lsOutMessage : lsBaseOutMessage {
// All derived types need to reflect on the |jsonrpc| member.
std::string jsonrpc = "2.0";
void ReflectWriter(Writer &writer) override {
Reflect(writer, static_cast<TDerived &>(*this));
}
// Defined by the protocol.
RequestCancelled = -32800,
};
MAKE_REFLECT_TYPE_PROXY(lsErrorCodes);
struct lsResponseError {
enum class lsErrorCodes : int {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
RequestCancelled = -32800,
};
// A number indicating the error type that occurred.
lsErrorCodes code;
// Short description.
// A string providing a short description of the error.
std::string message;
void Write(Writer &visitor);
// A Primitive or Structured value that contains additional
// information about the error. Can be omitted.
// std::optional<D> data;
};
MAKE_REFLECT_STRUCT(lsResponseError, code, message);
constexpr std::string_view ccls_xref("ccls.xref");
constexpr char ccls_xref[] = "ccls.xref";
constexpr char window_showMessage[] = "window/showMessage";
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
@ -317,35 +339,55 @@ MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name);
enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
MAKE_REFLECT_TYPE_PROXY(lsMessageType)
struct Out_ShowLogMessageParams {
enum class lsDiagnosticSeverity {
// Reports an error.
Error = 1,
// Reports a warning.
Warning = 2,
// Reports an information.
Information = 3,
// Reports a hint.
Hint = 4
};
MAKE_REFLECT_TYPE_PROXY(lsDiagnosticSeverity);
struct lsDiagnostic {
// The range at which the message applies.
lsRange range;
// The diagnostic's severity. Can be omitted. If omitted it is up to the
// client to interpret diagnostics as error, warning, info or hint.
std::optional<lsDiagnosticSeverity> severity;
// The diagnostic's code. Can be omitted.
int code = 0;
// A human-readable string describing the source of this
// diagnostic, e.g. 'typescript' or 'super lint'.
std::string source = "ccls";
// The diagnostic's message.
std::string message;
// Non-serialized set of fixits.
std::vector<lsTextEdit> fixits_;
};
MAKE_REFLECT_STRUCT(lsDiagnostic, range, severity, source, message);
struct lsPublishDiagnosticsParams {
// The URI for which diagnostic information is reported.
lsDocumentUri uri;
// An array of diagnostic information items.
std::vector<lsDiagnostic> diagnostics;
};
MAKE_REFLECT_STRUCT(lsPublishDiagnosticsParams, uri, diagnostics);
struct lsShowMessageParams {
lsMessageType type = lsMessageType::Error;
std::string message;
};
MAKE_REFLECT_STRUCT(Out_ShowLogMessageParams, type, message);
struct Out_ShowLogMessage : public lsOutMessage<Out_ShowLogMessage> {
enum class DisplayType { Show, Log };
DisplayType display_type = DisplayType::Show;
std::string method();
Out_ShowLogMessageParams params;
};
template <typename TVisitor>
void Reflect(TVisitor &visitor, Out_ShowLogMessage &value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER(jsonrpc);
std::string method = value.method();
REFLECT_MEMBER2("method", method);
REFLECT_MEMBER(params);
REFLECT_MEMBER_END();
}
struct Out_LocationList : public lsOutMessage<Out_LocationList> {
lsRequestId id;
std::vector<lsLocation> result;
};
MAKE_REFLECT_STRUCT(Out_LocationList, jsonrpc, id, result);
MAKE_REFLECT_STRUCT(lsShowMessageParams, type, message);
// Used to identify the language at a file level. The ordering is important, as
// a file previously identified as `C`, will be changed to `Cpp` if it

View File

@ -1,115 +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.
==============================================================================*/
#pragma once
#include "lsp.h"
enum class lsDiagnosticSeverity {
// Reports an error.
Error = 1,
// Reports a warning.
Warning = 2,
// Reports an information.
Information = 3,
// Reports a hint.
Hint = 4
};
MAKE_REFLECT_TYPE_PROXY(lsDiagnosticSeverity);
struct lsDiagnostic {
// The range at which the message applies.
lsRange range;
// The diagnostic's severity. Can be omitted. If omitted it is up to the
// client to interpret diagnostics as error, warning, info or hint.
std::optional<lsDiagnosticSeverity> severity;
// The diagnostic's code. Can be omitted.
int code = 0;
// A human-readable string describing the source of this
// diagnostic, e.g. 'typescript' or 'super lint'.
std::string source = "ccls";
// The diagnostic's message.
std::string message;
// Non-serialized set of fixits.
std::vector<lsTextEdit> fixits_;
};
MAKE_REFLECT_STRUCT(lsDiagnostic, range, severity, source, message);
enum class lsErrorCodes {
// Defined by JSON RPC
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
// Defined by the protocol.
RequestCancelled = -32800,
};
MAKE_REFLECT_TYPE_PROXY(lsErrorCodes);
struct Out_Error : public lsOutMessage<Out_Error> {
struct lsResponseError {
// A number indicating the error type that occurred.
lsErrorCodes code;
// A string providing a short description of the error.
std::string message;
// A Primitive or Structured value that contains additional
// information about the error. Can be omitted.
// std::optional<D> data;
};
lsRequestId id;
// The error object in case a request fails.
lsResponseError error;
};
MAKE_REFLECT_STRUCT(Out_Error::lsResponseError, code, message);
MAKE_REFLECT_STRUCT(Out_Error, jsonrpc, id, error);
// Diagnostics
struct Out_TextDocumentPublishDiagnostics
: public lsOutMessage<Out_TextDocumentPublishDiagnostics> {
struct Params {
// The URI for which diagnostic information is reported.
lsDocumentUri uri;
// An array of diagnostic information items.
std::vector<lsDiagnostic> diagnostics;
};
Params params;
};
template <typename TVisitor>
void Reflect(TVisitor &visitor, Out_TextDocumentPublishDiagnostics &value) {
std::string method = "textDocument/publishDiagnostics";
REFLECT_MEMBER_START();
REFLECT_MEMBER(jsonrpc);
REFLECT_MEMBER2("method", method);
REFLECT_MEMBER(params);
REFLECT_MEMBER_END();
}
MAKE_REFLECT_STRUCT(Out_TextDocumentPublishDiagnostics::Params, uri,
diagnostics);

View File

@ -30,12 +30,11 @@ std::optional<Matcher> Matcher::Create(const std::string &search) {
);
return m;
} catch (const std::exception &e) {
Out_ShowLogMessage out;
out.display_type = Out_ShowLogMessage::DisplayType::Show;
out.params.type = lsMessageType::Error;
out.params.message =
"ccls: Parsing EMCAScript regex \"" + search + "\" failed; " + e.what();
pipeline::WriteStdout(kMethodType_Unknown, out);
lsShowMessageParams params;
params.type = lsMessageType::Error;
params.message =
"failed to parse EMCAScript regex " + search + " : " + e.what();
pipeline::Notify(window_showMessage, params);
return std::nullopt;
}
}

View File

@ -30,23 +30,36 @@ MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);
namespace {
struct Out_CclsSetSkippedRanges
: public lsOutMessage<Out_CclsSetSkippedRanges> {
struct Params {
lsDocumentUri uri;
std::vector<lsRange> skippedRanges;
};
std::string method = "$ccls/setSkippedRanges";
Params params;
struct CclsSemanticHighlightSymbol {
int id = 0;
lsSymbolKind parentKind;
lsSymbolKind kind;
uint8_t storage;
std::vector<std::pair<int, int>> ranges;
// `lsRanges` is used to compute `ranges`.
std::vector<lsRange> lsRanges;
};
MAKE_REFLECT_STRUCT(Out_CclsSetSkippedRanges::Params, uri, skippedRanges);
MAKE_REFLECT_STRUCT(Out_CclsSetSkippedRanges, jsonrpc, method, params);
struct CclsSemanticHighlightParams {
lsDocumentUri uri;
std::vector<CclsSemanticHighlightSymbol> symbols;
};
MAKE_REFLECT_STRUCT(CclsSemanticHighlightSymbol, id, parentKind, kind, storage,
ranges, lsRanges);
MAKE_REFLECT_STRUCT(CclsSemanticHighlightParams, uri, symbols);
struct CclsSetSkippedRangesParams {
lsDocumentUri uri;
std::vector<lsRange> skippedRanges;
};
MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges);
struct ScanLineEvent {
lsPosition pos;
lsPosition end_pos; // Second key when there is a tie for insertion events.
int id;
Out_CclsPublishSemanticHighlighting::Symbol *symbol;
CclsSemanticHighlightSymbol *symbol;
bool operator<(const ScanLineEvent &other) const {
// See the comments below when insertion/deletion events are inserted.
if (!(pos == other.pos))
@ -68,7 +81,6 @@ MessageHandler::MessageHandler() {
message_handlers->push_back(this);
}
// static
std::vector<MessageHandler *> *MessageHandler::message_handlers = nullptr;
bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id,
@ -98,46 +110,40 @@ bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id,
}
if (id) {
Out_Error out;
out.id = *id;
lsResponseError err;
if (has_entry) {
out.error.code = lsErrorCodes::ServerNotInitialized;
out.error.message = absolute_path + " is being indexed";
err.code = lsErrorCodes::ServerNotInitialized;
err.message = absolute_path + " is being indexed";
} else {
out.error.code = lsErrorCodes::InternalError;
out.error.message = "Unable to find file " + absolute_path;
err.code = lsErrorCodes::InternalError;
err.message = "unable to find " + absolute_path;
}
LOG_S(INFO) << out.error.message;
pipeline::WriteStdout(kMethodType_Unknown, out);
pipeline::ReplyError(*id, err);
}
return false;
}
void EmitSkippedRanges(WorkingFile *working_file,
const std::vector<Range> &skipped_ranges) {
Out_CclsSetSkippedRanges out;
out.params.uri = lsDocumentUri::FromPath(working_file->filename);
for (Range skipped : skipped_ranges) {
std::optional<lsRange> ls_skipped = GetLsRange(working_file, skipped);
if (ls_skipped)
out.params.skippedRanges.push_back(*ls_skipped);
}
pipeline::WriteStdout(kMethodType_CclsPublishSkippedRanges, out);
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
CclsSetSkippedRangesParams params;
params.uri = lsDocumentUri::FromPath(wfile->filename);
for (Range skipped : file.def->skipped_ranges)
if (auto ls_skipped = GetLsRange(wfile, skipped))
params.skippedRanges.push_back(*ls_skipped);
pipeline::Notify("$ccls/publishSkippedRanges", params);
}
void EmitSemanticHighlighting(DB *db, WorkingFile *wfile, QueryFile *file) {
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
static GroupMatch match(g_config->highlight.whitelist,
g_config->highlight.blacklist);
assert(file->def);
assert(file.def);
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
!match.IsMatch(file->def->path))
!match.IsMatch(file.def->path))
return;
// Group symbols together.
std::unordered_map<SymbolIdx, Out_CclsPublishSemanticHighlighting::Symbol>
grouped_symbols;
for (auto [sym, refcnt] : file->symbol2refcnt) {
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
for (auto [sym, refcnt] : file.symbol2refcnt) {
if (refcnt <= 0) continue;
std::string_view detailed_name;
lsSymbolKind parent_kind = lsSymbolKind::Unknown;
@ -217,8 +223,8 @@ void EmitSemanticHighlighting(DB *db, WorkingFile *wfile, QueryFile *file) {
if (it != grouped_symbols.end()) {
it->second.lsRanges.push_back(*loc);
} else {
Out_CclsPublishSemanticHighlighting::Symbol symbol;
symbol.stableId = idx;
CclsSemanticHighlightSymbol symbol;
symbol.id = idx;
symbol.parentKind = parent_kind;
symbol.kind = kind;
symbol.storage = storage;
@ -232,7 +238,7 @@ void EmitSemanticHighlighting(DB *db, WorkingFile *wfile, QueryFile *file) {
std::vector<ScanLineEvent> events;
int id = 0;
for (auto &entry : grouped_symbols) {
Out_CclsPublishSemanticHighlighting::Symbol &symbol = entry.second;
CclsSemanticHighlightSymbol &symbol = entry.second;
for (auto &loc : symbol.lsRanges) {
// For ranges sharing the same start point, the one with leftmost end
// point comes first.
@ -267,13 +273,11 @@ void EmitSemanticHighlighting(DB *db, WorkingFile *wfile, QueryFile *file) {
deleted[~events[i].id] = 1;
}
Out_CclsPublishSemanticHighlighting out;
out.params.uri = lsDocumentUri::FromPath(wfile->filename);
CclsSemanticHighlightParams params;
params.uri = lsDocumentUri::FromPath(wfile->filename);
// Transform lsRange into pair<int, int> (offset pairs)
if (!g_config->highlight.lsRanges) {
std::vector<
std::pair<lsRange, Out_CclsPublishSemanticHighlighting::Symbol *>>
scratch;
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
for (auto &entry : grouped_symbols) {
for (auto &range : entry.second.lsRanges)
scratch.emplace_back(range, &entry.second);
@ -315,6 +319,6 @@ void EmitSemanticHighlighting(DB *db, WorkingFile *wfile, QueryFile *file) {
for (auto &entry : grouped_symbols)
if (entry.second.ranges.size() || entry.second.lsRanges.size())
out.params.symbols.push_back(std::move(entry.second));
pipeline::WriteStdout(kMethodType_CclsPublishSemanticHighlighting, out);
params.symbols.push_back(std::move(entry.second));
pipeline::Notify("$ccls/publishSemanticHighlight", params);
}

View File

@ -16,7 +16,6 @@ limitations under the License.
#pragma once
#include "lsp.h"
#include "method.h"
#include "query.h"
#include <memory>
@ -35,30 +34,6 @@ struct DB;
struct WorkingFile;
struct WorkingFiles;
struct Out_CclsPublishSemanticHighlighting
: public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
struct Symbol {
int stableId = 0;
lsSymbolKind parentKind;
lsSymbolKind kind;
uint8_t storage;
std::vector<std::pair<int, int>> ranges;
// `lsRanges` is used to compute `ranges`.
std::vector<lsRange> lsRanges;
};
struct Params {
lsDocumentUri uri;
std::vector<Symbol> symbols;
};
std::string method = "$ccls/publishSemanticHighlighting";
Params params;
};
MAKE_REFLECT_STRUCT(Out_CclsPublishSemanticHighlighting::Symbol, stableId,
parentKind, kind, storage, ranges, lsRanges);
MAKE_REFLECT_STRUCT(Out_CclsPublishSemanticHighlighting::Params, uri, symbols);
MAKE_REFLECT_STRUCT(Out_CclsPublishSemanticHighlighting, jsonrpc, method,
params);
// Usage:
//
@ -103,8 +78,6 @@ 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 *working_file,
const std::vector<Range> &skipped_ranges);
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file);
void EmitSemanticHighlighting(DB *db, WorkingFile *working_file,
QueryFile *file);
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);

View File

@ -38,7 +38,7 @@ bool operator&(CallType lhs, CallType rhs) {
return uint8_t(lhs) & uint8_t(rhs);
}
struct In_CclsCall : public RequestInMessage {
struct In_cclsCall : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
@ -62,34 +62,29 @@ struct In_CclsCall : public RequestInMessage {
};
Params params;
};
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(In_cclsCall::Params, textDocument, position, id, callee,
callType, qualified, levels, hierarchy);
MAKE_REFLECT_STRUCT(In_cclsCall, id, params);
REGISTER_IN_MESSAGE(In_cclsCall);
struct Out_CclsCall : public lsOutMessage<Out_CclsCall> {
struct Entry {
Usr usr;
std::string id;
std::string_view name;
lsLocation location;
CallType callType = CallType::Direct;
int numChildren;
// Empty if the |levels| limit is reached.
std::vector<Entry> children;
bool operator==(const Entry &o) const { return location == o.location; }
bool operator<(const Entry &o) const { return location < o.location; }
};
lsRequestId id;
std::optional<Entry> result;
struct Out_cclsCall {
Usr usr;
std::string id;
std::string_view name;
lsLocation location;
CallType callType = CallType::Direct;
int numChildren;
// Empty if the |levels| limit is reached.
std::vector<Out_cclsCall> children;
bool operator==(const Out_cclsCall &o) const {
return location == o.location;
}
bool operator<(const Out_cclsCall &o) const { return location < o.location; }
};
MAKE_REFLECT_STRUCT(Out_CclsCall::Entry, id, name, location, callType,
numChildren, children);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsCall, jsonrpc, id,
result);
MAKE_REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
children);
bool Expand(MessageHandler *m, Out_CclsCall::Entry *entry, bool callee,
bool Expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
CallType call_type, bool qualified, int levels) {
const QueryFunc &func = m->db->Func(entry->usr);
const QueryFunc::Def *def = func.AnyDef();
@ -99,7 +94,7 @@ bool Expand(MessageHandler *m, Out_CclsCall::Entry *entry, bool callee,
auto handle = [&](SymbolRef sym, int file_id, CallType call_type1) {
entry->numChildren++;
if (levels > 0) {
Out_CclsCall::Entry entry1;
Out_cclsCall entry1;
entry1.id = std::to_string(sym.usr);
entry1.usr = sym.usr;
if (auto loc = GetLsLocation(m->db, m->working_files,
@ -179,17 +174,17 @@ bool Expand(MessageHandler *m, Out_CclsCall::Entry *entry, bool callee,
return true;
}
struct Handler_CclsCall : BaseMessageHandler<In_CclsCall> {
struct Handler_cclsCall : BaseMessageHandler<In_cclsCall> {
MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsCall::Entry>
BuildInitial(Usr root_usr, bool callee, CallType call_type, bool qualified,
int levels) {
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;
Out_cclsCall entry;
entry.id = std::to_string(root_usr);
entry.usr = root_usr;
entry.callType = CallType::Direct;
@ -202,25 +197,22 @@ struct Handler_CclsCall : BaseMessageHandler<In_CclsCall> {
return entry;
}
void Run(In_CclsCall *request) override {
void Run(In_cclsCall *request) override {
auto &params = request->params;
Out_CclsCall out;
out.id = request->id;
std::optional<Out_cclsCall> result;
if (params.id.size()) {
try {
params.usr = std::stoull(params.id);
} catch (...) {
return;
}
Out_CclsCall::Entry entry;
entry.id = std::to_string(params.usr);
entry.usr = params.usr;
entry.callType = CallType::Direct;
result.emplace();
result->id = std::to_string(params.usr);
result->usr = params.usr;
result->callType = CallType::Direct;
if (db->HasFunc(params.usr))
Expand(this, &entry, params.callee, params.callType, params.qualified,
Expand(this, &*result, params.callee, params.callType, params.qualified,
params.levels);
out.result = std::move(entry);
} else {
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
@ -231,24 +223,21 @@ struct Handler_CclsCall : BaseMessageHandler<In_CclsCall> {
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, params.position)) {
if (sym.kind == SymbolKind::Func) {
out.result = BuildInitial(sym.usr, params.callee, params.callType,
params.qualified, params.levels);
result = BuildInitial(sym.usr, params.callee, params.callType,
params.qualified, params.levels);
break;
}
}
}
if (params.hierarchy) {
pipeline::WriteStdout(kMethodType, out);
return;
if (params.hierarchy)
pipeline::Reply(request->id, result);
else {
auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out);
}
Out_LocationList out1;
out1.id = request->id;
if (out.result)
FlattenHierarchy<Out_CclsCall::Entry>(*out.result, out1);
pipeline::WriteStdout(kMethodType, out1);
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsCall);
REGISTER_MESSAGE_HANDLER(Handler_cclsCall);
} // namespace

View File

@ -25,51 +25,46 @@ MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges,
namespace {
MethodType cclsInfo = "$ccls/info", fileInfo = "$ccls/fileInfo";
struct In_cclsInfo : public RequestInMessage {
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 : public lsOutMessage<Out_cclsInfo> {
lsRequestId id;
struct Result {
struct DB {
int files, funcs, types, vars;
} db;
struct Pipeline {
int pendingIndexRequests;
} pipeline;
struct Project {
int entries;
} project;
} result;
struct Out_cclsInfo {
struct DB {
int files, funcs, types, vars;
} db;
struct Pipeline {
int pendingIndexRequests;
} pipeline;
struct Project {
int entries;
} project;
};
MAKE_REFLECT_STRUCT(Out_cclsInfo::Result::DB, files, funcs, types, vars);
MAKE_REFLECT_STRUCT(Out_cclsInfo::Result::Pipeline, pendingIndexRequests);
MAKE_REFLECT_STRUCT(Out_cclsInfo::Result::Project, entries);
MAKE_REFLECT_STRUCT(Out_cclsInfo::Result, db, pipeline, project);
MAKE_REFLECT_STRUCT(Out_cclsInfo, jsonrpc, id, result);
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 out;
out.id = request->id;
out.result.db.files = db->files.size();
out.result.db.funcs = db->funcs.size();
out.result.db.types = db->types.size();
out.result.db.vars = db->vars.size();
out.result.pipeline.pendingIndexRequests = pipeline::pending_index_requests;
out.result.project.entries = 0;
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)
out.result.project.entries += folder.entries.size();
pipeline::WriteStdout(cclsInfo, out);
result.project.entries += folder.entries.size();
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_cclsInfo);
struct In_cclsFileInfo : public RequestInMessage {
struct In_cclsFileInfo : public RequestMessage {
MethodType GetMethodType() const override { return fileInfo; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -79,12 +74,6 @@ MAKE_REFLECT_STRUCT(In_cclsFileInfo::Params, textDocument);
MAKE_REFLECT_STRUCT(In_cclsFileInfo, id, params);
REGISTER_IN_MESSAGE(In_cclsFileInfo);
struct Out_cclsFileInfo : public lsOutMessage<Out_cclsFileInfo> {
lsRequestId id;
QueryFile::Def result;
};
MAKE_REFLECT_STRUCT(Out_cclsFileInfo, jsonrpc, id, result);
struct Handler_cclsFileInfo : BaseMessageHandler<In_cclsFileInfo> {
MethodType GetMethodType() const override { return fileInfo; }
void Run(In_cclsFileInfo *request) override {
@ -93,15 +82,14 @@ struct Handler_cclsFileInfo : BaseMessageHandler<In_cclsFileInfo> {
request->params.textDocument.uri.GetPath(), &file))
return;
Out_cclsFileInfo out;
out.id = request->id;
QueryFile::Def result;
// Expose some fields of |QueryFile::Def|.
out.result.path = file->def->path;
out.result.args = file->def->args;
out.result.language = file->def->language;
out.result.includes = file->def->includes;
out.result.skipped_ranges = file->def->skipped_ranges;
pipeline::WriteStdout(fileInfo, out);
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);

View File

@ -22,9 +22,10 @@ using namespace ccls;
#include <unordered_set>
namespace {
MethodType kMethodType = "$ccls/inheritance";
MethodType kMethodType = "$ccls/inheritance",
implementation = "textDocument/implementation";
struct In_CclsInheritance : public RequestInMessage {
struct In_cclsInheritance : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
// If id+kind are specified, expand a node; otherwise textDocument+position
@ -44,39 +45,32 @@ struct In_CclsInheritance : public RequestInMessage {
} params;
};
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(In_cclsInheritance::Params, textDocument, position, id,
kind, derived, qualified, levels, hierarchy);
MAKE_REFLECT_STRUCT(In_cclsInheritance, id, params);
REGISTER_IN_MESSAGE(In_cclsInheritance);
struct Out_CclsInheritance
: public lsOutMessage<Out_CclsInheritance> {
struct Entry {
Usr usr;
std::string id;
SymbolKind kind;
std::string_view name;
lsLocation location;
// For unexpanded nodes, this is an upper bound because some entities may be
// undefined. If it is 0, there are no members.
int numChildren;
// Empty if the |levels| limit is reached.
std::vector<Entry> children;
};
lsRequestId id;
std::optional<Entry> result;
struct Out_cclsInheritance {
Usr usr;
std::string id;
SymbolKind kind;
std::string_view name;
lsLocation location;
// For unexpanded nodes, this is an upper bound because some entities may be
// undefined. If it is 0, there are no members.
int numChildren;
// Empty if the |levels| limit is reached.
std::vector<Out_cclsInheritance> children;
};
MAKE_REFLECT_STRUCT(Out_CclsInheritance::Entry, id, kind, name,
location, numChildren, children);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsInheritance, jsonrpc,
id, result);
MAKE_REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
children);
bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry,
bool derived, bool qualified, int levels);
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels);
template <typename Q>
bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
bool derived, bool qualified, int levels, Q &entity) {
bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels, Q &entity) {
const auto *def = entity.AnyDef();
if (def) {
entry->name = def->Name(qualified);
@ -97,7 +91,7 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
for (auto usr : entity.derived) {
if (!seen.insert(usr).second)
continue;
Out_CclsInheritance::Entry entry1;
Out_cclsInheritance entry1;
entry1.id = std::to_string(usr);
entry1.usr = usr;
entry1.kind = entry->kind;
@ -112,7 +106,7 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
for (auto usr : def->bases) {
if (!seen.insert(usr).second)
continue;
Out_CclsInheritance::Entry entry1;
Out_cclsInheritance entry1;
entry1.id = std::to_string(usr);
entry1.usr = usr;
entry1.kind = entry->kind;
@ -126,8 +120,8 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
return true;
}
bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry,
bool derived, bool qualified, int levels) {
bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool qualified, int levels) {
if (entry->kind == SymbolKind::Func)
return ExpandHelper(m, entry, derived, qualified, levels,
m->db->Func(entry->usr));
@ -136,13 +130,12 @@ bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry,
m->db->Type(entry->usr));
}
struct Handler_CclsInheritance
: BaseMessageHandler<In_CclsInheritance> {
struct Handler_cclsInheritance : BaseMessageHandler<In_cclsInheritance> {
MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsInheritance::Entry>
BuildInitial(SymbolRef sym, bool derived, bool qualified, int levels) {
Out_CclsInheritance::Entry 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;
@ -150,25 +143,24 @@ struct Handler_CclsInheritance
return entry;
}
void Run(In_CclsInheritance *request) override {
void Run(In_cclsInheritance *request) override {
auto &params = request->params;
Out_CclsInheritance out;
out.id = request->id;
std::optional<Out_cclsInheritance> result;
if (params.id.size()) {
try {
params.usr = std::stoull(params.id);
} catch (...) {
return;
}
Out_CclsInheritance::Entry entry;
entry.id = std::to_string(params.usr);
entry.usr = params.usr;
entry.kind = params.kind;
if (((entry.kind == SymbolKind::Func && db->HasFunc(entry.usr)) ||
(entry.kind == SymbolKind::Type && db->HasType(entry.usr))) &&
Expand(this, &entry, params.derived, params.qualified, params.levels))
out.result = std::move(entry);
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,
@ -178,23 +170,38 @@ struct Handler_CclsInheritance
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position))
if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) {
out.result = BuildInitial(sym, params.derived, params.qualified,
params.levels);
result = BuildInitial(sym, params.derived, params.qualified,
params.levels);
break;
}
}
if (params.hierarchy) {
pipeline::WriteStdout(kMethodType, out);
return;
if (params.hierarchy)
pipeline::Reply(request->id, result);
else {
auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out);
}
Out_LocationList out1;
out1.id = request->id;
if (out.result)
FlattenHierarchy<Out_CclsInheritance::Entry>(*out.result, out1);
pipeline::WriteStdout(kMethodType, out1);
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsInheritance);
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 {
In_cclsInheritance request1;
request1.params.textDocument = request->params.textDocument;
request1.params.position = request->params.position;
Handler_cclsInheritance().Run(&request1);
}
};
REGISTER_MESSAGE_HANDLER(Handler_textDocumentImplementation);
} // namespace

View File

@ -30,7 +30,7 @@ using namespace clang;
namespace {
MethodType kMethodType = "$ccls/member";
struct In_CclsMember : public RequestInMessage {
struct In_cclsMember : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
@ -52,42 +52,36 @@ struct In_CclsMember : public RequestInMessage {
} params;
};
MAKE_REFLECT_STRUCT(In_CclsMember::Params, textDocument, position, id,
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(In_cclsMember, id, params);
REGISTER_IN_MESSAGE(In_cclsMember);
struct Out_CclsMember : public lsOutMessage<Out_CclsMember> {
struct Entry {
Usr usr;
std::string id;
std::string_view name;
std::string fieldName;
lsLocation location;
// For unexpanded nodes, this is an upper bound because some entities may be
// undefined. If it is 0, there are no members.
int numChildren = 0;
// Empty if the |levels| limit is reached.
std::vector<Entry> children;
};
lsRequestId id;
std::optional<Entry> result;
struct Out_cclsMember {
Usr usr;
std::string id;
std::string_view name;
std::string fieldName;
lsLocation location;
// For unexpanded nodes, this is an upper bound because some entities may be
// undefined. If it is 0, there are no members.
int numChildren = 0;
// Empty if the |levels| limit is reached.
std::vector<Out_cclsMember> children;
};
MAKE_REFLECT_STRUCT(Out_CclsMember::Entry, id, name, fieldName,
location, numChildren, children);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsMember, jsonrpc, id,
result);
MAKE_REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
children);
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
bool qualified, int levels, SymbolKind memberKind);
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, SymbolKind memberKind);
// Add a field to |entry| which is a Func/Type.
void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
const QueryVar &var, int64_t offset, bool qualified, int levels) {
void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
int64_t offset, bool qualified, int levels) {
const QueryVar::Def *def1 = var.AnyDef();
if (!def1)
return;
Out_CclsMember::Entry entry1;
Out_cclsMember entry1;
// With multiple inheritance, the offset is incorrect.
if (offset >= 0) {
if (offset / 8 < 10)
@ -124,8 +118,8 @@ void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
}
// Expand a type node by adding members recursively to it.
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
bool qualified, int levels, SymbolKind memberKind) {
bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
int levels, SymbolKind memberKind) {
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
entry->name = ClangBuiltinTypeName(int(entry->usr));
return true;
@ -157,7 +151,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
}
if (def->alias_of) {
const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef();
Out_CclsMember::Entry entry1;
Out_cclsMember entry1;
entry1.id = std::to_string(def->alias_of);
entry1.usr = def->alias_of;
if (def1 && def1->spell) {
@ -187,7 +181,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
if (seen1.insert(usr).second) {
QueryFunc &func1 = m->db->Func(usr);
if (const QueryFunc::Def *def1 = func1.AnyDef()) {
Out_CclsMember::Entry entry1;
Out_cclsMember entry1;
entry1.fieldName = def1->Name(false);
if (def1->spell) {
if (auto loc =
@ -208,7 +202,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
if (seen1.insert(usr).second) {
QueryType &type1 = m->db->Type(usr);
if (const QueryType::Def *def1 = type1.AnyDef()) {
Out_CclsMember::Entry entry1;
Out_cclsMember entry1;
entry1.fieldName = def1->Name(false);
if (def1->spell) {
if (auto loc =
@ -239,12 +233,12 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
return true;
}
struct Handler_CclsMember
: BaseMessageHandler<In_CclsMember> {
struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsMember::Entry>
BuildInitial(SymbolKind kind, Usr root_usr, bool qualified, int levels, SymbolKind memberKind) {
std::optional<Out_cclsMember> BuildInitial(SymbolKind kind, Usr root_usr,
bool qualified, int levels,
SymbolKind memberKind) {
switch (kind) {
default:
return {};
@ -253,7 +247,7 @@ struct Handler_CclsMember
if (!def)
return {};
Out_CclsMember::Entry entry;
Out_cclsMember entry;
// Not type, |id| is invalid.
entry.name = def->Name(qualified);
if (def->spell) {
@ -273,7 +267,7 @@ struct Handler_CclsMember
if (!def)
return {};
Out_CclsMember::Entry entry;
Out_cclsMember entry;
entry.id = std::to_string(root_usr);
entry.usr = root_usr;
if (def->spell) {
@ -287,24 +281,22 @@ struct Handler_CclsMember
}
}
void Run(In_CclsMember *request) override {
void Run(In_cclsMember *request) override {
auto &params = request->params;
Out_CclsMember out;
out.id = request->id;
std::optional<Out_cclsMember> result;
if (params.id.size()) {
try {
params.usr = std::stoull(params.id);
} catch (...) {
return;
}
Out_CclsMember::Entry entry;
entry.id = std::to_string(params.usr);
entry.usr = params.usr;
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(entry.usr) &&
Expand(this, &entry, params.qualified, params.levels, params.kind))
out.result = std::move(entry);
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,
@ -316,14 +308,14 @@ struct Handler_CclsMember
switch (sym.kind) {
case SymbolKind::Func:
case SymbolKind::Type:
out.result = BuildInitial(sym.kind, sym.usr, params.qualified,
params.levels, params.kind);
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)
out.result = BuildInitial(SymbolKind::Type, def->type,
params.qualified, params.levels, params.kind);
result = BuildInitial(SymbolKind::Type, def->type, params.qualified,
params.levels, params.kind);
break;
}
default:
@ -333,17 +325,14 @@ struct Handler_CclsMember
}
}
if (params.hierarchy) {
pipeline::WriteStdout(kMethodType, out);
return;
if (params.hierarchy)
pipeline::Reply(request->id, result);
else {
auto out = FlattenHierarchy(result);
pipeline::Reply(request->id, out);
}
Out_LocationList out1;
out1.id = request->id;
if (out.result)
FlattenHierarchy<Out_CclsMember::Entry>(*out.result, out1);
pipeline::WriteStdout(kMethodType, out1);
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsMember);
REGISTER_MESSAGE_HANDLER(Handler_cclsMember);
} // namespace

View File

@ -21,7 +21,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "$ccls/navigate";
struct In_CclsNavigate : public RequestInMessage {
struct In_CclsNavigate : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -100,15 +100,14 @@ struct Handler_CclsNavigate : BaseMessageHandler<In_CclsNavigate> {
res = sym.range;
break;
}
Out_LocationList out;
out.id = request->id;
std::vector<lsLocation> result;
if (res)
if (auto ls_range = GetLsRange(wfile, *res)) {
lsLocation &ls_loc = out.result.emplace_back();
lsLocation &ls_loc = result.emplace_back();
ls_loc.uri = params.textDocument.uri;
ls_loc.range = *ls_range;
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsNavigate);

View File

@ -28,7 +28,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "$ccls/reload";
struct In_CclsReload : public NotificationInMessage {
struct In_cclsReload : public NotificationMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
bool dependencies = true;
@ -36,14 +36,13 @@ struct In_CclsReload : public NotificationInMessage {
std::vector<std::string> blacklist;
} params;
};
MAKE_REFLECT_STRUCT(In_CclsReload::Params, dependencies, whitelist,
blacklist);
MAKE_REFLECT_STRUCT(In_CclsReload, params);
REGISTER_IN_MESSAGE(In_CclsReload);
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> {
struct Handler_cclsReload : BaseMessageHandler<In_cclsReload> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_CclsReload *request) override {
void Run(In_cclsReload *request) override {
const auto &params = request->params;
// Send index requests for every file.
if (params.whitelist.empty() && params.blacklist.empty()) {
@ -91,5 +90,5 @@ struct Handler_CclsReload : BaseMessageHandler<In_CclsReload> {
}
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsReload);
REGISTER_MESSAGE_HANDLER(Handler_cclsReload);
} // namespace

View File

@ -21,7 +21,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "$ccls/vars";
struct In_CclsVars : public RequestInMessage {
struct In_cclsVars : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params : lsTextDocumentPositionParams {
// 1: field
@ -30,14 +30,14 @@ struct In_CclsVars : public RequestInMessage {
unsigned kind = ~0u;
} params;
};
MAKE_REFLECT_STRUCT(In_CclsVars::Params, textDocument, position, kind);
MAKE_REFLECT_STRUCT(In_CclsVars, id, params);
REGISTER_IN_MESSAGE(In_CclsVars);
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> {
struct Handler_cclsVars : BaseMessageHandler<In_cclsVars> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_CclsVars *request) override {
void Run(In_cclsVars *request) override {
auto &params = request->params;
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
@ -47,8 +47,7 @@ struct Handler_CclsVars : BaseMessageHandler<In_CclsVars> {
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_LocationList out;
out.id = request->id;
std::vector<lsLocation> result;
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, params.position)) {
Usr usr = sym.usr;
@ -63,14 +62,14 @@ struct Handler_CclsVars : BaseMessageHandler<In_CclsVars> {
[[fallthrough]];
}
case SymbolKind::Type:
out.result = GetLsLocations(
result = GetLsLocations(
db, working_files,
GetVarDeclarations(db, db->Type(usr).instances, params.kind));
break;
}
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsVars);
REGISTER_MESSAGE_HANDLER(Handler_cclsVars);
} // namespace

View File

@ -16,7 +16,7 @@ limitations under the License.
#include "message_handler.h"
namespace {
struct In_Exit : public NotificationInMessage {
struct In_Exit : public NotificationMessage {
MethodType GetMethodType() const override { return kMethodType_Exit; }
};
MAKE_REFLECT_EMPTY_STRUCT(In_Exit);

View File

@ -400,7 +400,7 @@ struct lsInitializeError {
};
MAKE_REFLECT_STRUCT(lsInitializeError, retry);
struct In_InitializeRequest : public RequestInMessage {
struct In_InitializeRequest : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsInitializeParams params;
@ -408,15 +408,10 @@ struct In_InitializeRequest : public RequestInMessage {
MAKE_REFLECT_STRUCT(In_InitializeRequest, id, params);
REGISTER_IN_MESSAGE(In_InitializeRequest);
struct Out_InitializeResponse : public lsOutMessage<Out_InitializeResponse> {
struct InitializeResult {
lsServerCapabilities capabilities;
};
lsRequestId id;
InitializeResult result;
struct lsInitializeResult {
lsServerCapabilities capabilities;
};
MAKE_REFLECT_STRUCT(Out_InitializeResponse::InitializeResult, capabilities);
MAKE_REFLECT_STRUCT(Out_InitializeResponse, jsonrpc, id, result);
MAKE_REFLECT_STRUCT(lsInitializeResult, capabilities);
void *Indexer(void *arg_) {
MessageHandler *h;
@ -486,13 +481,10 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
// Send initialization before starting indexers, so we don't send a
// status update too early.
// TODO: query request->params.capabilities.textDocument and support
// only things the client supports.
Out_InitializeResponse out;
out.id = request->id;
pipeline::WriteStdout(kMethodType, out);
{
lsInitializeResult result;
pipeline::Reply(request->id, result);
}
// Set project root.
EnsureEndsInSlash(project_path);

View File

@ -20,24 +20,17 @@ using namespace ccls;
namespace {
MethodType kMethodType = "shutdown";
struct In_Shutdown : public RequestInMessage {
struct In_Shutdown : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
};
MAKE_REFLECT_STRUCT(In_Shutdown, id);
REGISTER_IN_MESSAGE(In_Shutdown);
struct Out_Shutdown : public lsOutMessage<Out_Shutdown> {
lsRequestId id;
JsonNull result;
};
MAKE_REFLECT_STRUCT(Out_Shutdown, jsonrpc, id, result);
struct Handler_Shutdown : BaseMessageHandler<In_Shutdown> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_Shutdown *request) override {
Out_Shutdown out;
out.id = request->id;
pipeline::WriteStdout(kMethodType, out);
JsonNull result;
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_Shutdown);

View File

@ -21,7 +21,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/codeAction";
struct In_TextDocumentCodeAction : public RequestInMessage {
struct In_TextDocumentCodeAction : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
// Contains additional diagnostic information about the context in which
@ -54,13 +54,6 @@ struct lsCodeAction {
};
MAKE_REFLECT_STRUCT(lsCodeAction, title, kind, edit);
struct Out_TextDocumentCodeAction
: public lsOutMessage<Out_TextDocumentCodeAction> {
lsRequestId id;
std::vector<lsCodeAction> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeAction, jsonrpc, id, result);
struct Handler_TextDocumentCodeAction
: BaseMessageHandler<In_TextDocumentCodeAction> {
MethodType GetMethodType() const override { return kMethodType; }
@ -71,20 +64,19 @@ struct Handler_TextDocumentCodeAction
working_files->GetFileByFilename(params.textDocument.uri.GetPath());
if (!wfile)
return;
Out_TextDocumentCodeAction out;
out.id = request->id;
std::vector<lsCodeAction> result;
std::vector<lsDiagnostic> diagnostics;
working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; });
for (lsDiagnostic &diag : diagnostics)
if (diag.fixits_.size()) {
lsCodeAction &cmd = out.result.emplace_back();
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::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeAction);

View File

@ -48,12 +48,6 @@ struct Cmd_xref {
};
MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field);
struct Out_xref : public lsOutMessage<Out_xref> {
lsRequestId id;
std::vector<lsLocation> result;
};
MAKE_REFLECT_STRUCT(Out_xref, jsonrpc, id, result);
template <typename T>
std::string ToString(T &v) {
rapidjson::StringBuffer output;
@ -69,7 +63,7 @@ struct CommonCodeLensParams {
WorkingFile *wfile;
};
struct In_TextDocumentCodeLens : public RequestInMessage {
struct In_TextDocumentCodeLens : public RequestMessage {
MethodType GetMethodType() const override { return codeLens; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -79,20 +73,12 @@ MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens::Params, textDocument);
MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentCodeLens);
struct Out_TextDocumentCodeLens
: public lsOutMessage<Out_TextDocumentCodeLens> {
lsRequestId id;
std::vector<lsCodeLens> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeLens, jsonrpc, id, result);
struct Handler_TextDocumentCodeLens
: BaseMessageHandler<In_TextDocumentCodeLens> {
MethodType GetMethodType() const override { return codeLens; }
void Run(In_TextDocumentCodeLens *request) override {
auto &params = request->params;
Out_TextDocumentCodeLens out;
out.id = request->id;
std::vector<lsCodeLens> result;
std::string path = params.textDocument.uri.GetPath();
QueryFile *file;
@ -107,7 +93,7 @@ struct Handler_TextDocumentCodeLens
std::optional<lsRange> range = GetLsRange(wfile, use.range);
if (!range)
return;
lsCodeLens &code_lens = out.result.emplace_back();
lsCodeLens &code_lens = result.emplace_back();
code_lens.range = *range;
code_lens.command = lsCommand();
code_lens.command->command = std::string(ccls_xref);
@ -178,12 +164,12 @@ struct Handler_TextDocumentCodeLens
};
}
pipeline::WriteStdout(codeLens, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens);
struct In_WorkspaceExecuteCommand : public RequestInMessage {
struct In_WorkspaceExecuteCommand : public RequestMessage {
MethodType GetMethodType() const override { return executeCommand; }
lsCommand params;
};
@ -203,12 +189,11 @@ struct Handler_WorkspaceExecuteCommand
if (params.command == ccls_xref) {
Cmd_xref cmd;
Reflect(json_reader, cmd);
Out_xref out;
out.id = request->id;
std::vector<lsLocation> result;
auto Map = [&](auto &&uses) {
for (auto &use : uses)
if (auto loc = GetLsLocation(db, working_files, use))
out.result.push_back(std::move(*loc));
result.push_back(std::move(*loc));
};
switch (cmd.kind) {
case SymbolKind::Func: {
@ -247,7 +232,7 @@ struct Handler_WorkspaceExecuteCommand
default:
break;
}
pipeline::WriteStdout(executeCommand, out);
pipeline::Reply(request->id, result);
}
}
};

View File

@ -66,28 +66,21 @@ struct lsCompletionParams : lsTextDocumentPositionParams {
};
MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context);
struct In_TextDocumentComplete : public RequestInMessage {
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 lsTextDocumentCompleteResult {
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_STRUCT(lsTextDocumentCompleteResult, isIncomplete, items);
struct Out_TextDocumentComplete
: public lsOutMessage<Out_TextDocumentComplete> {
lsRequestId id;
lsTextDocumentCompleteResult result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentComplete, jsonrpc, id, result);
MAKE_REFLECT_STRUCT(lsCompletionList, isIncomplete, items);
void DecorateIncludePaths(const std::smatch &match,
std::vector<lsCompletionItem> *items) {
@ -153,11 +146,11 @@ template <typename T> char *tofixedbase64(T input, char *out) {
// Pre-filters completion responses before sending to vscode. This results in a
// significantly snappier completion experience as vscode is easily overloaded
// when given 1000+ completion items.
void FilterCandidates(Out_TextDocumentComplete *complete_response,
void FilterCandidates(lsCompletionList &result,
const std::string &complete_text, lsPosition begin_pos,
lsPosition end_pos, const std::string &buffer_line) {
assert(begin_pos.line == end_pos.line);
auto &items = complete_response->result.items;
auto &items = result.items;
// People usually does not want to insert snippets or parenthesis when
// changing function or type names, e.g. "str.|()" or "std::|<int>".
@ -173,7 +166,7 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
int max_num = g_config->completion.maxNum;
if (items.size() > max_num) {
items.resize(max_num);
complete_response->result.isIncomplete = true;
result.isIncomplete = true;
}
for (auto &item : items) {
@ -497,13 +490,12 @@ struct Handler_TextDocumentCompletion
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
const auto &params = request->params;
Out_TextDocumentComplete out;
out.id = request->id;
lsCompletionList result;
std::string path = params.textDocument.uri.GetPath();
WorkingFile *file = working_files->GetFileByFilename(path);
if (!file) {
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
return;
}
@ -548,7 +540,7 @@ struct Handler_TextDocumentCompletion
}
if (did_fail_check) {
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
return;
}
}
@ -561,8 +553,7 @@ struct Handler_TextDocumentCompletion
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
Out_TextDocumentComplete out;
out.id = request->id;
lsCompletionList result;
{
std::unique_lock<std::mutex> lock(
include_complete->completion_items_mutex, std::defer_lock);
@ -571,14 +562,14 @@ struct Handler_TextDocumentCompletion
std::string quote = preprocess.match[5];
for (auto &item : include_complete->completion_items)
if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
out.result.items.push_back(item);
result.items.push_back(item);
}
begin_pos.character = 0;
end_pos.character = (int)buffer_line.size();
FilterCandidates(&out, preprocess.pattern, begin_pos, end_pos,
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
buffer_line);
DecorateIncludePaths(preprocess.match, &out.result.items);
pipeline::WriteStdout(kMethodType, out);
DecorateIncludePaths(preprocess.match, &result.items);
pipeline::Reply(request->id, result);
} else {
std::string path = params.textDocument.uri.GetPath();
CompletionManager::OnComplete callback =
@ -587,13 +578,12 @@ struct Handler_TextDocumentCompletion
if (!OptConsumer)
return;
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
Out_TextDocumentComplete out;
out.id = id;
out.result.items = Consumer->ls_items;
lsCompletionList result;
result.items = Consumer->ls_items;
FilterCandidates(&out, completion_text, begin_pos, end_pos,
FilterCandidates(result, completion_text, begin_pos, end_pos,
buffer_line);
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(id, result);
if (!Consumer->from_cache) {
cache.WithLock([&]() {
cache.path = path;

View File

@ -26,7 +26,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/definition";
struct In_TextDocumentDefinition : public RequestInMessage {
struct In_TextDocumentDefinition : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentPositionParams params;
};
@ -66,9 +66,7 @@ struct Handler_TextDocumentDefinition
params.textDocument.uri.GetPath(), &file, &file_id))
return;
Out_LocationList out;
out.id = request->id;
std::vector<lsLocation> result;
Maybe<Use> on_def;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsPosition &ls_pos = params.position;
@ -104,19 +102,18 @@ struct Handler_TextDocumentDefinition
uses.push_back(*on_def);
}
auto locs = GetLsLocations(db, working_files, uses);
out.result.insert(out.result.end(), locs.begin(), locs.end());
result.insert(result.end(), locs.begin(), locs.end());
}
if (out.result.size()) {
std::sort(out.result.begin(), out.result.end());
out.result.erase(std::unique(out.result.begin(), out.result.end()),
out.result.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) {
out.result.push_back(
result.push_back(
lsLocation{lsDocumentUri::FromPath(include.resolved_path)});
range = {{0, 0}, {0, 0}};
break;
@ -177,12 +174,12 @@ struct Handler_TextDocumentDefinition
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
assert(dr);
if (auto loc = GetLsLocation(db, working_files, *dr))
out.result.push_back(*loc);
result.push_back(*loc);
}
}
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDefinition);

View File

@ -27,7 +27,7 @@ MethodType didClose = "textDocument/didClose";
MethodType didOpen = "textDocument/didOpen";
MethodType didSave = "textDocument/didSave";
struct In_TextDocumentDidChange : public NotificationInMessage {
struct In_TextDocumentDidChange : public NotificationMessage {
MethodType GetMethodType() const override { return didChange; }
lsTextDocumentDidChangeParams params;
};
@ -51,7 +51,7 @@ struct Handler_TextDocumentDidChange
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);
struct In_TextDocumentDidClose : public NotificationInMessage {
struct In_TextDocumentDidClose : public NotificationMessage {
MethodType GetMethodType() const override { return didClose; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -68,19 +68,13 @@ struct Handler_TextDocumentDidClose
void Run(In_TextDocumentDidClose *request) override {
std::string path = request->params.textDocument.uri.GetPath();
// Clear any diagnostics for the file.
Out_TextDocumentPublishDiagnostics out;
out.params.uri = request->params.textDocument.uri;
pipeline::WriteStdout(didClose, out);
// Remove internal state.
working_files->OnClose(request->params.textDocument);
clang_complete->OnClose(path);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);
struct In_TextDocumentDidOpen : public NotificationInMessage {
struct In_TextDocumentDidOpen : public NotificationMessage {
MethodType GetMethodType() const override { return didOpen; }
struct Params {
@ -113,9 +107,9 @@ struct Handler_TextDocumentDidOpen
QueryFile *file = nullptr;
FindFileOrFail(db, project, std::nullopt, path, &file);
if (file && file->def) {
EmitSkippedRanges(working_file, file->def->skipped_ranges);
EmitSemanticHighlighting(db, working_file, file);
if (file) {
EmitSkippedRanges(working_file, *file);
EmitSemanticHighlight(db, working_file, *file);
}
include_complete->AddFile(working_file->filename);
@ -137,7 +131,7 @@ struct Handler_TextDocumentDidOpen
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
struct In_TextDocumentDidSave : public NotificationInMessage {
struct In_TextDocumentDidSave : public NotificationMessage {
MethodType GetMethodType() const override { return didSave; }
struct Params {

View File

@ -39,20 +39,13 @@ struct lsDocumentHighlight {
};
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
struct In_TextDocumentDocumentHighlight : public RequestInMessage {
struct In_TextDocumentDocumentHighlight : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentPositionParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentDocumentHighlight, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentDocumentHighlight);
struct Out_TextDocumentDocumentHighlight
: public lsOutMessage<Out_TextDocumentDocumentHighlight> {
lsRequestId id;
std::vector<lsDocumentHighlight> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentHighlight, jsonrpc, id, result);
struct Handler_TextDocumentDocumentHighlight
: BaseMessageHandler<In_TextDocumentDocumentHighlight> {
MethodType GetMethodType() const override { return kMethodType; }
@ -63,14 +56,12 @@ struct Handler_TextDocumentDocumentHighlight
request->params.textDocument.uri.GetPath(), &file,
&file_id))
return;
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_TextDocumentDocumentHighlight out;
out.id = request->id;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
std::vector<lsDocumentHighlight> result;
std::vector<SymbolRef> syms = FindSymbolsAtLocation(
working_file, file, request->params.position, true);
std::vector<SymbolRef> syms =
FindSymbolsAtLocation(wfile, file, request->params.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0)
continue;
@ -90,11 +81,11 @@ struct Handler_TextDocumentDocumentHighlight
else
highlight.kind = lsDocumentHighlight::Text;
highlight.role = sym.role;
out.result.push_back(highlight);
result.push_back(highlight);
}
}
std::sort(out.result.begin(), out.result.end());
pipeline::WriteStdout(kMethodType, out);
std::sort(result.begin(), result.end());
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDocumentHighlight);

View File

@ -24,7 +24,7 @@ MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);
namespace {
MethodType kMethodType = "textDocument/documentSymbol";
struct In_TextDocumentDocumentSymbol : public RequestInMessage {
struct In_TextDocumentDocumentSymbol : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -40,20 +40,6 @@ MAKE_REFLECT_STRUCT(In_TextDocumentDocumentSymbol::Params, textDocument, all,
MAKE_REFLECT_STRUCT(In_TextDocumentDocumentSymbol, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentDocumentSymbol);
struct Out_SimpleDocumentSymbol
: public lsOutMessage<Out_SimpleDocumentSymbol> {
lsRequestId id;
std::vector<lsRange> result;
};
MAKE_REFLECT_STRUCT(Out_SimpleDocumentSymbol, jsonrpc, id, result);
struct Out_TextDocumentDocumentSymbol
: public lsOutMessage<Out_TextDocumentDocumentSymbol> {
lsRequestId id;
std::vector<lsSymbolInformation> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentSymbol, jsonrpc, id, result);
struct lsDocumentSymbol {
std::string name;
std::string detail;
@ -69,13 +55,6 @@ void Reflect(Writer &vis, std::unique_ptr<lsDocumentSymbol> &v) {
Reflect(vis, *v);
}
struct Out_HierarchicalDocumentSymbol
: public lsOutMessage<Out_HierarchicalDocumentSymbol> {
lsRequestId id;
std::vector<std::unique_ptr<lsDocumentSymbol>> result;
};
MAKE_REFLECT_STRUCT(Out_HierarchicalDocumentSymbol, jsonrpc, id, result);
template <typename Def>
bool Ignore(const Def *def) {
return false;
@ -107,15 +86,14 @@ struct Handler_TextDocumentDocumentSymbol
const auto &symbol2refcnt =
params.all ? file->symbol2refcnt : file->outline2refcnt;
if (params.startLine >= 0) {
Out_SimpleDocumentSymbol out;
out.id = request->id;
std::vector<lsRange> result;
for (auto [sym, refcnt] : symbol2refcnt)
if (refcnt > 0 && params.startLine <= sym.range.start.line &&
sym.range.start.line <= params.endLine)
if (auto loc = GetLsLocation(db, working_files, sym, file_id))
out.result.push_back(loc->range);
std::sort(out.result.begin(), out.result.end());
pipeline::WriteStdout(kMethodType, out);
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<const QueryFunc::Def *, lsDocumentSymbol *>> funcs;
@ -207,15 +185,13 @@ struct Handler_TextDocumentDocumentSymbol
ds->children.push_back(std::move(it->second));
}
}
Out_HierarchicalDocumentSymbol out;
out.id = request->id;
std::vector<std::unique_ptr<lsDocumentSymbol>> result;
for (auto &[_, ds] : sym2ds)
if (ds)
out.result.push_back(std::move(ds));
pipeline::WriteStdout(kMethodType, out);
result.push_back(std::move(ds));
pipeline::Reply(request->id, result);
} else {
Out_TextDocumentDocumentSymbol out;
out.id = request->id;
std::vector<lsSymbolInformation> result;
for (auto [sym, refcnt] : symbol2refcnt) {
if (refcnt <= 0) continue;
if (std::optional<lsSymbolInformation> info =
@ -227,11 +203,11 @@ struct Handler_TextDocumentDocumentSymbol
continue;
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
info->location = *loc;
out.result.push_back(*info);
result.push_back(*info);
}
}
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
}
};

View File

@ -36,13 +36,6 @@ struct lsFormattingOptions {
};
MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces);
struct Out_TextDocumentFormatting
: public lsOutMessage<Out_TextDocumentFormatting> {
lsRequestId id;
std::vector<lsTextEdit> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentFormatting, jsonrpc, id, result);
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());
@ -91,20 +84,17 @@ void Format(WorkingFile *wfile, tooling::Range range, lsRequestId id) {
auto ReplsOrErr =
FormatCode(code, wfile->filename, range);
if (ReplsOrErr) {
Out_TextDocumentFormatting out;
out.id = id;
out.result = ReplacementsToEdits(code, *ReplsOrErr);
pipeline::WriteStdout(formatting, out);
auto result = ReplacementsToEdits(code, *ReplsOrErr);
pipeline::Reply(id, result);
} else {
Out_Error err;
err.id = id;
err.error.code = lsErrorCodes::UnknownErrorCode;
err.error.message = llvm::toString(ReplsOrErr.takeError());
pipeline::WriteStdout(kMethodType_Unknown, err);
lsResponseError err;
err.code = lsErrorCodes::UnknownErrorCode;
err.message = llvm::toString(ReplsOrErr.takeError());
pipeline::ReplyError(id, err);
}
}
struct In_TextDocumentFormatting : public RequestInMessage {
struct In_TextDocumentFormatting : public RequestMessage {
MethodType GetMethodType() const override { return formatting; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -132,7 +122,7 @@ struct Handler_TextDocumentFormatting
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting);
struct In_TextDocumentOnTypeFormatting : public RequestInMessage {
struct In_TextDocumentOnTypeFormatting : public RequestMessage {
MethodType GetMethodType() const override { return onTypeFormatting; }
struct Params {
lsTextDocumentIdentifier textDocument;
@ -168,7 +158,7 @@ struct Handler_TextDocumentOnTypeFormatting
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting);
struct In_TextDocumentRangeFormatting : public RequestInMessage {
struct In_TextDocumentRangeFormatting : public RequestMessage {
MethodType GetMethodType() const override { return rangeFormatting; }
struct Params {
lsTextDocumentIdentifier textDocument;

View File

@ -70,25 +70,18 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
return {hover, ls_comments};
}
struct In_TextDocumentHover : public RequestInMessage {
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 Out_TextDocumentHover : public lsOutMessage<Out_TextDocumentHover> {
struct Result {
std::vector<lsMarkedString> contents;
std::optional<lsRange> range;
};
lsRequestId id;
std::optional<Result> result;
struct lsHover {
std::vector<lsMarkedString> contents;
std::optional<lsRange> range;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentHover::Result, contents, range);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_TextDocumentHover, jsonrpc, id,
result);
MAKE_REFLECT_STRUCT(lsHover, contents, range);
struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
MethodType GetMethodType() const override { return kMethodType; }
@ -99,33 +92,27 @@ struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
params.textDocument.uri.GetPath(), &file))
return;
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsHover result;
Out_TextDocumentHover out;
out.id = request->id;
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, params.position)) {
// Found symbol. Return hover.
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);
auto [hover, comments] = GetHover(db, file->def->language, sym, file->id);
if (comments || hover) {
out.result = Out_TextDocumentHover::Result();
out.result->range = *ls_range;
result.range = *ls_range;
if (comments)
out.result->contents.push_back(*comments);
result.contents.push_back(*comments);
if (hover)
out.result->contents.push_back(*hover);
result.contents.push_back(*hover);
break;
}
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentHover);

View File

@ -1,64 +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 "message_handler.h"
#include "pipeline.hh"
#include "query_utils.h"
using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/implementation";
struct In_TextDocumentImplementation : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
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 kMethodType; }
void Run(In_TextDocumentImplementation *request) override {
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file)) {
return;
}
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_LocationList out;
out.id = request->id;
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) {
if (sym.kind == SymbolKind::Type) {
QueryType &type = db->GetType(sym);
out.result = GetLsLocations(db, working_files,
GetTypeDeclarations(db, type.derived));
break;
} else if (sym.kind == SymbolKind::Func) {
QueryFunc &func = db->GetFunc(sym);
out.result = GetLsLocations(db, working_files,
GetFuncDeclarations(db, func.derived));
break;
}
}
pipeline::WriteStdout(kMethodType, out);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentImplementation);
} // namespace

View File

@ -24,7 +24,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/references";
struct In_TextDocumentReferences : public RequestInMessage {
struct In_TextDocumentReferences : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct lsReferenceContext {
bool base = true;
@ -60,11 +60,10 @@ struct Handler_TextDocumentReferences
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file))
return;
Out_LocationList out;
out.id = request->id;
std::vector<lsLocation> result;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!file) {
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
return;
}
@ -86,7 +85,7 @@ struct Handler_TextDocumentReferences
!(use.role & params.context.excludeRole) &&
seen_uses.insert(use).second)
if (auto loc = GetLsLocation(db, working_files, use)) {
out.result.push_back(*loc);
result.push_back(*loc);
}
};
WithEntity(db, sym, [&](const auto &entity) {
@ -116,7 +115,7 @@ struct Handler_TextDocumentReferences
break;
}
if (out.result.empty()) {
if (result.empty()) {
// |path| is the #include line. If the cursor is not on such line but line
// = 0,
// use the current filename.
@ -134,16 +133,16 @@ struct Handler_TextDocumentReferences
for (const IndexInclude &include : file1.def->includes)
if (include.resolved_path == path) {
// Another file |file1| has the same include line.
lsLocation &loc = out.result.emplace_back();
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)out.result.size() >= g_config->xref.maxNum)
out.result.resize(g_config->xref.maxNum);
pipeline::WriteStdout(kMethodType, out);
if ((int)result.size() >= g_config->xref.maxNum)
result.resize(g_config->xref.maxNum);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentReferences);

View File

@ -63,7 +63,7 @@ lsWorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *working_files,
return edit;
}
struct In_TextDocumentRename : public RequestInMessage {
struct In_TextDocumentRename : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
// The document to format.
@ -84,12 +84,6 @@ MAKE_REFLECT_STRUCT(In_TextDocumentRename::Params, textDocument, position,
MAKE_REFLECT_STRUCT(In_TextDocumentRename, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentRename);
struct Out_TextDocumentRename : public lsOutMessage<Out_TextDocumentRename> {
lsRequestId id;
lsWorkspaceEdit result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentRename, jsonrpc, id, result);
struct Handler_TextDocumentRename : BaseMessageHandler<In_TextDocumentRename> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentRename *request) override {
@ -97,25 +91,19 @@ struct Handler_TextDocumentRename : BaseMessageHandler<In_TextDocumentRename> {
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file,
&file_id)) {
&file_id))
return;
}
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_TextDocumentRename out;
out.id = request->id;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsWorkspaceEdit result;
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) {
// Found symbol. Return references to rename.
out.result =
FindSymbolsAtLocation(wfile, file, request->params.position)) {
result =
BuildWorkspaceEdit(db, working_files, sym, request->params.newName);
break;
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRename);

View File

@ -55,20 +55,13 @@ struct lsSignatureHelp {
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature,
activeParameter);
struct In_TextDocumentSignatureHelp : public RequestInMessage {
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);
struct Out_TextDocumentSignatureHelp
: public lsOutMessage<Out_TextDocumentSignatureHelp> {
lsRequestId id;
lsSignatureHelp result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result);
std::string BuildOptional(const CodeCompletionString &CCS,
std::vector<lsParameterInformation> &ls_params) {
std::string ret;
@ -199,10 +192,7 @@ struct Handler_TextDocumentSignatureHelp
if (!OptConsumer)
return;
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
Out_TextDocumentSignatureHelp out;
out.id = id;
out.result = Consumer->ls_sighelp;
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(id, Consumer->ls_sighelp);
if (!Consumer->from_cache) {
cache.WithLock([&]() {
cache.path = path;

View File

@ -21,7 +21,7 @@ using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/typeDefinition";
struct In_TextDocumentTypeDefinition : public RequestInMessage {
struct In_TextDocumentTypeDefinition : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentPositionParams params;
};
@ -40,18 +40,17 @@ struct Handler_TextDocumentTypeDefinition
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_LocationList out;
out.id = request->id;
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))
out.result.push_back(*ls_loc);
result.push_back(*ls_loc);
}
if (out.result.empty())
if (result.empty())
for (const DeclRef &dr : type.declarations)
if (auto ls_loc = GetLsLocation(db, working_files, dr))
out.result.push_back(*ls_loc);
result.push_back(*ls_loc);
};
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) {
@ -75,7 +74,7 @@ struct Handler_TextDocumentTypeDefinition
}
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentTypeDefinition);

View File

@ -25,6 +25,7 @@ using namespace ccls;
namespace {
MethodType didChangeConfiguration = "workspace/didChangeConfiguration",
didChangeWatchedFiles = "workspace/didChangeWatchedFiles",
didChangeWorkspaceFolders = "workspace/didChangeWorkspaceFolders";
struct lsDidChangeConfigurationParams {
@ -32,7 +33,7 @@ struct lsDidChangeConfigurationParams {
};
MAKE_REFLECT_STRUCT(lsDidChangeConfigurationParams, placeholder);
struct In_workspaceDidChangeConfiguration : public NotificationInMessage {
struct In_workspaceDidChangeConfiguration : public NotificationMessage {
MethodType GetMethodType() const override { return didChangeConfiguration; }
lsDidChangeConfigurationParams params;
};
@ -53,12 +54,66 @@ struct Handler_workspaceDidChangeConfiguration
};
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 NotificationInMessage {
struct In_workspaceDidChangeWorkspaceFolders : public NotificationMessage {
MethodType GetMethodType() const override {
return didChangeWorkspaceFolders;
}

View File

@ -1,79 +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 "message_handler.h"
#include "pipeline.hh"
#include "project.h"
#include "working_files.h"
using namespace ccls;
namespace {
MethodType kMethodType = "workspace/didChangeWatchedFiles";
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 NotificationInMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsDidChangeWatchedFilesParams params;
};
MAKE_REFLECT_STRUCT(In_WorkspaceDidChangeWatchedFiles, params);
REGISTER_IN_MESSAGE(In_WorkspaceDidChangeWatchedFiles);
struct Handler_WorkspaceDidChangeWatchedFiles
: BaseMessageHandler<In_WorkspaceDidChangeWatchedFiles> {
MethodType GetMethodType() const override { return kMethodType; }
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);
} // namespace

View File

@ -54,7 +54,7 @@ bool AddSymbol(
return true;
}
struct In_WorkspaceSymbol : public RequestInMessage {
struct In_WorkspaceSymbol : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
std::string query;
@ -65,20 +65,11 @@ MAKE_REFLECT_STRUCT(In_WorkspaceSymbol::Params, query);
MAKE_REFLECT_STRUCT(In_WorkspaceSymbol, id, params);
REGISTER_IN_MESSAGE(In_WorkspaceSymbol);
struct Out_WorkspaceSymbol : public lsOutMessage<Out_WorkspaceSymbol> {
lsRequestId id;
std::vector<lsSymbolInformation> result;
};
MAKE_REFLECT_STRUCT(Out_WorkspaceSymbol, jsonrpc, id, result);
///// Fuzzy matching
struct Handler_WorkspaceSymbol : BaseMessageHandler<In_WorkspaceSymbol> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_WorkspaceSymbol *request) override {
Out_WorkspaceSymbol out;
out.id = request->id;
std::vector<lsSymbolInformation> result;
std::string query = request->params.query;
// {symbol info, matching detailed_name or short_name, index}
@ -128,20 +119,20 @@ struct Handler_WorkspaceSymbol : BaseMessageHandler<In_WorkspaceSymbol> {
std::sort(cands.begin(), cands.end(), [](const auto &l, const auto &r) {
return std::get<1>(l) > std::get<1>(r);
});
out.result.reserve(cands.size());
result.reserve(cands.size());
for (auto &cand : cands) {
// Discard awful candidates.
if (std::get<1>(cand) <= FuzzyMatcher::kMinScore)
break;
out.result.push_back(std::get<0>(cand));
result.push_back(std::get<0>(cand));
}
} else {
out.result.reserve(cands.size());
result.reserve(cands.size());
for (auto &cand : cands)
out.result.push_back(std::get<0>(cand));
result.push_back(std::get<0>(cand));
}
pipeline::WriteStdout(kMethodType, out);
pipeline::Reply(request->id, result);
}
};
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceSymbol);

View File

@ -1,55 +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 "method.h"
MethodType kMethodType_Unknown = "$unknown";
MethodType kMethodType_Exit = "exit";
MethodType kMethodType_TextDocumentPublishDiagnostics =
"textDocument/publishDiagnostics";
MethodType kMethodType_CclsPublishSkippedRanges = "$ccls/publishSkippedRanges";
MethodType kMethodType_CclsPublishSemanticHighlighting =
"$ccls/publishSemanticHighlighting";
void Reflect(Reader &visitor, lsRequestId &value) {
if (visitor.IsInt64()) {
value.type = lsRequestId::kInt;
value.value = int(visitor.GetInt64());
} else if (visitor.IsInt()) {
value.type = lsRequestId::kInt;
value.value = visitor.GetInt();
} else if (visitor.IsString()) {
value.type = lsRequestId::kString;
value.value = atoll(visitor.GetString());
} else {
value.type = lsRequestId::kNone;
value.value = -1;
}
}
void Reflect(Writer &visitor, lsRequestId &value) {
switch (value.type) {
case lsRequestId::kNone:
visitor.Null();
break;
case lsRequestId::kInt:
visitor.Int(value.value);
break;
case lsRequestId::kString:
auto s = std::to_string(value.value);
visitor.String(s.c_str(), s.length());
break;
}
}

View File

@ -1,59 +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.
==============================================================================*/
#pragma once
#include "serializer.h"
#include "utils.h"
#include <string>
using MethodType = const char *;
extern MethodType kMethodType_Unknown;
extern MethodType kMethodType_Exit;
extern MethodType kMethodType_TextDocumentPublishDiagnostics;
extern MethodType kMethodType_CclsPublishSkippedRanges;
extern MethodType kMethodType_CclsPublishSemanticHighlighting;
struct lsRequestId {
// The client can send the request id as an int or a string. We should output
// the same format we received.
enum Type { kNone, kInt, kString };
Type type = kNone;
int value = -1;
bool Valid() const { return type != kNone; }
};
void Reflect(Reader &visitor, lsRequestId &value);
void Reflect(Writer &visitor, lsRequestId &value);
struct InMessage {
virtual ~InMessage() = default;
virtual MethodType GetMethodType() const = 0;
virtual lsRequestId GetRequestId() const = 0;
};
struct RequestInMessage : public InMessage {
// number or string, actually no null
lsRequestId id;
lsRequestId GetRequestId() const override { return id; }
};
// NotificationInMessage does not have |id|.
struct NotificationInMessage : public InMessage {
lsRequestId GetRequestId() const override { return lsRequestId(); }
};

View File

@ -26,6 +26,9 @@ limitations under the License.
#include "platform.h"
#include "project.h"
#include "query_utils.h"
#include "serializers/json.h"
#include <rapidjson/writer.h>
#include <llvm/Support/Threading.h>
#include <llvm/Support/Timer.h>
@ -76,18 +79,13 @@ struct Index_Request {
int64_t ts = tick++;
};
struct Stdout_Request {
MethodType method;
std::string content;
};
MultiQueueWaiter *main_waiter;
MultiQueueWaiter *indexer_waiter;
MultiQueueWaiter *stdout_waiter;
ThreadedQueue<std::unique_ptr<InMessage>> *on_request;
ThreadedQueue<Index_Request> *index_request;
ThreadedQueue<IndexUpdate> *on_indexed;
ThreadedQueue<Stdout_Request> *for_stdout;
ThreadedQueue<std::string> *for_stdout;
struct InMemoryIndexFile {
std::string content;
@ -299,11 +297,10 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
if (!ok) {
if (request.id.Valid()) {
Out_Error out;
out.id = request.id;
out.error.code = lsErrorCodes::InternalError;
out.error.message = "Failed to index " + path_to_index;
pipeline::WriteStdout(kMethodType_Unknown, out);
lsResponseError err;
err.code = lsErrorCodes::InternalError;
err.message = "failed to index " + path_to_index;
pipeline::ReplyError(request.id, err);
}
return true;
}
@ -363,7 +360,7 @@ void Init() {
index_request = new ThreadedQueue<Index_Request>(indexer_waiter);
stdout_waiter = new MultiQueueWaiter;
for_stdout = new ThreadedQueue<Stdout_Request>(stdout_waiter);
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
}
void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project,
@ -383,8 +380,8 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
std::string filename = LowerPathIfInsensitive(f->filename);
if (db->name2file_id.find(filename) == db->name2file_id.end())
continue;
QueryFile *file = &db->files[db->name2file_id[filename]];
EmitSemanticHighlighting(db, f.get(), file);
QueryFile &file = db->files[db->name2file_id[filename]];
EmitSemanticHighlight(db, f.get(), file);
}
return;
}
@ -403,8 +400,9 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
// request.path
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
: def_u.second);
EmitSkippedRanges(wfile, def_u.first.skipped_ranges);
EmitSemanticHighlighting(db, wfile, &db->files[update->file_id]);
QueryFile &file = db->files[update->file_id];
EmitSkippedRanges(wfile, file);
EmitSemanticHighlight(db, wfile, file);
}
}
}
@ -414,21 +412,20 @@ void LaunchStdin() {
set_thread_name("stdin");
while (true) {
std::unique_ptr<InMessage> message;
std::optional<std::string> err =
std::optional<std::string> error =
MessageRegistry::instance()->ReadMessageFromStdin(&message);
// Message parsing can fail if we don't recognize the method.
if (err) {
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()) {
Out_Error out;
out.id = id;
out.error.code = lsErrorCodes::InvalidParams;
out.error.message = std::move(*err);
WriteStdout(kMethodType_Unknown, out);
lsResponseError err;
err.code = lsErrorCodes::InvalidParams;
err.message = std::move(*error);
ReplyError(id, err);
}
}
continue;
@ -453,20 +450,12 @@ void LaunchStdout() {
set_thread_name("stdout");
while (true) {
std::vector<Stdout_Request> messages = for_stdout->DequeueAll();
if (messages.empty()) {
stdout_waiter->Wait(for_stdout);
continue;
}
for (auto &message : messages) {
#ifdef _WIN32
fwrite(message.content.c_str(), message.content.size(), 1, stdout);
fflush(stdout);
#else
write(1, message.content.c_str(), message.content.size());
#endif
std::vector<std::string> messages = for_stdout->DequeueAll();
for (auto &s : messages) {
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
llvm::outs().flush();
}
stdout_waiter->Wait(for_stdout);
}
})
.detach();
@ -480,20 +469,17 @@ void MainLoop() {
CompletionManager clang_complete(
&project, &working_files,
[&](std::string path, std::vector<lsDiagnostic> diagnostics) {
Out_TextDocumentPublishDiagnostics out;
out.params.uri = lsDocumentUri::FromPath(path);
out.params.diagnostics = diagnostics;
ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics,
out);
lsPublishDiagnosticsParams params;
params.uri = lsDocumentUri::FromPath(path);
params.diagnostics = diagnostics;
Notify("textDocument/publishDiagnostics", params);
},
[](lsRequestId id) {
if (id.Valid()) {
Out_Error out;
out.id = id;
out.error.code = lsErrorCodes::InternalError;
out.error.message = "Dropping completion request; a newer request "
"has come in that will be serviced instead.";
pipeline::WriteStdout(kMethodType_Unknown, out);
lsResponseError err;
err.code = lsErrorCodes::InternalError;
err.message = "drop older completion request";
ReplyError(id, err);
}
});
@ -566,14 +552,54 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
return ReadContent(GetCachePath(path));
}
void WriteStdout(MethodType method, lsBaseOutMessage &response) {
std::ostringstream sstream;
response.Write(sstream);
void Notify(const char *method, const std::function<void(Writer &)> &fn) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output);
w.StartObject();
w.Key("jsonrpc");
w.String("2.0");
w.Key("method");
w.String(method);
w.Key("params");
JsonWriter writer(&w);
fn(writer);
w.EndObject();
for_stdout->PushBack(output.GetString());
}
Stdout_Request out;
out.content = sstream.str();
out.method = method;
for_stdout->PushBack(std::move(out));
static void Reply(lsRequestId id, const char *key,
const std::function<void(Writer &)> &fn) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> w(output);
w.StartObject();
w.Key("jsonrpc");
w.String("2.0");
w.Key("id");
switch (id.type) {
case lsRequestId::kNone:
w.Null();
break;
case lsRequestId::kInt:
w.Int(id.value);
break;
case lsRequestId::kString:
auto s = std::to_string(id.value);
w.String(s.c_str(), s.length());
break;
}
w.Key(key);
JsonWriter writer(&w);
fn(writer);
w.EndObject();
for_stdout->PushBack(output.GetString());
}
void Reply(lsRequestId id, const std::function<void(Writer &)> &fn) {
Reply(id, "result", fn);
}
void ReplyError(lsRequestId id, const std::function<void(Writer &)> &fn) {
Reply(id, "error", fn);
}
} // namespace ccls::pipeline

View File

@ -1,7 +1,6 @@
#pragma once
#include "lsp_diagnostic.h"
#include "method.h"
#include "lsp.h"
#include "query.h"
#include <atomic>
@ -15,7 +14,6 @@ struct GroupMatch;
struct VFS;
struct Project;
struct WorkingFiles;
struct lsBaseOutMessage;
struct VFS {
struct State {
@ -52,6 +50,20 @@ void Index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, lsRequestId id = {});
std::optional<std::string> LoadIndexedContent(const std::string& path);
void WriteStdout(MethodType method, lsBaseOutMessage &response);
void Notify(const char *method, const std::function<void(Writer &)> &fn);
template <typename T> void Notify(const char *method, T &result) {
Notify(method, [&](Writer &w) { Reflect(w, result); });
}
void Reply(lsRequestId id, const std::function<void(Writer &)> &fn);
template <typename T> void Reply(lsRequestId id, T &result) {
Reply(id, [&](Writer &w) { Reflect(w, result); });
}
void ReplyError(lsRequestId id, const std::function<void(Writer &)> &fn);
template <typename T> void ReplyError(lsRequestId id, T &result) {
ReplyError(id, [&](Writer &w) { Reflect(w, result); });
}
} // namespace pipeline
} // namespace ccls

View File

@ -17,7 +17,6 @@ limitations under the License.
#include "config.h"
#include "lsp.h"
#include "method.h"
#include <functional>
#include <mutex>

View File

@ -32,7 +32,13 @@ using namespace llvm;
bool gTestOutputMode = false;
//// Elementary types
Reader::~Reader() {}
BinaryReader::~BinaryReader() {}
JsonReader::~JsonReader() {}
Writer::~Writer() {}
BinaryWriter::~BinaryWriter() {}
JsonWriter::~JsonWriter() {}
void Reflect(Reader &visitor, uint8_t &value) { value = visitor.GetUInt8(); }
void Reflect(Writer &visitor, uint8_t &value) { visitor.UInt8(value); }

View File

@ -38,11 +38,10 @@ class StringRef;
enum class SerializeFormat { Binary, Json };
struct JsonNull {};
struct mandatory_optional_tag {};
class Reader {
public:
virtual ~Reader() {}
virtual ~Reader();
virtual SerializeFormat Format() const = 0;
virtual bool IsBool() = 0;
@ -72,7 +71,7 @@ public:
class Writer {
public:
virtual ~Writer() {}
virtual ~Writer();
virtual SerializeFormat Format() const = 0;
virtual void Null() = 0;
@ -97,8 +96,6 @@ struct IndexFile;
#define REFLECT_MEMBER_START() ReflectMemberStart(visitor)
#define REFLECT_MEMBER_END() ReflectMemberEnd(visitor);
#define REFLECT_MEMBER(name) ReflectMember(visitor, #name, value.name)
#define REFLECT_MEMBER_MANDATORY_OPTIONAL(name) \
ReflectMember(visitor, #name, value.name, mandatory_optional_tag{})
#define REFLECT_MEMBER2(name, value) ReflectMember(visitor, name, value)
#define MAKE_REFLECT_TYPE_PROXY(type_name) \
@ -115,8 +112,6 @@ struct IndexFile;
}
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
#define _MAPPABLE_REFLECT_MEMBER_MANDATORY_OPTIONAL(name) \
REFLECT_MEMBER_MANDATORY_OPTIONAL(name);
#define MAKE_REFLECT_EMPTY_STRUCT(type, ...) \
template <typename TVisitor> void Reflect(TVisitor &visitor, type &value) { \
@ -131,13 +126,6 @@ struct IndexFile;
REFLECT_MEMBER_END(); \
}
#define MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(type, ...) \
template <typename TVisitor> void Reflect(TVisitor &visitor, type &value) { \
REFLECT_MEMBER_START(); \
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER_MANDATORY_OPTIONAL, __VA_ARGS__) \
REFLECT_MEMBER_END(); \
}
// clang-format off
// Config has many fields, we need to support at least its number of fields.
#define NUM_VA_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,N,...) N
@ -268,13 +256,6 @@ void ReflectMember(Writer &visitor, const char *name, Maybe<T> &value) {
}
}
template <typename T>
void ReflectMember(Writer &visitor, const char *name, T &value,
mandatory_optional_tag) {
visitor.Key(name);
Reflect(visitor, value);
}
template <typename L, typename R>
void Reflect(Reader &vis, std::pair<L, R> &v) {
vis.Member("L", [&]() { Reflect(vis, v.first); });

View File

@ -46,6 +46,7 @@ class BinaryReader : public Reader {
public:
BinaryReader(std::string_view buf) : p_(buf.data()) {}
virtual ~BinaryReader();
SerializeFormat Format() const override { return SerializeFormat::Binary; }
bool IsBool() override { return true; }
@ -110,6 +111,7 @@ class BinaryWriter : public Writer {
void VarInt(int64_t n) { VarUInt(uint64_t(n) << 1 ^ n >> 63); }
public:
virtual ~BinaryWriter();
SerializeFormat Format() const override { return SerializeFormat::Binary; }
std::string Take() { return std::move(buf_); }

View File

@ -26,6 +26,7 @@ class JsonReader : public Reader {
public:
JsonReader(rapidjson::GenericValue<rapidjson::UTF8<>> *m) : m_(m) {}
virtual ~JsonReader();
SerializeFormat Format() const override { return SerializeFormat::Json; }
rapidjson::GenericValue<rapidjson::UTF8<>> &m() { return *m_; }
@ -95,6 +96,7 @@ class JsonWriter : public Writer {
public:
JsonWriter(rapidjson::Writer<rapidjson::StringBuffer> *m) : m_(m) {}
virtual ~JsonWriter();
SerializeFormat Format() const override { return SerializeFormat::Json; }
rapidjson::Writer<rapidjson::StringBuffer> &m() { return *m_; }

View File

@ -315,6 +315,8 @@ void WorkingFile::ComputeLineMapping() {
std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
bool is_end) {
if (line == (int)index_lines.size() && !*column)
return buffer_content.size();
if (line < 0 || line >= (int)index_lines.size()) {
LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, "
<< index_lines.size() << ")) in " << filename;
@ -329,7 +331,6 @@ std::optional<int> WorkingFile::GetBufferPosFromIndexPos(int line, int *column,
std::optional<int> WorkingFile::GetIndexPosFromBufferPos(int line, int *column,
bool is_end) {
// See GetBufferLineFromIndexLine for additional comments.
if (line < 0 || line >= (int)buffer_lines.size())
return std::nullopt;

View File

@ -15,7 +15,7 @@ limitations under the License.
#pragma once
#include "lsp_diagnostic.h"
#include "lsp.h"
#include "utils.h"
#include <mutex>