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 8fea558f95
commit 6deadc5f24
48 changed files with 716 additions and 1097 deletions

View File

@ -187,7 +187,6 @@ target_sources(ccls PRIVATE
src/main.cc src/main.cc
src/include_complete.cc src/include_complete.cc
src/indexer.cc src/indexer.cc
src/method.cc
src/log.cc src/log.cc
src/lsp.cc src/lsp.cc
src/match.cc src/match.cc
@ -225,12 +224,10 @@ target_sources(ccls PRIVATE
src/messages/textDocument_documentHighlight.cc src/messages/textDocument_documentHighlight.cc
src/messages/textDocument_documentSymbol.cc src/messages/textDocument_documentSymbol.cc
src/messages/textDocument_hover.cc src/messages/textDocument_hover.cc
src/messages/textDocument_implementation.cc
src/messages/textDocument_references.cc src/messages/textDocument_references.cc
src/messages/textDocument_rename.cc src/messages/textDocument_rename.cc
src/messages/textDocument_signatureHelp.cc src/messages/textDocument_signatureHelp.cc
src/messages/textDocument_typeDefinition.cc src/messages/textDocument_typeDefinition.cc
src/messages/workspace_did.cc src/messages/workspace_did.cc
src/messages/workspace_didChangeWatchedFiles.cc
src/messages/workspace_symbol.cc src/messages/workspace_symbol.cc
) )

View File

@ -5,8 +5,8 @@
#include "clang_tu.hh" #include "clang_tu.hh"
#include "lru_cache.h" #include "lru_cache.h"
#include "lsp.h"
#include "lsp_completion.h" #include "lsp_completion.h"
#include "lsp_diagnostic.h"
#include "project.h" #include "project.h"
#include "threaded_queue.h" #include "threaded_queue.h"
#include "working_files.h" #include "working_files.h"

View File

@ -9,19 +9,22 @@
#include <queue> #include <queue>
template <typename Node> 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; std::queue<const Node *> q;
for (auto &entry : root.children) for (auto &entry : root->children)
q.push(&entry); q.push(&entry);
while (q.size()) { while (q.size()) {
auto *entry = q.front(); auto *entry = q.front();
q.pop(); q.pop();
if (entry->location.uri.raw_uri.size()) if (entry->location.uri.raw_uri.size())
out.result.push_back({entry->location}); ret.push_back({entry->location});
for (auto &entry1 : entry->children) for (auto &entry1 : entry->children)
q.push(&entry1); q.push(&entry1);
} }
std::sort(out.result.begin(), out.result.end()); std::sort(ret.begin(), ret.end());
out.result.erase(std::unique(out.result.begin(), out.result.end()), ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
out.result.end()); return ret;
} }

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.h"
#include "lsp_diagnostic.h"
#include "maybe.h" #include "maybe.h"
#include "position.h" #include "position.h"
#include "serializer.h" #include "serializer.h"

View File

@ -6,11 +6,46 @@
#include "log.hh" #include "log.hh"
#include "serializers/json.h" #include "serializers/json.h"
#include <rapidjson/writer.h> #include <rapidjson/document.h>
#include <algorithm> #include <algorithm>
#include <stdio.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; MessageRegistry *MessageRegistry::instance_ = nullptr;
lsTextDocumentIdentifier lsTextDocumentIdentifier
@ -134,29 +169,6 @@ MessageRegistry *MessageRegistry::instance() {
return 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 lsDocumentUri::FromPath(const std::string &path) {
lsDocumentUri result; lsDocumentUri result;
result.SetPath(path); result.SetPath(path);
@ -261,9 +273,3 @@ void Reflect(Writer &visitor, lsMarkedString &value) {
Reflect(visitor, value.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

@ -4,13 +4,42 @@
#pragma once #pragma once
#include "config.h" #include "config.h"
#include "method.h"
#include "serializer.h" #include "serializer.h"
#include "utils.h" #include "utils.h"
#include <iosfwd> #include <iosfwd>
#include <unordered_map> #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) \ #define REGISTER_IN_MESSAGE(type) \
static MessageRegistryRegister<type> type##message_handler_instance_; static MessageRegistryRegister<type> type##message_handler_instance_;
@ -41,45 +70,38 @@ template <typename T> struct MessageRegistryRegister {
} }
}; };
struct lsBaseOutMessage { enum class lsErrorCodes {
virtual ~lsBaseOutMessage(); // Defined by JSON RPC
virtual void ReflectWriter(Writer &) = 0; 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. // Defined by the protocol.
void Write(std::ostream &out); RequestCancelled = -32800,
};
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));
}
}; };
MAKE_REFLECT_TYPE_PROXY(lsErrorCodes);
struct lsResponseError { struct lsResponseError {
enum class lsErrorCodes : int { // A number indicating the error type that occurred.
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
RequestCancelled = -32800,
};
lsErrorCodes code; lsErrorCodes code;
// Short description.
// A string providing a short description of the error.
std::string message; 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";
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -305,35 +327,55 @@ MAKE_REFLECT_STRUCT(lsWorkspaceFolder, uri, name);
enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 }; enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
MAKE_REFLECT_TYPE_PROXY(lsMessageType) 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; lsMessageType type = lsMessageType::Error;
std::string message; std::string message;
}; };
MAKE_REFLECT_STRUCT(Out_ShowLogMessageParams, type, message); MAKE_REFLECT_STRUCT(lsShowMessageParams, 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);
// Used to identify the language at a file level. The ordering is important, as // 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 // a file previously identified as `C`, will be changed to `Cpp` if it

View File

@ -1,103 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#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

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

