Remove variant and clean up

This commit is contained in:
Fangrui Song 2018-04-16 12:36:02 -07:00
parent 12ad568159
commit ebaf168e16
19 changed files with 171 additions and 190 deletions

View File

@ -656,18 +656,18 @@ ClangCompleteManager::ClangCompleteManager(Project* project,
on_dropped_(on_dropped), on_dropped_(on_dropped),
preloaded_sessions_(kMaxPreloadedSessions), preloaded_sessions_(kMaxPreloadedSessions),
completion_sessions_(kMaxCompletionSessions) { completion_sessions_(kMaxCompletionSessions) {
new std::thread([&]() { std::thread([&]() {
SetThreadName("comp-query"); SetThreadName("comp-query");
CompletionQueryMain(this); CompletionQueryMain(this);
}); }).detach();
new std::thread([&]() { std::thread([&]() {
SetThreadName("comp-preload"); SetThreadName("comp-preload");
CompletionPreloadMain(this); CompletionPreloadMain(this);
}); }).detach();
new std::thread([&]() { std::thread([&]() {
SetThreadName("diag-query"); SetThreadName("diag-query");
DiagnosticQueryMain(this); DiagnosticQueryMain(this);
}); }).detach();
} }
void ClangCompleteManager::CodeComplete( void ClangCompleteManager::CodeComplete(
@ -680,7 +680,6 @@ void ClangCompleteManager::CodeComplete(
} }
void ClangCompleteManager::DiagnosticsUpdate( void ClangCompleteManager::DiagnosticsUpdate(
const lsRequestId& id,
const lsTextDocumentIdentifier& document) { const lsTextDocumentIdentifier& document) {
bool has = false; bool has = false;
diagnostic_request_.Iterate([&](const DiagnosticRequest& request) { diagnostic_request_.Iterate([&](const DiagnosticRequest& request) {

View File

@ -91,8 +91,7 @@ struct ClangCompleteManager {
const lsTextDocumentPositionParams& completion_location, const lsTextDocumentPositionParams& completion_location,
const OnComplete& on_complete); const OnComplete& on_complete);
// Request a diagnostics update. // Request a diagnostics update.
void DiagnosticsUpdate(const lsRequestId& request_id, void DiagnosticsUpdate(const lsTextDocumentIdentifier& document);
const lsTextDocumentIdentifier& document);
// Notify the completion manager that |filename| has been viewed and we // Notify the completion manager that |filename| has been viewed and we
// should begin preloading completion data. // should begin preloading completion data.

View File

@ -172,7 +172,7 @@ void RunQueryDbThread(const std::string& bin_name,
args); args);
}, },
[](lsRequestId id) { [](lsRequestId id) {
if (!std::holds_alternative<std::monostate>(id)) { if (id.Valid()) {
Out_Error out; Out_Error out;
out.id = id; out.id = id;
out.error.code = lsErrorCodes::InternalError; out.error.code = lsErrorCodes::InternalError;
@ -243,7 +243,7 @@ void RunQueryDbThread(const std::string& bin_name,
// //
// |ipc| is connected to a server. // |ipc| is connected to a server.
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) { void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
new std::thread([request_times]() { std::thread([request_times]() {
SetThreadName("stdin"); SetThreadName("stdin");
auto* queue = QueueManager::instance(); auto* queue = QueueManager::instance();
while (true) { while (true) {
@ -257,7 +257,7 @@ void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
// 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 (!std::holds_alternative<std::monostate>(id)) { if (id.Valid()) {
Out_Error out; Out_Error out;
out.id = id; out.id = id;
out.error.code = lsErrorCodes::InvalidParams; out.error.code = lsErrorCodes::InvalidParams;
@ -279,12 +279,12 @@ void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
if (method_type == kMethodType_Exit) if (method_type == kMethodType_Exit)
break; break;
} }
}); }).detach();
} }
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times, void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
MultiQueueWaiter* waiter) { MultiQueueWaiter* waiter) {
new std::thread([=]() { std::thread([=]() {
SetThreadName("stdout"); SetThreadName("stdout");
auto* queue = QueueManager::instance(); auto* queue = QueueManager::instance();
@ -305,7 +305,7 @@ void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
fflush(stdout); fflush(stdout);
} }
} }
}); }).detach();
} }
void LanguageServerMain(const std::string& bin_name, void LanguageServerMain(const std::string& bin_name,

View File

@ -393,8 +393,7 @@ void ParseFile(DiagnosticsEngine* diag_engine,
file_contents, &perf); file_contents, &perf);
if (indexes.empty()) { if (indexes.empty()) {
if (g_config->index.enabled && if (g_config->index.enabled && request.id.Valid()) {
!std::holds_alternative<std::monostate>(request.id)) {
Out_Error out; Out_Error out;
out.id = request.id; out.id = request.id;
out.error.code = lsErrorCodes::InternalError; out.error.code = lsErrorCodes::InternalError;

View File

@ -29,21 +29,16 @@ std::string ElideLongPath(const std::string& path) {
size_t TrimCommonPathPrefix(const std::string& result, size_t TrimCommonPathPrefix(const std::string& result,
const std::string& trimmer) { const std::string& trimmer) {
size_t i = 0; #ifdef _WIN32
while (i < result.size() && i < trimmer.size()) { std::string s = result, t = trimmer;
char a = result[i]; std::transform(s.begin(), s.end(), s.begin(), ::tolower);
char b = trimmer[i]; std::transform(t.begin(), t.end(), t.begin(), ::tolower);
#if defined(_WIN32) if (s.compare(0, t.size(), t) == 0)
a = (char)tolower(a); return t.size();
b = (char)tolower(b); #else
if (result.compare(0, trimmer.size(), trimmer) == 0)
return trimmer.size();
#endif #endif
if (a != b)
break;
++i;
}
if (i == trimmer.size())
return i;
return 0; return 0;
} }
@ -51,21 +46,14 @@ size_t TrimCommonPathPrefix(const std::string& result,
bool TrimPath(Project* project, bool TrimPath(Project* project,
const std::string& project_root, const std::string& project_root,
std::string* insert_path) { std::string* insert_path) {
size_t start = 0; size_t start = TrimCommonPathPrefix(*insert_path, project_root);
bool angle = false; bool angle = false;
size_t len = TrimCommonPathPrefix(*insert_path, project_root); for (auto& include_dir : project->quote_include_directories)
if (len > start) start = std::max(start, TrimCommonPathPrefix(*insert_path, include_dir));
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->angle_include_directories) { for (auto& include_dir : project->angle_include_directories) {
len = TrimCommonPathPrefix(*insert_path, include_dir); auto len = TrimCommonPathPrefix(*insert_path, include_dir);
if (len > start) { if (len > start) {
start = len; start = len;
angle = true; angle = true;
@ -115,7 +103,7 @@ void IncludeComplete::Rescan() {
g_config->completion.includeBlacklist); g_config->completion.includeBlacklist);
is_scanning = true; is_scanning = true;
new std::thread([this]() { std::thread([this]() {
SetThreadName("scan_includes"); SetThreadName("scan_includes");
Timer timer; Timer timer;
@ -129,7 +117,7 @@ void IncludeComplete::Rescan() {
timer.ResetAndPrint("[perf] Scanning for includes"); timer.ResetAndPrint("[perf] Scanning for includes");
is_scanning = false; is_scanning = false;
}); }).detach();
} }
void IncludeComplete::InsertCompletionItem(const std::string& absolute_path, void IncludeComplete::InsertCompletionItem(const std::string& absolute_path,

View File

@ -275,6 +275,19 @@ bool lsTextEdit::operator==(const lsTextEdit& that) {
return range == that.range && newText == that.newText; 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() { std::string Out_ShowLogMessage::method() {
if (display_type == DisplayType::Log) if (display_type == DisplayType::Log)
return "window/logMessage"; return "window/logMessage";

View File

@ -239,7 +239,7 @@ MAKE_REFLECT_STRUCT(lsTextDocumentIdentifier, uri);
struct lsVersionedTextDocumentIdentifier { struct lsVersionedTextDocumentIdentifier {
lsDocumentUri uri; lsDocumentUri uri;
// The version number of this document. number | null // The version number of this document. number | null
std::variant<std::monostate, int> version; std::optional<int> version;
lsTextDocumentIdentifier AsTextDocumentIdentifier() const; 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 // Note that markdown strings will be sanitized - that means html will be
// escaped. // escaped.
struct lsMarkedString1 { struct lsMarkedString {
std::string_view language; std::optional<std::string> language;
std::string_view value; std::string value;
}; };
using lsMarkedString = std::variant<std::string_view, lsMarkedString1>; void Reflect(Writer& visitor, lsMarkedString& value);
MAKE_REFLECT_STRUCT(lsMarkedString1, language, value);
struct lsTextDocumentContentChangeEvent { struct lsTextDocumentContentChangeEvent {
// The range of the document that changed. // The range of the document that changed.

View File

@ -87,7 +87,7 @@ struct Handler_CclsFreshenIndex : BaseMessageHandler<In_CclsFreshenIndex> {
Timer time; Timer time;
// Send index requests for every file. // 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"); time.ResetAndPrint("[perf] Dispatched $ccls/freshenIndex index requests");
} }
}; };

View File

@ -518,12 +518,12 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
} }
LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers"; LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; i++) { for (int i = 0; i < g_config->index.threads; i++) {
new std::thread([=]() { std::thread([=]() {
SetThreadName("indexer" + std::to_string(i)); SetThreadName("indexer" + std::to_string(i));
Indexer_Main(diag_engine, file_consumer_shared, timestamp_manager, Indexer_Main(diag_engine, file_consumer_shared, timestamp_manager,
import_manager, import_pipeline_status, project, import_manager, import_pipeline_status, project,
working_files, waiter); working_files, waiter);
}); }).detach();
} }
// Start scanning include directories before dispatching project // Start scanning include directories before dispatching project

View File

@ -11,8 +11,8 @@ MAKE_REFLECT_STRUCT(In_Shutdown, id);
REGISTER_IN_MESSAGE(In_Shutdown); REGISTER_IN_MESSAGE(In_Shutdown);
struct Out_Shutdown : public lsOutMessage<Out_Shutdown> { struct Out_Shutdown : public lsOutMessage<Out_Shutdown> {
lsRequestId id; // defaults to std::monostate (null) lsRequestId id;
std::monostate result; // null JsonNull result;
}; };
MAKE_REFLECT_STRUCT(Out_Shutdown, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_Shutdown, jsonrpc, id, result);

View File

@ -39,7 +39,6 @@ struct Handler_TextDocumentDidChange
} }
clang_complete->NotifyEdit(path); clang_complete->NotifyEdit(path);
clang_complete->DiagnosticsUpdate( clang_complete->DiagnosticsUpdate(
std::monostate(),
request->params.textDocument.AsTextDocumentIdentifier()); request->params.textDocument.AsTextDocumentIdentifier());
} }
}; };

View File

@ -5,41 +5,40 @@
namespace { namespace {
MethodType kMethodType = "textDocument/hover"; MethodType kMethodType = "textDocument/hover";
std::pair<std::string_view, std::string_view> GetCommentsAndHover( // Find the comments for |sym|, if any.
QueryDatabase* db, 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;
}
});
return ret;
}
// Returns the hover or detailed name for `sym`, if any.
std::optional<lsMarkedString> GetHoverOrName(QueryDatabase* db,
const std::string& language,
SymbolRef sym) { SymbolRef sym) {
switch (sym.kind) {
case SymbolKind::Type: { std::optional<lsMarkedString> ret;
if (const auto* def = db->GetType(sym).AnyDef()) { WithEntity(db, sym, [&](const auto& entity) {
return {def->comments, !def->hover.empty() if (const auto* def = entity.AnyDef()) {
? std::string_view(def->hover) lsMarkedString m;
: std::string_view(def->detailed_name)}; m.language = language;
} if (!def->hover.empty()) {
break; m.value = def->hover;
} ret = m;
case SymbolKind::Func: { } else if (!def->detailed_name.empty()) {
if (const auto* def = db->GetFunc(sym).AnyDef()) { m.value = def->detailed_name;
return {def->comments, !def->hover.empty() ret = m;
? std::string_view(def->hover)
: std::string_view(def->detailed_name)};
}
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 { struct In_TextDocumentHover : public RequestInMessage {
@ -66,7 +65,7 @@ void Reflect(Writer& visitor, Out_TextDocumentHover& value) {
if (value.result) if (value.result)
REFLECT_MEMBER(result); REFLECT_MEMBER(result);
else { 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. // |null| to be compliant with the LSP.
visitor.Key("result"); visitor.Key("result");
visitor.Null(); visitor.Null();
@ -77,11 +76,11 @@ void Reflect(Writer& visitor, Out_TextDocumentHover& value) {
struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> { struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentHover* request) override { void Run(In_TextDocumentHover* request) override {
auto& params = request->params;
QueryFile* file; QueryFile* file;
if (!FindFileOrFail(db, project, request->id, if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file)) { params.textDocument.uri.GetPath(), &file))
return; return;
}
WorkingFile* working_file = WorkingFile* working_file =
working_files->GetFileByFilename(file->def->path); working_files->GetFileByFilename(file->def->path);
@ -90,25 +89,23 @@ struct Handler_TextDocumentHover : BaseMessageHandler<In_TextDocumentHover> {
out.id = request->id; out.id = request->id;
for (SymbolRef sym : for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) { FindSymbolsAtLocation(working_file, file, params.position)) {
// Found symbol. Return hover. // 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;
std::pair<std::string_view, std::string_view> comments_hover = std::optional<lsMarkedString> comments = GetComments(db, sym);
GetCommentsAndHover(db, sym); std::optional<lsMarkedString> hover =
if (comments_hover.first.size() || comments_hover.second.size()) { GetHoverOrName(db, file->def->language, sym);
if (comments || hover) {
out.result = Out_TextDocumentHover::Result(); 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; out.result->range = *ls_range;
if (comments)
out.result->contents.push_back(*comments);
if (hover)
out.result->contents.push_back(*hover);
break; break;
} }
} }

View File

@ -33,7 +33,7 @@ struct Handler_WorkspaceDidChangeConfiguration
std::to_string(project->entries.size()) + " files)"); std::to_string(project->entries.size()) + " files)");
time.Reset(); time.Reset();
project->Index(QueueManager::instance(), working_files, std::monostate()); project->Index(QueueManager::instance(), working_files, lsRequestId());
time.ResetAndPrint( time.ResetAndPrint(
"[perf] Dispatched workspace/didChangeConfiguration index requests"); "[perf] Dispatched workspace/didChangeConfiguration index requests");

View File

@ -2,6 +2,40 @@
MethodType kMethodType_Unknown = "$unknown"; MethodType kMethodType_Unknown = "$unknown";
MethodType kMethodType_Exit = "exit"; MethodType kMethodType_Exit = "exit";
MethodType kMethodType_TextDocumentPublishDiagnostics = "textDocument/publishDiagnostics"; MethodType kMethodType_TextDocumentPublishDiagnostics =
MethodType kMethodType_CclsPublishInactiveRegions = "$ccls/publishInactiveRegions"; "textDocument/publishDiagnostics";
MethodType kMethodType_CclsPublishSemanticHighlighting = "$ccls/publishSemanticHighlighting"; 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;
}
}

View File

@ -12,7 +12,18 @@ extern MethodType kMethodType_TextDocumentPublishDiagnostics;
extern MethodType kMethodType_CclsPublishInactiveRegions; extern MethodType kMethodType_CclsPublishInactiveRegions;
extern MethodType kMethodType_CclsPublishSemanticHighlighting; 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 { struct InMessage {
virtual ~InMessage() = default; virtual ~InMessage() = default;
@ -32,6 +43,6 @@ struct RequestInMessage : public InMessage {
// NotificationInMessage does not have |id|. // NotificationInMessage does not have |id|.
struct NotificationInMessage : public InMessage { struct NotificationInMessage : public InMessage {
lsRequestId GetRequestId() const override { lsRequestId GetRequestId() const override {
return std::monostate(); return lsRequestId();
} }
}; };

View File

@ -8,7 +8,6 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <variant>
#include <vector> #include <vector>
class QueueManager; class QueueManager;

View File

@ -140,6 +140,15 @@ void Reflect(Writer& visitor, NtString& value) {
visitor.String(s ? s : ""); 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 // TODO: Move this to indexer.cc
void Reflect(Reader& visitor, IndexInclude& value) { void Reflect(Reader& visitor, IndexInclude& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
@ -299,15 +308,6 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
REFLECT_MEMBER_END(); 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) { void Reflect(Reader& visitor, SerializeFormat& value) {
std::string fmt = visitor.GetString(); std::string fmt = visitor.GetString();
value = fmt[0] == 'b' ? SerializeFormat::Binary : SerializeFormat::Json; value = fmt[0] == 'b' ? SerializeFormat::Binary : SerializeFormat::Json;

View File

@ -13,11 +13,12 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>
#include <variant>
#include <vector> #include <vector>
enum class SerializeFormat { Binary, Json }; enum class SerializeFormat { Binary, Json };
struct JsonNull {};
class Reader { class Reader {
public: public:
virtual ~Reader() {} virtual ~Reader() {}
@ -168,20 +169,18 @@ void Reflect(Writer& visitor, std::string_view& view);
void Reflect(Reader& visitor, NtString& value); void Reflect(Reader& visitor, NtString& value);
void Reflect(Writer& visitor, NtString& value); void Reflect(Writer& visitor, NtString& value);
// std::monostate is used to represent JSON null void Reflect(Reader& visitor, JsonNull& value);
void Reflect(Reader& visitor, std::monostate&); void Reflect(Writer& visitor, JsonNull& value);
void Reflect(Writer& visitor, std::monostate&);
void Reflect(Reader& visitor, SerializeFormat& value); void Reflect(Reader& visitor, SerializeFormat& value);
void Reflect(Writer& visitor, SerializeFormat& value); void Reflect(Writer& visitor, SerializeFormat& value);
//// Type constructors //// 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). // (in `key: value` context).
// Reflect std::optional<T> is used for a different purpose, whether an object is // Reflect std::optional<T> is used for a different purpose, whether an object is
// nullable (possibly in `value` context). For the nullable semantics, // nullable (possibly in `value` context).
// std::variant<std::monostate, T> is recommended.
template <typename T> template <typename T>
void Reflect(Reader& visitor, std::optional<T>& value) { void Reflect(Reader& visitor, std::optional<T>& value) {
if (visitor.IsNull()) { 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 // std::vector
template <typename T> template <typename T>
void Reflect(Reader& visitor, std::vector<T>& values) { void Reflect(Reader& visitor, std::vector<T>& values) {

View File

@ -24,19 +24,15 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
if (offset >= content.size()) if (offset >= content.size())
offset = (int)content.size() - 1; offset = (int)content.size() - 1;
lsPosition result; int line = 0, col = 0;
int i = 0; int i = 0;
while (i < offset) { for (; i < offset; i++) {
if (content[i] == '\n') { if (content[i] == '\n')
result.line += 1; line++, col = 0;
result.character = 0; else
} else { col++;
result.character += 1;
} }
++i; return {line, col};
}
return result;
} }
std::vector<std::string> ToLines(const std::string& content) { std::vector<std::string> ToLines(const std::string& content) {
@ -511,8 +507,8 @@ void WorkingFiles::OnChange(const lsTextDocumentDidChangeParams& change) {
} }
// version: number | null // version: number | null
if (std::holds_alternative<int>(change.textDocument.version)) if (change.textDocument.version)
file->version = std::get<int>(change.textDocument.version); file->version = *change.textDocument.version;
for (const lsTextDocumentContentChangeEvent& diff : change.contentChanges) { for (const lsTextDocumentContentChangeEvent& diff : change.contentChanges) {
// Per the spec replace everything if the rangeLength and range are not set. // Per the spec replace everything if the rangeLength and range are not set.