mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-27 10:02:03 +00:00
Remove variant and clean up
This commit is contained in:
parent
4d519dcbcb
commit
fa9df5bcef
@ -656,18 +656,18 @@ ClangCompleteManager::ClangCompleteManager(Project* project,
|
||||
on_dropped_(on_dropped),
|
||||
preloaded_sessions_(kMaxPreloadedSessions),
|
||||
completion_sessions_(kMaxCompletionSessions) {
|
||||
new std::thread([&]() {
|
||||
std::thread([&]() {
|
||||
SetThreadName("comp-query");
|
||||
CompletionQueryMain(this);
|
||||
});
|
||||
new std::thread([&]() {
|
||||
}).detach();
|
||||
std::thread([&]() {
|
||||
SetThreadName("comp-preload");
|
||||
CompletionPreloadMain(this);
|
||||
});
|
||||
new std::thread([&]() {
|
||||
}).detach();
|
||||
std::thread([&]() {
|
||||
SetThreadName("diag-query");
|
||||
DiagnosticQueryMain(this);
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void ClangCompleteManager::CodeComplete(
|
||||
@ -680,7 +680,6 @@ void ClangCompleteManager::CodeComplete(
|
||||
}
|
||||
|
||||
void ClangCompleteManager::DiagnosticsUpdate(
|
||||
const lsRequestId& id,
|
||||
const lsTextDocumentIdentifier& document) {
|
||||
bool has = false;
|
||||
diagnostic_request_.Iterate([&](const DiagnosticRequest& request) {
|
||||
|
@ -91,8 +91,7 @@ struct ClangCompleteManager {
|
||||
const lsTextDocumentPositionParams& completion_location,
|
||||
const OnComplete& on_complete);
|
||||
// Request a diagnostics update.
|
||||
void DiagnosticsUpdate(const lsRequestId& request_id,
|
||||
const lsTextDocumentIdentifier& document);
|
||||
void DiagnosticsUpdate(const lsTextDocumentIdentifier& document);
|
||||
|
||||
// Notify the completion manager that |filename| has been viewed and we
|
||||
// should begin preloading completion data.
|
||||
|
@ -172,7 +172,7 @@ void RunQueryDbThread(const std::string& bin_name,
|
||||
args);
|
||||
},
|
||||
[](lsRequestId id) {
|
||||
if (!std::holds_alternative<std::monostate>(id)) {
|
||||
if (id.Valid()) {
|
||||
Out_Error out;
|
||||
out.id = id;
|
||||
out.error.code = lsErrorCodes::InternalError;
|
||||
@ -243,7 +243,7 @@ void RunQueryDbThread(const std::string& bin_name,
|
||||
//
|
||||
// |ipc| is connected to a server.
|
||||
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
new std::thread([request_times]() {
|
||||
std::thread([request_times]() {
|
||||
SetThreadName("stdin");
|
||||
auto* queue = QueueManager::instance();
|
||||
while (true) {
|
||||
@ -257,7 +257,7 @@ void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
// Emit an error ResponseMessage if |id| is available.
|
||||
if (message) {
|
||||
lsRequestId id = message->GetRequestId();
|
||||
if (!std::holds_alternative<std::monostate>(id)) {
|
||||
if (id.Valid()) {
|
||||
Out_Error out;
|
||||
out.id = id;
|
||||
out.error.code = lsErrorCodes::InvalidParams;
|
||||
@ -279,12 +279,12 @@ void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
if (method_type == kMethodType_Exit)
|
||||
break;
|
||||
}
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
|
||||
MultiQueueWaiter* waiter) {
|
||||
new std::thread([=]() {
|
||||
std::thread([=]() {
|
||||
SetThreadName("stdout");
|
||||
auto* queue = QueueManager::instance();
|
||||
|
||||
@ -305,7 +305,7 @@ void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void LanguageServerMain(const std::string& bin_name,
|
||||
|
@ -393,8 +393,7 @@ void ParseFile(DiagnosticsEngine* diag_engine,
|
||||
file_contents, &perf);
|
||||
|
||||
if (indexes.empty()) {
|
||||
if (g_config->index.enabled &&
|
||||
!std::holds_alternative<std::monostate>(request.id)) {
|
||||
if (g_config->index.enabled && request.id.Valid()) {
|
||||
Out_Error out;
|
||||
out.id = request.id;
|
||||
out.error.code = lsErrorCodes::InternalError;
|
||||
|
@ -29,21 +29,16 @@ std::string ElideLongPath(const std::string& path) {
|
||||
|
||||
size_t TrimCommonPathPrefix(const std::string& result,
|
||||
const std::string& trimmer) {
|
||||
size_t i = 0;
|
||||
while (i < result.size() && i < trimmer.size()) {
|
||||
char a = result[i];
|
||||
char b = trimmer[i];
|
||||
#if defined(_WIN32)
|
||||
a = (char)tolower(a);
|
||||
b = (char)tolower(b);
|
||||
#ifdef _WIN32
|
||||
std::string s = result, t = trimmer;
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
|
||||
if (s.compare(0, t.size(), t) == 0)
|
||||
return t.size();
|
||||
#else
|
||||
if (result.compare(0, trimmer.size(), trimmer) == 0)
|
||||
return trimmer.size();
|
||||
#endif
|
||||
if (a != b)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i == trimmer.size())
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -51,21 +46,14 @@ size_t TrimCommonPathPrefix(const std::string& result,
|
||||
bool TrimPath(Project* project,
|
||||
const std::string& project_root,
|
||||
std::string* insert_path) {
|
||||
size_t start = 0;
|
||||
size_t start = TrimCommonPathPrefix(*insert_path, project_root);
|
||||
bool angle = false;
|
||||
|
||||
size_t len = TrimCommonPathPrefix(*insert_path, project_root);
|
||||
if (len > start)
|
||||
start = len;
|
||||
|
||||
for (auto& include_dir : project->quote_include_directories) {
|
||||
len = TrimCommonPathPrefix(*insert_path, include_dir);
|
||||
if (len > start)
|
||||
start = len;
|
||||
}
|
||||
for (auto& include_dir : project->quote_include_directories)
|
||||
start = std::max(start, TrimCommonPathPrefix(*insert_path, include_dir));
|
||||
|
||||
for (auto& include_dir : project->angle_include_directories) {
|
||||
len = TrimCommonPathPrefix(*insert_path, include_dir);
|
||||
auto len = TrimCommonPathPrefix(*insert_path, include_dir);
|
||||
if (len > start) {
|
||||
start = len;
|
||||
angle = true;
|
||||
@ -115,8 +103,8 @@ void IncludeComplete::Rescan() {
|
||||
g_config->completion.includeBlacklist);
|
||||
|
||||
is_scanning = true;
|
||||
new std::thread([this]() {
|
||||
SetThreadName("scan_includes");
|
||||
std::thread([this]() {
|
||||
SetThreadName("scan_includes");
|
||||
Timer timer;
|
||||
|
||||
InsertStlIncludes();
|
||||
@ -129,7 +117,7 @@ void IncludeComplete::Rescan() {
|
||||
|
||||
timer.ResetAndPrint("[perf] Scanning for includes");
|
||||
is_scanning = false;
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void IncludeComplete::InsertCompletionItem(const std::string& absolute_path,
|
||||
|
13
src/lsp.cc
13
src/lsp.cc
@ -275,6 +275,19 @@ bool lsTextEdit::operator==(const lsTextEdit& that) {
|
||||
return range == that.range && newText == that.newText;
|
||||
}
|
||||
|
||||
void Reflect(Writer& visitor, lsMarkedString& value) {
|
||||
// If there is a language, emit a `{language:string, value:string}` object. If
|
||||
// not, emit a string.
|
||||
if (value.language) {
|
||||
REFLECT_MEMBER_START();
|
||||
REFLECT_MEMBER(language);
|
||||
REFLECT_MEMBER(value);
|
||||
REFLECT_MEMBER_END();
|
||||
} else {
|
||||
Reflect(visitor, value.value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Out_ShowLogMessage::method() {
|
||||
if (display_type == DisplayType::Log)
|
||||
return "window/logMessage";
|
||||
|
11
src/lsp.h
11
src/lsp.h
@ -239,7 +239,7 @@ MAKE_REFLECT_STRUCT(lsTextDocumentIdentifier, uri);
|
||||
struct lsVersionedTextDocumentIdentifier {
|
||||
lsDocumentUri uri;
|
||||
// The version number of this document. number | null
|
||||
std::variant<std::monostate, int> version;
|
||||
std::optional<int> version;
|
||||
|
||||
lsTextDocumentIdentifier AsTextDocumentIdentifier() const;
|
||||
};
|
||||
@ -325,12 +325,11 @@ MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces);
|
||||
//
|
||||
// Note that markdown strings will be sanitized - that means html will be
|
||||
// escaped.
|
||||
struct lsMarkedString1 {
|
||||
std::string_view language;
|
||||
std::string_view value;
|
||||
struct lsMarkedString {
|
||||
std::optional<std::string> language;
|
||||
std::string value;
|
||||
};
|
||||
using lsMarkedString = std::variant<std::string_view, lsMarkedString1>;
|
||||
MAKE_REFLECT_STRUCT(lsMarkedString1, language, value);
|
||||
void Reflect(Writer& visitor, lsMarkedString& value);
|
||||
|
||||
struct lsTextDocumentContentChangeEvent {
|
||||
// The range of the document that changed.
|
||||
|
@ -87,7 +87,7 @@ struct Handler_CclsFreshenIndex : BaseMessageHandler<In_CclsFreshenIndex> {
|
||||
|
||||
Timer time;
|
||||
// Send index requests for every file.
|
||||
project->Index(QueueManager::instance(), working_files, std::monostate());
|
||||
project->Index(QueueManager::instance(), working_files, lsRequestId());
|
||||
time.ResetAndPrint("[perf] Dispatched $ccls/freshenIndex index requests");
|
||||
}
|
||||
};
|
||||
|
@ -518,12 +518,12 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
|
||||
}
|
||||
LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers";
|
||||
for (int i = 0; i < g_config->index.threads; i++) {
|
||||
new std::thread([=]() {
|
||||
std::thread([=]() {
|
||||
SetThreadName("indexer" + std::to_string(i));
|
||||
Indexer_Main(diag_engine, file_consumer_shared, timestamp_manager,
|
||||
import_manager, import_pipeline_status, project,
|
||||
working_files, waiter);
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
// Start scanning include directories before dispatching project
|
||||
|
@ -11,8 +11,8 @@ MAKE_REFLECT_STRUCT(In_Shutdown, id);
|
||||
REGISTER_IN_MESSAGE(In_Shutdown);
|
||||
|
||||
struct Out_Shutdown : public lsOutMessage<Out_Shutdown> {
|
||||
lsRequestId id; // defaults to std::monostate (null)
|
||||
std::monostate result; // null
|
||||
lsRequestId id;
|
||||
JsonNull result;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(Out_Shutdown, jsonrpc, id, result);
|
||||
|
||||
|
@ -39,7 +39,6 @@ struct Handler_TextDocumentDidChange
|
||||
}
|
||||
clang_complete->NotifyEdit(path);
|
||||
clang_complete->DiagnosticsUpdate(
|
||||
std::monostate(),
|
||||
request->params.textDocument.AsTextDocumentIdentifier());
|
||||
}
|
||||
};
|
||||
|
@ -5,41 +5,40 @@
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/hover";
|
||||
|
||||
std::pair<std::string_view, std::string_view> GetCommentsAndHover(
|
||||
QueryDatabase* db,
|
||||
SymbolRef sym) {
|
||||
switch (sym.kind) {
|
||||
case SymbolKind::Type: {
|
||||
if (const auto* def = db->GetType(sym).AnyDef()) {
|
||||
return {def->comments, !def->hover.empty()
|
||||
? std::string_view(def->hover)
|
||||
: std::string_view(def->detailed_name)};
|
||||
// Find the comments for |sym|, if any.
|
||||
std::optional<lsMarkedString> GetComments(QueryDatabase* db, SymbolRef sym) {
|
||||
std::optional<lsMarkedString> ret;
|
||||
WithEntity(db, sym, [&](const auto& entity) {
|
||||
if (const auto* def = entity.AnyDef())
|
||||
if (!def->comments.empty()) {
|
||||
lsMarkedString m;
|
||||
m.value = def->comments;
|
||||
ret = m;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Func: {
|
||||
if (const auto* def = db->GetFunc(sym).AnyDef()) {
|
||||
return {def->comments, !def->hover.empty()
|
||||
? std::string_view(def->hover)
|
||||
: std::string_view(def->detailed_name)};
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Returns the hover or detailed name for `sym`, if any.
|
||||
std::optional<lsMarkedString> GetHoverOrName(QueryDatabase* db,
|
||||
const std::string& language,
|
||||
SymbolRef sym) {
|
||||
|
||||
std::optional<lsMarkedString> ret;
|
||||
WithEntity(db, sym, [&](const auto& entity) {
|
||||
if (const auto* def = entity.AnyDef()) {
|
||||
lsMarkedString m;
|
||||
m.language = language;
|
||||
if (!def->hover.empty()) {
|
||||
m.value = def->hover;
|
||||
ret = m;
|
||||
} else if (!def->detailed_name.empty()) {
|
||||
m.value = def->detailed_name;
|
||||
ret = m;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SymbolKind::Var: {
|
||||
if (const auto* def = db->GetVar(sym).AnyDef()) {
|
||||
return {def->comments, !def->hover.empty()
|
||||
? std::string_view(def->hover)
|
||||
: std::string_view(def->detailed_name)};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SymbolKind::File:
|
||||
case SymbolKind::Invalid: {
|
||||
assert(false && "unexpected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {"", ""};
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct In_TextDocumentHover : public RequestInMessage {
|
||||
@ -66,7 +65,7 @@ void Reflect(Writer& visitor, Out_TextDocumentHover& value) {
|
||||
if (value.result)
|
||||
REFLECT_MEMBER(result);
|
||||
else {
|
||||
// Empty std::optional<> is elided by the default serializer, we need to write
|
||||
// Empty optional<> is elided by the default serializer, we need to write
|
||||
// |null| to be compliant with the LSP.
|
||||
visitor.Key("result");
|
||||
visitor.Null();
|
||||
@ -77,11 +76,11 @@ void Reflect(Writer& visitor, Out_TextDocumentHover& value) {
|
||||
struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
void Run(In_TextDocumentHover* request) override {
|
||||
auto& params = request->params;
|
||||
QueryFile* file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file)) {
|
||||
params.textDocument.uri.GetPath(), &file))
|
||||
return;
|
||||
}
|
||||
|
||||
WorkingFile* working_file =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
@ -90,25 +89,23 @@ struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
|
||||
out.id = request->id;
|
||||
|
||||
for (SymbolRef sym :
|
||||
FindSymbolsAtLocation(working_file, file, request->params.position)) {
|
||||
FindSymbolsAtLocation(working_file, file, params.position)) {
|
||||
// Found symbol. Return hover.
|
||||
std::optional<lsRange> ls_range = GetLsRange(
|
||||
working_files->GetFileByFilename(file->def->path), sym.range);
|
||||
if (!ls_range)
|
||||
continue;
|
||||
|
||||
std::pair<std::string_view, std::string_view> comments_hover =
|
||||
GetCommentsAndHover(db, sym);
|
||||
if (comments_hover.first.size() || comments_hover.second.size()) {
|
||||
std::optional<lsMarkedString> comments = GetComments(db, sym);
|
||||
std::optional<lsMarkedString> hover =
|
||||
GetHoverOrName(db, file->def->language, sym);
|
||||
if (comments || hover) {
|
||||
out.result = Out_TextDocumentHover::Result();
|
||||
if (comments_hover.first.size()) {
|
||||
out.result->contents.emplace_back(comments_hover.first);
|
||||
}
|
||||
if (comments_hover.second.size()) {
|
||||
out.result->contents.emplace_back(lsMarkedString1{
|
||||
std::string_view(file->def->language), comments_hover.second});
|
||||
}
|
||||
out.result->range = *ls_range;
|
||||
if (comments)
|
||||
out.result->contents.push_back(*comments);
|
||||
if (hover)
|
||||
out.result->contents.push_back(*hover);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ struct Handler_WorkspaceDidChangeConfiguration
|
||||
std::to_string(project->entries.size()) + " files)");
|
||||
|
||||
time.Reset();
|
||||
project->Index(QueueManager::instance(), working_files, std::monostate());
|
||||
project->Index(QueueManager::instance(), working_files, lsRequestId());
|
||||
time.ResetAndPrint(
|
||||
"[perf] Dispatched workspace/didChangeConfiguration index requests");
|
||||
|
||||
|
@ -2,6 +2,40 @@
|
||||
|
||||
MethodType kMethodType_Unknown = "$unknown";
|
||||
MethodType kMethodType_Exit = "exit";
|
||||
MethodType kMethodType_TextDocumentPublishDiagnostics = "textDocument/publishDiagnostics";
|
||||
MethodType kMethodType_CclsPublishInactiveRegions = "$ccls/publishInactiveRegions";
|
||||
MethodType kMethodType_CclsPublishSemanticHighlighting = "$ccls/publishSemanticHighlighting";
|
||||
MethodType kMethodType_TextDocumentPublishDiagnostics =
|
||||
"textDocument/publishDiagnostics";
|
||||
MethodType kMethodType_CclsPublishInactiveRegions =
|
||||
"$ccls/publishInactiveRegions";
|
||||
MethodType kMethodType_CclsPublishSemanticHighlighting =
|
||||
"$ccls/publishSemanticHighlighting";
|
||||
|
||||
void Reflect(Reader& visitor, lsRequestId& value) {
|
||||
if (visitor.IsInt64()) {
|
||||
value.type = lsRequestId::kInt;
|
||||
value.value = 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().c_str());
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
15
src/method.h
15
src/method.h
@ -12,7 +12,18 @@ extern MethodType kMethodType_TextDocumentPublishDiagnostics;
|
||||
extern MethodType kMethodType_CclsPublishInactiveRegions;
|
||||
extern MethodType kMethodType_CclsPublishSemanticHighlighting;
|
||||
|
||||
using lsRequestId = std::variant<std::monostate, int64_t, std::string>;
|
||||
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;
|
||||
@ -32,6 +43,6 @@ struct RequestInMessage : public InMessage {
|
||||
// NotificationInMessage does not have |id|.
|
||||
struct NotificationInMessage : public InMessage {
|
||||
lsRequestId GetRequestId() const override {
|
||||
return std::monostate();
|
||||
return lsRequestId();
|
||||
}
|
||||
};
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class QueueManager;
|
||||
|
@ -140,6 +140,15 @@ void Reflect(Writer& visitor, NtString& value) {
|
||||
visitor.String(s ? s : "");
|
||||
}
|
||||
|
||||
void Reflect(Reader& visitor, JsonNull& value) {
|
||||
assert(visitor.Format() == SerializeFormat::Json);
|
||||
visitor.GetNull();
|
||||
}
|
||||
|
||||
void Reflect(Writer& visitor, JsonNull& value) {
|
||||
visitor.Null();
|
||||
}
|
||||
|
||||
// TODO: Move this to indexer.cc
|
||||
void Reflect(Reader& visitor, IndexInclude& value) {
|
||||
REFLECT_MEMBER_START();
|
||||
@ -299,15 +308,6 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
|
||||
REFLECT_MEMBER_END();
|
||||
}
|
||||
|
||||
void Reflect(Reader& visitor, std::monostate&) {
|
||||
assert(visitor.Format() == SerializeFormat::Json);
|
||||
visitor.GetNull();
|
||||
}
|
||||
|
||||
void Reflect(Writer& visitor, std::monostate&) {
|
||||
visitor.Null();
|
||||
}
|
||||
|
||||
void Reflect(Reader& visitor, SerializeFormat& value) {
|
||||
std::string fmt = visitor.GetString();
|
||||
value = fmt[0] == 'b' ? SerializeFormat::Binary : SerializeFormat::Json;
|
||||
|
@ -13,11 +13,12 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
enum class SerializeFormat { Binary, Json };
|
||||
|
||||
struct JsonNull {};
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
virtual ~Reader() {}
|
||||
@ -168,20 +169,18 @@ void Reflect(Writer& visitor, std::string_view& view);
|
||||
void Reflect(Reader& visitor, NtString& value);
|
||||
void Reflect(Writer& visitor, NtString& value);
|
||||
|
||||
// std::monostate is used to represent JSON null
|
||||
void Reflect(Reader& visitor, std::monostate&);
|
||||
void Reflect(Writer& visitor, std::monostate&);
|
||||
void Reflect(Reader& visitor, JsonNull& value);
|
||||
void Reflect(Writer& visitor, JsonNull& value);
|
||||
|
||||
void Reflect(Reader& visitor, SerializeFormat& value);
|
||||
void Reflect(Writer& visitor, SerializeFormat& value);
|
||||
|
||||
//// Type constructors
|
||||
|
||||
// ReflectMember std::optional<T> is used to represent TypeScript std::optional properties
|
||||
// ReflectMember std::optional<T> is used to represent TypeScript optional properties
|
||||
// (in `key: value` context).
|
||||
// Reflect std::optional<T> is used for a different purpose, whether an object is
|
||||
// nullable (possibly in `value` context). For the nullable semantics,
|
||||
// std::variant<std::monostate, T> is recommended.
|
||||
// nullable (possibly in `value` context).
|
||||
template <typename T>
|
||||
void Reflect(Reader& visitor, std::optional<T>& value) {
|
||||
if (visitor.IsNull()) {
|
||||
@ -243,57 +242,6 @@ void ReflectMember(Writer& visitor, const char* name, Maybe<T>& value) {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper struct to reflect std::variant
|
||||
template <size_t N, typename... Ts>
|
||||
struct ReflectVariant {
|
||||
// If T appears in Ts..., we should set the value of std::variant<Ts...> to
|
||||
// what we get from Reader.
|
||||
template <typename T>
|
||||
void ReflectTag(Reader& visitor, std::variant<Ts...>& value) {
|
||||
if constexpr (std::disjunction_v<std::is_same<T, Ts>...>) {
|
||||
T a;
|
||||
Reflect(visitor, a);
|
||||
value = std::move(a);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(Reader& visitor, std::variant<Ts...>& value) {
|
||||
// Based on tag dispatch, call different ReflectTag helper.
|
||||
if (visitor.IsNull())
|
||||
ReflectTag<std::monostate>(visitor, value);
|
||||
// It is possible that IsInt64() && IsInt(). We don't call ReflectTag<int>
|
||||
// if int is not in Ts...
|
||||
else if (std::disjunction_v<std::is_same<int, Ts>...> && visitor.IsInt())
|
||||
ReflectTag<int>(visitor, value);
|
||||
else if (visitor.IsInt64())
|
||||
ReflectTag<int64_t>(visitor, value);
|
||||
else if (visitor.IsString())
|
||||
ReflectTag<std::string>(visitor, value);
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// Check which type the variant contains and call corresponding Reflect.
|
||||
void operator()(Writer& visitor, std::variant<Ts...>& value) {
|
||||
if (value.index() == N - 1)
|
||||
Reflect(visitor, std::get<N - 1>(value));
|
||||
else
|
||||
ReflectVariant<N - 1, Ts...>()(visitor, value);
|
||||
}
|
||||
};
|
||||
|
||||
// Writer reflection on std::variant recurses. This is induction basis.
|
||||
template <typename... Ts>
|
||||
struct ReflectVariant<0, Ts...> {
|
||||
void operator()(Writer& visitor, std::variant<Ts...>& value) {}
|
||||
};
|
||||
|
||||
// std::variant
|
||||
template <typename TVisitor, typename... Ts>
|
||||
void Reflect(TVisitor& visitor, std::variant<Ts...>& value) {
|
||||
ReflectVariant<sizeof...(Ts), Ts...>()(visitor, value);
|
||||
}
|
||||
|
||||
// std::vector
|
||||
template <typename T>
|
||||
void Reflect(Reader& visitor, std::vector<T>& values) {
|
||||
|
@ -24,19 +24,15 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
|
||||
if (offset >= content.size())
|
||||
offset = (int)content.size() - 1;
|
||||
|
||||
lsPosition result;
|
||||
int line = 0, col = 0;
|
||||
int i = 0;
|
||||
while (i < offset) {
|
||||
if (content[i] == '\n') {
|
||||
result.line += 1;
|
||||
result.character = 0;
|
||||
} else {
|
||||
result.character += 1;
|
||||
}
|
||||
++i;
|
||||
for (; i < offset; i++) {
|
||||
if (content[i] == '\n')
|
||||
line++, col = 0;
|
||||
else
|
||||
col++;
|
||||
}
|
||||
|
||||
return result;
|
||||
return {line, col};
|
||||
}
|
||||
|
||||
std::vector<std::string> ToLines(const std::string& content) {
|
||||
@ -511,8 +507,8 @@ void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams& change) {
|
||||
}
|
||||
|
||||
// version: number | null
|
||||
if (std::holds_alternative<int>(change.textDocument.version))
|
||||
file->version = std::get<int>(change.textDocument.version);
|
||||
if (change.textDocument.version)
|
||||
file->version = *change.textDocument.version;
|
||||
|
||||
for (const lsTextDocumentContentChangeEvent& diff : change.contentChanges) {
|
||||
// Per the spec replace everything if the rangeLength and range are not set.
|
||||
|
Loading…
Reference in New Issue
Block a user