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),
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) {

View File

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

View File

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

View File

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

View File

@ -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,7 +103,7 @@ void IncludeComplete::Rescan() {
g_config->completion.includeBlacklist);
is_scanning = true;
new std::thread([this]() {
std::thread([this]() {
SetThreadName("scan_includes");
Timer timer;
@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,41 +5,40 @@
namespace {
MethodType kMethodType = "textDocument/hover";
std::pair<std::string_view, std::string_view> GetCommentsAndHover(
QueryDatabase* db,
// 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;
}
});
return ret;
}
// Returns the hover or detailed name for `sym`, if any.
std::optional<lsMarkedString> GetHoverOrName(QueryDatabase* db,
const std::string& language,
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)};
}
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)};
}
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;
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;
}
}
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
for (; i < offset; i++) {
if (content[i] == '\n')
line++, col = 0;
else
col++;
}
++i;
}
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.