View File

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

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include "lsp.h" #include "lsp.h"
#include "method.h"
#include "query.h" #include "query.h"
#include <memory> #include <memory>
@ -23,30 +22,6 @@ struct DB;
struct WorkingFile; struct WorkingFile;
struct WorkingFiles; 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: // Usage:
// //
@ -91,8 +66,6 @@ bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id,
const std::string &absolute_path, const std::string &absolute_path,
QueryFile **out_query_file, int *out_file_id = nullptr); QueryFile **out_query_file, int *out_file_id = nullptr);
void EmitSkippedRanges(WorkingFile *working_file, void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file);
const std::vector<Range> &skipped_ranges);
void EmitSemanticHighlighting(DB *db, WorkingFile *working_file, void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file);
QueryFile *file);

View File

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

View File

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

View File

@ -10,9 +10,10 @@ using namespace ccls;
#include <unordered_set> #include <unordered_set>
namespace { 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; } MethodType GetMethodType() const override { return kMethodType; }
struct Params { struct Params {
// If id+kind are specified, expand a node; otherwise textDocument+position // If id+kind are specified, expand a node; otherwise textDocument+position
@ -32,39 +33,32 @@ struct In_CclsInheritance : public RequestInMessage {
} params; } params;
}; };
MAKE_REFLECT_STRUCT(In_CclsInheritance::Params, textDocument, position, MAKE_REFLECT_STRUCT(In_cclsInheritance::Params, textDocument, position, id,
id, kind, derived, qualified, levels, hierarchy); kind, derived, qualified, levels, hierarchy);
MAKE_REFLECT_STRUCT(In_CclsInheritance, id, params); MAKE_REFLECT_STRUCT(In_cclsInheritance, id, params);
REGISTER_IN_MESSAGE(In_CclsInheritance); REGISTER_IN_MESSAGE(In_cclsInheritance);
struct Out_CclsInheritance struct Out_cclsInheritance {
: public lsOutMessage<Out_CclsInheritance> { Usr usr;
struct Entry { std::string id;
Usr usr; SymbolKind kind;
std::string id; std::string_view name;
SymbolKind kind; lsLocation location;
std::string_view name; // For unexpanded nodes, this is an upper bound because some entities may be
lsLocation location; // undefined. If it is 0, there are no members.
// For unexpanded nodes, this is an upper bound because some entities may be int numChildren;
// undefined. If it is 0, there are no members. // Empty if the |levels| limit is reached.
int numChildren; std::vector<Out_cclsInheritance> children;
// Empty if the |levels| limit is reached.
std::vector<Entry> children;
};
lsRequestId id;
std::optional<Entry> result;
}; };
MAKE_REFLECT_STRUCT(Out_CclsInheritance::Entry, id, kind, name, MAKE_REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
location, numChildren, children); children);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsInheritance, jsonrpc,
id, result);
bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry, bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool derived, bool qualified, int levels); bool qualified, int levels);
template <typename Q> template <typename Q>
bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry, bool ExpandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool derived, bool qualified, int levels, Q &entity) { bool qualified, int levels, Q &entity) {
const auto *def = entity.AnyDef(); const auto *def = entity.AnyDef();
if (def) { if (def) {
entry->name = def->Name(qualified); entry->name = def->Name(qualified);
@ -85,7 +79,7 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
for (auto usr : entity.derived) { for (auto usr : entity.derived) {
if (!seen.insert(usr).second) if (!seen.insert(usr).second)
continue; continue;
Out_CclsInheritance::Entry entry1; Out_cclsInheritance entry1;
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;
entry1.kind = entry->kind; entry1.kind = entry->kind;
@ -100,7 +94,7 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
for (auto usr : def->bases) { for (auto usr : def->bases) {
if (!seen.insert(usr).second) if (!seen.insert(usr).second)
continue; continue;
Out_CclsInheritance::Entry entry1; Out_cclsInheritance entry1;
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;
entry1.kind = entry->kind; entry1.kind = entry->kind;
@ -114,8 +108,8 @@ bool ExpandHelper(MessageHandler *m, Out_CclsInheritance::Entry *entry,
return true; return true;
} }
bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry, bool Expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
bool derived, bool qualified, int levels) { bool qualified, int levels) {
if (entry->kind == SymbolKind::Func) if (entry->kind == SymbolKind::Func)
return ExpandHelper(m, entry, derived, qualified, levels, return ExpandHelper(m, entry, derived, qualified, levels,
m->db->Func(entry->usr)); m->db->Func(entry->usr));
@ -124,13 +118,12 @@ bool Expand(MessageHandler *m, Out_CclsInheritance::Entry *entry,
m->db->Type(entry->usr)); m->db->Type(entry->usr));
} }
struct Handler_CclsInheritance struct Handler_cclsInheritance : BaseMessageHandler<In_cclsInheritance> {
: BaseMessageHandler<In_CclsInheritance> {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsInheritance::Entry> std::optional<Out_cclsInheritance> BuildInitial(SymbolRef sym, bool derived,
BuildInitial(SymbolRef sym, bool derived, bool qualified, int levels) { bool qualified, int levels) {
Out_CclsInheritance::Entry entry; Out_cclsInheritance entry;
entry.id = std::to_string(sym.usr); entry.id = std::to_string(sym.usr);
entry.usr = sym.usr; entry.usr = sym.usr;
entry.kind = sym.kind; entry.kind = sym.kind;
@ -138,25 +131,24 @@ struct Handler_CclsInheritance
return entry; return entry;
} }
void Run(In_CclsInheritance *request) override { void Run(In_cclsInheritance *request) override {
auto &params = request->params; auto &params = request->params;
Out_CclsInheritance out; std::optional<Out_cclsInheritance> result;
out.id = request->id;
if (params.id.size()) { if (params.id.size()) {
try { try {
params.usr = std::stoull(params.id); params.usr = std::stoull(params.id);
} catch (...) { } catch (...) {
return; return;
} }
Out_CclsInheritance::Entry entry; result.emplace();
entry.id = std::to_string(params.usr); result->id = std::to_string(params.usr);
entry.usr = params.usr; result->usr = params.usr;
entry.kind = params.kind; result->kind = params.kind;
if (((entry.kind == SymbolKind::Func && db->HasFunc(entry.usr)) || if (!(((params.kind == SymbolKind::Func && db->HasFunc(params.usr)) ||
(entry.kind == SymbolKind::Type && db->HasType(entry.usr))) && (params.kind == SymbolKind::Type && db->HasType(params.usr))) &&
Expand(this, &entry, params.derived, params.qualified, params.levels)) Expand(this, &*result, params.derived, params.qualified,
out.result = std::move(entry); params.levels)))
result.reset();
} else { } else {
QueryFile *file; QueryFile *file;
if (!FindFileOrFail(db, project, request->id, if (!FindFileOrFail(db, project, request->id,
@ -166,23 +158,38 @@ struct Handler_CclsInheritance
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position))
if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) { if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) {
out.result = BuildInitial(sym, params.derived, params.qualified, result = BuildInitial(sym, params.derived, params.qualified,
params.levels); params.levels);
break; break;
} }
} }
if (params.hierarchy) { if (params.hierarchy)
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
return; 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 } // namespace

View File

@ -18,7 +18,7 @@ using namespace clang;
namespace { namespace {
MethodType kMethodType = "$ccls/member"; MethodType kMethodType = "$ccls/member";
struct In_CclsMember : public RequestInMessage { struct In_cclsMember : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
struct Params { struct Params {
@ -40,42 +40,36 @@ struct In_CclsMember : public RequestInMessage {
} params; } params;
}; };
MAKE_REFLECT_STRUCT(In_CclsMember::Params, textDocument, position, id, MAKE_REFLECT_STRUCT(In_cclsMember::Params, textDocument, position, id,
qualified, levels, kind, hierarchy); qualified, levels, kind, hierarchy);
MAKE_REFLECT_STRUCT(In_CclsMember, id, params); MAKE_REFLECT_STRUCT(In_cclsMember, id, params);
REGISTER_IN_MESSAGE(In_CclsMember); REGISTER_IN_MESSAGE(In_cclsMember);
struct Out_CclsMember : public lsOutMessage<Out_CclsMember> { struct Out_cclsMember {
struct Entry { Usr usr;
Usr usr; std::string id;
std::string id; std::string_view name;
std::string_view name; std::string fieldName;
std::string fieldName; lsLocation location;
lsLocation location; // For unexpanded nodes, this is an upper bound because some entities may be
// For unexpanded nodes, this is an upper bound because some entities may be // undefined. If it is 0, there are no members.
// undefined. If it is 0, there are no members. int numChildren = 0;
int numChildren = 0; // Empty if the |levels| limit is reached.
// Empty if the |levels| limit is reached. std::vector<Out_cclsMember> children;
std::vector<Entry> children;
};
lsRequestId id;
std::optional<Entry> result;
}; };
MAKE_REFLECT_STRUCT(Out_CclsMember::Entry, id, name, fieldName, MAKE_REFLECT_STRUCT(Out_cclsMember, id, name, fieldName, location, numChildren,
location, numChildren, children); children);
MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsMember, jsonrpc, id,
result);
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry, bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
bool qualified, int levels, SymbolKind memberKind); int levels, SymbolKind memberKind);
// Add a field to |entry| which is a Func/Type. // Add a field to |entry| which is a Func/Type.
void DoField(MessageHandler *m, Out_CclsMember::Entry *entry, void DoField(MessageHandler *m, Out_cclsMember *entry, const QueryVar &var,
const QueryVar &var, int64_t offset, bool qualified, int levels) { int64_t offset, bool qualified, int levels) {
const QueryVar::Def *def1 = var.AnyDef(); const QueryVar::Def *def1 = var.AnyDef();
if (!def1) if (!def1)
return; return;
Out_CclsMember::Entry entry1; Out_cclsMember entry1;
// With multiple inheritance, the offset is incorrect. // With multiple inheritance, the offset is incorrect.
if (offset >= 0) { if (offset >= 0) {
if (offset / 8 < 10) if (offset / 8 < 10)
@ -112,8 +106,8 @@ void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
} }
// Expand a type node by adding members recursively to it. // Expand a type node by adding members recursively to it.
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry, bool Expand(MessageHandler *m, Out_cclsMember *entry, bool qualified,
bool qualified, int levels, SymbolKind memberKind) { int levels, SymbolKind memberKind) {
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) { if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
entry->name = ClangBuiltinTypeName(int(entry->usr)); entry->name = ClangBuiltinTypeName(int(entry->usr));
return true; return true;
@ -145,7 +139,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
} }
if (def->alias_of) { if (def->alias_of) {
const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef(); 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.id = std::to_string(def->alias_of);
entry1.usr = def->alias_of; entry1.usr = def->alias_of;
if (def1 && def1->spell) { if (def1 && def1->spell) {
@ -175,7 +169,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
if (seen1.insert(usr).second) { if (seen1.insert(usr).second) {
QueryFunc &func1 = m->db->Func(usr); QueryFunc &func1 = m->db->Func(usr);
if (const QueryFunc::Def *def1 = func1.AnyDef()) { if (const QueryFunc::Def *def1 = func1.AnyDef()) {
Out_CclsMember::Entry entry1; Out_cclsMember entry1;
entry1.fieldName = def1->Name(false); entry1.fieldName = def1->Name(false);
if (def1->spell) { if (def1->spell) {
if (auto loc = if (auto loc =
@ -196,7 +190,7 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
if (seen1.insert(usr).second) { if (seen1.insert(usr).second) {
QueryType &type1 = m->db->Type(usr); QueryType &type1 = m->db->Type(usr);
if (const QueryType::Def *def1 = type1.AnyDef()) { if (const QueryType::Def *def1 = type1.AnyDef()) {
Out_CclsMember::Entry entry1; Out_cclsMember entry1;
entry1.fieldName = def1->Name(false); entry1.fieldName = def1->Name(false);
if (def1->spell) { if (def1->spell) {
if (auto loc = if (auto loc =
@ -227,12 +221,12 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
return true; return true;
} }
struct Handler_CclsMember struct Handler_cclsMember : BaseMessageHandler<In_cclsMember> {
: BaseMessageHandler<In_CclsMember> {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsMember::Entry> std::optional<Out_cclsMember> BuildInitial(SymbolKind kind, Usr root_usr,
BuildInitial(SymbolKind kind, Usr root_usr, bool qualified, int levels, SymbolKind memberKind) { bool qualified, int levels,
SymbolKind memberKind) {
switch (kind) { switch (kind) {
default: default:
return {}; return {};
@ -241,7 +235,7 @@ struct Handler_CclsMember
if (!def) if (!def)
return {}; return {};
Out_CclsMember::Entry entry; Out_cclsMember entry;
// Not type, |id| is invalid. // Not type, |id| is invalid.
entry.name = def->Name(qualified); entry.name = def->Name(qualified);
if (def->spell) { if (def->spell) {
@ -261,7 +255,7 @@ struct Handler_CclsMember
if (!def) if (!def)
return {}; return {};
Out_CclsMember::Entry entry; Out_cclsMember entry;
entry.id = std::to_string(root_usr); entry.id = std::to_string(root_usr);
entry.usr = root_usr; entry.usr = root_usr;
if (def->spell) { if (def->spell) {
@ -275,24 +269,22 @@ struct Handler_CclsMember
} }
} }
void Run(In_CclsMember *request) override { void Run(In_cclsMember *request) override {
auto &params = request->params; auto &params = request->params;
Out_CclsMember out; std::optional<Out_cclsMember> result;
out.id = request->id;
if (params.id.size()) { if (params.id.size()) {
try { try {
params.usr = std::stoull(params.id); params.usr = std::stoull(params.id);
} catch (...) { } catch (...) {
return; return;
} }
Out_CclsMember::Entry entry; result.emplace();
entry.id = std::to_string(params.usr); result->id = std::to_string(params.usr);
entry.usr = params.usr; result->usr = params.usr;
// entry.name is empty as it is known by the client. // entry.name is empty as it is known by the client.
if (db->HasType(entry.usr) && if (!(db->HasType(params.usr) && Expand(this, &*result, params.qualified,
Expand(this, &entry, params.qualified, params.levels, params.kind)) params.levels, params.kind)))
out.result = std::move(entry); result.reset();
} else { } else {
QueryFile *file; QueryFile *file;
if (!FindFileOrFail(db, project, request->id, if (!FindFileOrFail(db, project, request->id,
@ -304,14 +296,14 @@ struct Handler_CclsMember
switch (sym.kind) { switch (sym.kind) {
case SymbolKind::Func: case SymbolKind::Func:
case SymbolKind::Type: case SymbolKind::Type:
out.result = BuildInitial(sym.kind, sym.usr, params.qualified, result = BuildInitial(sym.kind, sym.usr, params.qualified,
params.levels, params.kind); params.levels, params.kind);
break; break;
case SymbolKind::Var: { case SymbolKind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->GetVar(sym).AnyDef();
if (def && def->type) if (def && def->type)
out.result = BuildInitial(SymbolKind::Type, def->type, result = BuildInitial(SymbolKind::Type, def->type, params.qualified,
params.qualified, params.levels, params.kind); params.levels, params.kind);
break; break;
} }
default: default:
@ -321,17 +313,14 @@ struct Handler_CclsMember
} }
} }
if (params.hierarchy) { if (params.hierarchy)
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
return; 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 } // namespace

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,12 +36,6 @@ struct Cmd_xref {
}; };
MAKE_REFLECT_STRUCT(Cmd_xref, usr, kind, field); 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> template <typename T>
std::string ToString(T &v) { std::string ToString(T &v) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
@ -57,7 +51,7 @@ struct CommonCodeLensParams {
WorkingFile *wfile; WorkingFile *wfile;
}; };
struct In_TextDocumentCodeLens : public RequestInMessage { struct In_TextDocumentCodeLens : public RequestMessage {
MethodType GetMethodType() const override { return codeLens; } MethodType GetMethodType() const override { return codeLens; }
struct Params { struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
@ -67,20 +61,12 @@ MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens::Params, textDocument);
MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params); MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentCodeLens); 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 struct Handler_TextDocumentCodeLens
: BaseMessageHandler<In_TextDocumentCodeLens> { : BaseMessageHandler<In_TextDocumentCodeLens> {
MethodType GetMethodType() const override { return codeLens; } MethodType GetMethodType() const override { return codeLens; }
void Run(In_TextDocumentCodeLens *request) override { void Run(In_TextDocumentCodeLens *request) override {
auto &params = request->params; auto &params = request->params;
Out_TextDocumentCodeLens out; std::vector<lsCodeLens> result;
out.id = request->id;
std::string path = params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
QueryFile *file; QueryFile *file;
@ -95,7 +81,7 @@ struct Handler_TextDocumentCodeLens
std::optional<lsRange> range = GetLsRange(wfile, use.range); std::optional<lsRange> range = GetLsRange(wfile, use.range);
if (!range) if (!range)
return; return;
lsCodeLens &code_lens = out.result.emplace_back(); lsCodeLens &code_lens = result.emplace_back();
code_lens.range = *range; code_lens.range = *range;
code_lens.command = lsCommand(); code_lens.command = lsCommand();
code_lens.command->command = std::string(ccls_xref); code_lens.command->command = std::string(ccls_xref);
@ -166,12 +152,12 @@ struct Handler_TextDocumentCodeLens
}; };
} }
pipeline::WriteStdout(codeLens, out); pipeline::Reply(request->id, result);
} }
}; };
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens); REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens);
struct In_WorkspaceExecuteCommand : public RequestInMessage { struct In_WorkspaceExecuteCommand : public RequestMessage {
MethodType GetMethodType() const override { return executeCommand; } MethodType GetMethodType() const override { return executeCommand; }
lsCommand params; lsCommand params;
}; };
@ -191,12 +177,11 @@ struct Handler_WorkspaceExecuteCommand
if (params.command == ccls_xref) { if (params.command == ccls_xref) {
Cmd_xref cmd; Cmd_xref cmd;
Reflect(json_reader, cmd); Reflect(json_reader, cmd);
Out_xref out; std::vector<lsLocation> result;
out.id = request->id;
auto Map = [&](auto &&uses) { auto Map = [&](auto &&uses) {
for (auto &use : uses) for (auto &use : uses)
if (auto loc = GetLsLocation(db, working_files, use)) if (auto loc = GetLsLocation(db, working_files, use))
out.result.push_back(std::move(*loc)); result.push_back(std::move(*loc));
}; };
switch (cmd.kind) { switch (cmd.kind) {
case SymbolKind::Func: { case SymbolKind::Func: {
@ -235,7 +220,7 @@ struct Handler_WorkspaceExecuteCommand
default: default:
break; break;
} }
pipeline::WriteStdout(executeCommand, out); pipeline::Reply(request->id, result);
} }
} }
}; };

View File

@ -54,28 +54,21 @@ struct lsCompletionParams : lsTextDocumentPositionParams {
}; };
MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context); MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context);
struct In_TextDocumentComplete : public RequestInMessage { struct In_TextDocumentComplete : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
lsCompletionParams params; lsCompletionParams params;
}; };
MAKE_REFLECT_STRUCT(In_TextDocumentComplete, id, params); MAKE_REFLECT_STRUCT(In_TextDocumentComplete, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentComplete); REGISTER_IN_MESSAGE(In_TextDocumentComplete);
struct lsTextDocumentCompleteResult { struct lsCompletionList {
// This list it not complete. Further typing should result in recomputing // This list it not complete. Further typing should result in recomputing
// this list. // this list.
bool isIncomplete = false; bool isIncomplete = false;
// The completion items. // The completion items.
std::vector<lsCompletionItem> items; std::vector<lsCompletionItem> items;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentCompleteResult, isIncomplete, items); MAKE_REFLECT_STRUCT(lsCompletionList, isIncomplete, items);
struct Out_TextDocumentComplete
: public lsOutMessage<Out_TextDocumentComplete> {
lsRequestId id;
lsTextDocumentCompleteResult result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentComplete, jsonrpc, id, result);
void DecorateIncludePaths(const std::smatch &match, void DecorateIncludePaths(const std::smatch &match,
std::vector<lsCompletionItem> *items) { std::vector<lsCompletionItem> *items) {
@ -141,11 +134,11 @@ template <typename T> char *tofixedbase64(T input, char *out) {
// Pre-filters completion responses before sending to vscode. This results in a // Pre-filters completion responses before sending to vscode. This results in a
// significantly snappier completion experience as vscode is easily overloaded // significantly snappier completion experience as vscode is easily overloaded
// when given 1000+ completion items. // when given 1000+ completion items.
void FilterCandidates(Out_TextDocumentComplete *complete_response, void FilterCandidates(lsCompletionList &result,
const std::string &complete_text, lsPosition begin_pos, const std::string &complete_text, lsPosition begin_pos,
lsPosition end_pos, const std::string &buffer_line) { lsPosition end_pos, const std::string &buffer_line) {
assert(begin_pos.line == end_pos.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 // People usually does not want to insert snippets or parenthesis when
// changing function or type names, e.g. "str.|()" or "std::|<int>". // changing function or type names, e.g. "str.|()" or "std::|<int>".
@ -161,7 +154,7 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
int max_num = g_config->completion.maxNum; int max_num = g_config->completion.maxNum;
if (items.size() > max_num) { if (items.size() > max_num) {
items.resize(max_num); items.resize(max_num);
complete_response->result.isIncomplete = true; result.isIncomplete = true;
} }
for (auto &item : items) { for (auto &item : items) {
@ -485,13 +478,12 @@ struct Handler_TextDocumentCompletion
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache; static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
const auto &params = request->params; const auto &params = request->params;
Out_TextDocumentComplete out; lsCompletionList result;
out.id = request->id;
std::string path = params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
WorkingFile *file = working_files->GetFileByFilename(path); WorkingFile *file = working_files->GetFileByFilename(path);
if (!file) { if (!file) {
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
return; return;
} }
@ -536,7 +528,7 @@ struct Handler_TextDocumentCompletion
} }
if (did_fail_check) { if (did_fail_check) {
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
return; return;
} }
} }
@ -549,8 +541,7 @@ struct Handler_TextDocumentCompletion
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
if (preprocess.ok && preprocess.keyword.compare("include") == 0) { if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
Out_TextDocumentComplete out; lsCompletionList result;
out.id = request->id;
{ {
std::unique_lock<std::mutex> lock( std::unique_lock<std::mutex> lock(
include_complete->completion_items_mutex, std::defer_lock); include_complete->completion_items_mutex, std::defer_lock);
@ -559,14 +550,14 @@ struct Handler_TextDocumentCompletion
std::string quote = preprocess.match[5]; std::string quote = preprocess.match[5];
for (auto &item : include_complete->completion_items) for (auto &item : include_complete->completion_items)
if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\"")) if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
out.result.items.push_back(item); result.items.push_back(item);
} }
begin_pos.character = 0; begin_pos.character = 0;
end_pos.character = (int)buffer_line.size(); 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); buffer_line);
DecorateIncludePaths(preprocess.match, &out.result.items); DecorateIncludePaths(preprocess.match, &result.items);
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
} else { } else {
std::string path = params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
CompletionManager::OnComplete callback = CompletionManager::OnComplete callback =
@ -575,13 +566,12 @@ struct Handler_TextDocumentCompletion
if (!OptConsumer) if (!OptConsumer)
return; return;
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer); auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
Out_TextDocumentComplete out; lsCompletionList result;
out.id = id; result.items = Consumer->ls_items;
out.result.items = Consumer->ls_items;
FilterCandidates(&out, completion_text, begin_pos, end_pos, FilterCandidates(result, completion_text, begin_pos, end_pos,
buffer_line); buffer_line);
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(id, result);
if (!Consumer->from_cache) { if (!Consumer->from_cache) {
cache.WithLock([&]() { cache.WithLock([&]() {
cache.path = path; cache.path = path;

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);
namespace { namespace {
MethodType kMethodType = "textDocument/documentSymbol"; MethodType kMethodType = "textDocument/documentSymbol";
struct In_TextDocumentDocumentSymbol : public RequestInMessage { struct In_TextDocumentDocumentSymbol : public RequestMessage {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
struct Params { struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
@ -28,20 +28,6 @@ MAKE_REFLECT_STRUCT(In_TextDocumentDocumentSymbol::Params, textDocument, all,
MAKE_REFLECT_STRUCT(In_TextDocumentDocumentSymbol, id, params); MAKE_REFLECT_STRUCT(In_TextDocumentDocumentSymbol, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentDocumentSymbol); 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 { struct lsDocumentSymbol {
std::string name; std::string name;
std::string detail; std::string detail;
@ -57,13 +43,6 @@ void Reflect(Writer &vis, std::unique_ptr<lsDocumentSymbol> &v) {
Reflect(vis, *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> template <typename Def>
bool Ignore(const Def *def) { bool Ignore(const Def *def) {
return false; return false;
@ -95,15 +74,14 @@ struct Handler_TextDocumentDocumentSymbol
const auto &symbol2refcnt = const auto &symbol2refcnt =
params.all ? file->symbol2refcnt : file->outline2refcnt; params.all ? file->symbol2refcnt : file->outline2refcnt;
if (params.startLine >= 0) { if (params.startLine >= 0) {
Out_SimpleDocumentSymbol out; std::vector<lsRange> result;
out.id = request->id;
for (auto [sym, refcnt] : symbol2refcnt) for (auto [sym, refcnt] : symbol2refcnt)
if (refcnt > 0 && params.startLine <= sym.range.start.line && if (refcnt > 0 && params.startLine <= sym.range.start.line &&
sym.range.start.line <= params.endLine) sym.range.start.line <= params.endLine)
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) if (auto loc = GetLsLocation(db, working_files, sym, file_id))
out.result.push_back(loc->range); result.push_back(loc->range);
std::sort(out.result.begin(), out.result.end()); std::sort(result.begin(), result.end());
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
} else if (g_config->client.hierarchicalDocumentSymbolSupport) { } else if (g_config->client.hierarchicalDocumentSymbolSupport) {
std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds; std::unordered_map<SymbolIdx, std::unique_ptr<lsDocumentSymbol>> sym2ds;
std::vector<std::pair<const QueryFunc::Def *, lsDocumentSymbol *>> funcs; std::vector<std::pair<const QueryFunc::Def *, lsDocumentSymbol *>> funcs;
@ -195,15 +173,13 @@ struct Handler_TextDocumentDocumentSymbol
ds->children.push_back(std::move(it->second)); ds->children.push_back(std::move(it->second));
} }
} }
Out_HierarchicalDocumentSymbol out; std::vector<std::unique_ptr<lsDocumentSymbol>> result;
out.id = request->id;
for (auto &[_, ds] : sym2ds) for (auto &[_, ds] : sym2ds)
if (ds) if (ds)
out.result.push_back(std::move(ds)); result.push_back(std::move(ds));
pipeline::WriteStdout(kMethodType, out); pipeline::Reply(request->id, result);
} else { } else {
Out_TextDocumentDocumentSymbol out; std::vector<lsSymbolInformation> result;
out.id = request->id;
for (auto [sym, refcnt] : symbol2refcnt) { for (auto [sym, refcnt] : symbol2refcnt) {
if (refcnt <= 0) continue; if (refcnt <= 0) continue;
if (std::optional<lsSymbolInformation> info = if (std::optional<lsSymbolInformation> info =
@ -215,11 +191,11 @@ struct Handler_TextDocumentDocumentSymbol
continue; continue;
if (auto loc = GetLsLocation(db, working_files, sym, file_id)) { if (auto loc = GetLsLocation(db, working_files, sym, file_id)) {
info->location = *loc; info->location = *loc;
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); 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> llvm::Expected<tooling::Replacements>
FormatCode(std::string_view code, std::string_view file, tooling::Range Range) { FormatCode(std::string_view code, std::string_view file, tooling::Range Range) {
StringRef Code(code.data(), code.size()), File(file.data(), file.size()); StringRef Code(code.data(), code.size()), File(file.data(), file.size());
@ -91,20 +84,17 @@ void Format(WorkingFile *wfile, tooling::Range range, lsRequestId id) {
auto ReplsOrErr = auto ReplsOrErr =
FormatCode(code, wfile->filename, range); FormatCode(code, wfile->filename, range);
if (ReplsOrErr) { if (ReplsOrErr) {
Out_TextDocumentFormatting out; auto result = ReplacementsToEdits(code, *ReplsOrErr);
out.id = id; pipeline::Reply(id, result);
out.result = ReplacementsToEdits(code, *ReplsOrErr);
pipeline::WriteStdout(formatting, out);
} else { } else {
Out_Error err; lsResponseError err;
err.id = id; err.code = lsErrorCodes::UnknownErrorCode;
err.error.code = lsErrorCodes::UnknownErrorCode; err.message = llvm::toString(ReplsOrErr.takeError());
err.error.message = llvm::toString(ReplsOrErr.takeError()); pipeline::ReplyError(id, err);
pipeline::WriteStdout(kMethodType_Unknown, err);
} }
} }
struct In_TextDocumentFormatting : public RequestInMessage { struct In_TextDocumentFormatting : public RequestMessage {
MethodType GetMethodType() const override { return formatting; } MethodType GetMethodType() const override { return formatting; }
struct Params { struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
@ -132,7 +122,7 @@ struct Handler_TextDocumentFormatting
}; };
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting); REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting);
struct In_TextDocumentOnTypeFormatting : public RequestInMessage { struct In_TextDocumentOnTypeFormatting : public RequestMessage {
MethodType GetMethodType() const override { return onTypeFormatting; } MethodType GetMethodType() const override { return onTypeFormatting; }
struct Params { struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
@ -168,7 +158,7 @@ struct Handler_TextDocumentOnTypeFormatting
}; };
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting); REGISTER_MESSAGE_HANDLER(Handler_TextDocumentOnTypeFormatting);
struct In_TextDocumentRangeFormatting : public RequestInMessage { struct In_TextDocumentRangeFormatting : public RequestMessage {
MethodType GetMethodType() const override { return rangeFormatting; } MethodType GetMethodType() const override { return rangeFormatting; }
struct Params { struct Params {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;

View File

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

View File

@ -1,52 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "message_handler.h"
#include "pipeline.hh"
#include "query_utils.h"
using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,67 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#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

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

View File

@ -1,43 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#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,47 +0,0 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#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

@ -14,6 +14,9 @@
#include "platform.h" #include "platform.h"
#include "project.h" #include "project.h"
#include "query_utils.h" #include "query_utils.h"
#include "serializers/json.h"
#include <rapidjson/writer.h>
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
#include <llvm/Support/Timer.h> #include <llvm/Support/Timer.h>
@ -64,18 +67,13 @@ struct Index_Request {
int64_t ts = tick++; int64_t ts = tick++;
}; };
struct Stdout_Request {
MethodType method;
std::string content;
};
MultiQueueWaiter *main_waiter; MultiQueueWaiter *main_waiter;
MultiQueueWaiter *indexer_waiter; MultiQueueWaiter *indexer_waiter;
MultiQueueWaiter *stdout_waiter; MultiQueueWaiter *stdout_waiter;
ThreadedQueue<std::unique_ptr<InMessage>> *on_request; ThreadedQueue<std::unique_ptr<InMessage>> *on_request;
ThreadedQueue<Index_Request> *index_request; ThreadedQueue<Index_Request> *index_request;
ThreadedQueue<IndexUpdate> *on_indexed; ThreadedQueue<IndexUpdate> *on_indexed;
ThreadedQueue<Stdout_Request> *for_stdout; ThreadedQueue<std::string> *for_stdout;
struct InMemoryIndexFile { struct InMemoryIndexFile {
std::string content; std::string content;
@ -287,11 +285,10 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
if (!ok) { if (!ok) {
if (request.id.Valid()) { if (request.id.Valid()) {
Out_Error out; lsResponseError err;
out.id = request.id; err.code = lsErrorCodes::InternalError;
out.error.code = lsErrorCodes::InternalError; err.message = "failed to index " + path_to_index;
out.error.message = "Failed to index " + path_to_index; pipeline::ReplyError(request.id, err);
pipeline::WriteStdout(kMethodType_Unknown, out);
} }
return true; return true;
} }
@ -351,7 +348,7 @@ void Init() {
index_request = new ThreadedQueue<Index_Request>(indexer_waiter); index_request = new ThreadedQueue<Index_Request>(indexer_waiter);
stdout_waiter = new MultiQueueWaiter; 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, void Indexer_Main(CompletionManager *completion, VFS *vfs, Project *project,
@ -371,8 +368,8 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
std::string filename = LowerPathIfInsensitive(f->filename); std::string filename = LowerPathIfInsensitive(f->filename);
if (db->name2file_id.find(filename) == db->name2file_id.end()) if (db->name2file_id.find(filename) == db->name2file_id.end())
continue; continue;
QueryFile *file = &db->files[db->name2file_id[filename]]; QueryFile &file = db->files[db->name2file_id[filename]];
EmitSemanticHighlighting(db, f.get(), file); EmitSemanticHighlight(db, f.get(), file);
} }
return; return;
} }
@ -391,8 +388,9 @@ void Main_OnIndexed(DB *db, WorkingFiles *working_files, IndexUpdate *update) {
// request.path // request.path
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
: def_u.second); : def_u.second);
EmitSkippedRanges(wfile, def_u.first.skipped_ranges); QueryFile &file = db->files[update->file_id];
EmitSemanticHighlighting(db, wfile, &db->files[update->file_id]); EmitSkippedRanges(wfile, file);
EmitSemanticHighlight(db, wfile, file);
} }
} }
} }
@ -402,21 +400,20 @@ void LaunchStdin() {
set_thread_name("stdin"); set_thread_name("stdin");
while (true) { while (true) {
std::unique_ptr<InMessage> message; std::unique_ptr<InMessage> message;
std::optional<std::string> err = std::optional<std::string> error =
MessageRegistry::instance()->ReadMessageFromStdin(&message); MessageRegistry::instance()->ReadMessageFromStdin(&message);
// Message parsing can fail if we don't recognize the method. // Message parsing can fail if we don't recognize the method.
if (err) { if (error) {
// The message may be partially deserialized. // The message may be partially deserialized.
// Emit an error ResponseMessage if |id| is available. // Emit an error ResponseMessage if |id| is available.
if (message) { if (message) {
lsRequestId id = message->GetRequestId(); lsRequestId id = message->GetRequestId();
if (id.Valid()) { if (id.Valid()) {
Out_Error out; lsResponseError err;
out.id = id; err.code = lsErrorCodes::InvalidParams;
out.error.code = lsErrorCodes::InvalidParams; err.message = std::move(*error);
out.error.message = std::move(*err); ReplyError(id, err);
WriteStdout(kMethodType_Unknown, out);
} }
} }
continue; continue;
@ -441,20 +438,12 @@ void LaunchStdout() {
set_thread_name("stdout"); set_thread_name("stdout");
while (true) { while (true) {
std::vector<Stdout_Request> messages = for_stdout->DequeueAll(); std::vector<std::string> messages = for_stdout->DequeueAll();
if (messages.empty()) { for (auto &s : messages) {
stdout_waiter->Wait(for_stdout); llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
continue; llvm::outs().flush();
}
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
} }
stdout_waiter->Wait(for_stdout);
} }
}) })
.detach(); .detach();
@ -468,20 +457,17 @@ void MainLoop() {
CompletionManager clang_complete( CompletionManager clang_complete(
&project, &working_files, &project, &working_files,
[&](std::string path, std::vector<lsDiagnostic> diagnostics) { [&](std::string path, std::vector<lsDiagnostic> diagnostics) {
Out_TextDocumentPublishDiagnostics out; lsPublishDiagnosticsParams params;
out.params.uri = lsDocumentUri::FromPath(path); params.uri = lsDocumentUri::FromPath(path);
out.params.diagnostics = diagnostics; params.diagnostics = diagnostics;
ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics, Notify("textDocument/publishDiagnostics", params);
out);
}, },
[](lsRequestId id) { [](lsRequestId id) {
if (id.Valid()) { if (id.Valid()) {
Out_Error out; lsResponseError err;
out.id = id; err.code = lsErrorCodes::InternalError;
out.error.code = lsErrorCodes::InternalError; err.message = "drop older completion request";
out.error.message = "Dropping completion request; a newer request " ReplyError(id, err);
"has come in that will be serviced instead.";
pipeline::WriteStdout(kMethodType_Unknown, out);
} }
}); });
@ -554,14 +540,54 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
return ReadContent(GetCachePath(path)); return ReadContent(GetCachePath(path));
} }
void WriteStdout(MethodType method, lsBaseOutMessage &response) { void Notify(const char *method, const std::function<void(Writer &)> &fn) {
std::ostringstream sstream; rapidjson::StringBuffer output;
response.Write(sstream); 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; static void Reply(lsRequestId id, const char *key,
out.content = sstream.str(); const std::function<void(Writer &)> &fn) {
out.method = method; rapidjson::StringBuffer output;
for_stdout->PushBack(std::move(out)); 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 } // namespace ccls::pipeline

View File

@ -3,8 +3,7 @@
#pragma once #pragma once
#include "lsp_diagnostic.h" #include "lsp.h"
#include "method.h"
#include "query.h" #include "query.h"
#include <atomic> #include <atomic>
@ -18,7 +17,6 @@ struct GroupMatch;
struct VFS; struct VFS;
struct Project; struct Project;
struct WorkingFiles; struct WorkingFiles;
struct lsBaseOutMessage;
struct VFS { struct VFS {
struct State { struct State {
@ -55,6 +53,20 @@ void Index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, lsRequestId id = {}); IndexMode mode, lsRequestId id = {});
std::optional<std::string> LoadIndexedContent(const std::string& path); 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 pipeline
} // namespace ccls } // namespace ccls

View File

@ -5,7 +5,6 @@
#include "config.h" #include "config.h"
#include "lsp.h" #include "lsp.h"
#include "method.h"
#include <functional> #include <functional>
#include <mutex> #include <mutex>

View File

@ -20,7 +20,13 @@ using namespace llvm;
bool gTestOutputMode = false; 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(Reader &visitor, uint8_t &value) { value = visitor.GetUInt8(); }
void Reflect(Writer &visitor, uint8_t &value) { visitor.UInt8(value); } void Reflect(Writer &visitor, uint8_t &value) { visitor.UInt8(value); }

View File

@ -26,11 +26,10 @@ class StringRef;
enum class SerializeFormat { Binary, Json }; enum class SerializeFormat { Binary, Json };
struct JsonNull {}; struct JsonNull {};
struct mandatory_optional_tag {};
class Reader { class Reader {
public: public:
virtual ~Reader() {} virtual ~Reader();
virtual SerializeFormat Format() const = 0; virtual SerializeFormat Format() const = 0;
virtual bool IsBool() = 0; virtual bool IsBool() = 0;
@ -60,7 +59,7 @@ public:
class Writer { class Writer {
public: public:
virtual ~Writer() {} virtual ~Writer();
virtual SerializeFormat Format() const = 0; virtual SerializeFormat Format() const = 0;
virtual void Null() = 0; virtual void Null() = 0;
@ -85,8 +84,6 @@ struct IndexFile;
#define REFLECT_MEMBER_START() ReflectMemberStart(visitor) #define REFLECT_MEMBER_START() ReflectMemberStart(visitor)
#define REFLECT_MEMBER_END() ReflectMemberEnd(visitor); #define REFLECT_MEMBER_END() ReflectMemberEnd(visitor);
#define REFLECT_MEMBER(name) ReflectMember(visitor, #name, value.name) #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 REFLECT_MEMBER2(name, value) ReflectMember(visitor, name, value)
#define MAKE_REFLECT_TYPE_PROXY(type_name) \ #define MAKE_REFLECT_TYPE_PROXY(type_name) \
@ -103,8 +100,6 @@ struct IndexFile;
} }
#define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name); #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, ...) \ #define MAKE_REFLECT_EMPTY_STRUCT(type, ...) \
template <typename TVisitor> void Reflect(TVisitor &visitor, type &value) { \ template <typename TVisitor> void Reflect(TVisitor &visitor, type &value) { \
@ -119,13 +114,6 @@ struct IndexFile;
REFLECT_MEMBER_END(); \ 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 // clang-format off
// Config has many fields, we need to support at least its number of fields. // 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 #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
@ -256,13 +244,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> template <typename L, typename R>
void Reflect(Reader &vis, std::pair<L, R> &v) { void Reflect(Reader &vis, std::pair<L, R> &v) {
vis.Member("L", [&]() { Reflect(vis, v.first); }); vis.Member("L", [&]() { Reflect(vis, v.first); });

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include "lsp_diagnostic.h" #include "lsp.h"
#include "utils.h" #include "utils.h"
#include <mutex> #include <mutex>