Remove WithFileContent & lex_utils.{cc,h}

This commit is contained in:
Fangrui Song 2018-05-31 20:06:09 -07:00
parent da545f1139
commit 34dc1e93e9
19 changed files with 100 additions and 254 deletions

View File

@ -202,7 +202,6 @@ target_sources(ccls PRIVATE
src/indexer.cc src/indexer.cc
src/method.cc src/method.cc
src/language.cc src/language.cc
src/lex_utils.cc
src/log.cc src/log.cc
src/lsp.cc src/lsp.cc
src/match.cc src/match.cc

View File

@ -3,9 +3,11 @@
#include "log.hh" #include "log.hh"
#include "platform.h" #include "platform.h"
#include "serializer.h" #include "serializer.h"
#include "timer.h"
#include "type_printer.h" #include "type_printer.h"
#include <llvm/Support/Timer.h>
using namespace llvm;
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
@ -2016,7 +2018,8 @@ std::vector<std::unique_ptr<IndexFile>> ClangIndexer::Index(
file = NormalizePath(file); file = NormalizePath(file);
Timer timer; Timer timer("parse", "parse tu");
timer.startTimer();
std::vector<CXUnsavedFile> unsaved_files; std::vector<CXUnsavedFile> unsaved_files;
for (const FileContents& contents : file_contents) { for (const FileContents& contents : file_contents) {
@ -2034,7 +2037,7 @@ std::vector<std::unique_ptr<IndexFile>> ClangIndexer::Index(
if (!tu) if (!tu)
return {}; return {};
perf->index_parse = timer.ElapsedMicrosecondsAndReset(); timer.stopTimer();
return ParseWithTu(vfs, perf, tu.get(), &index, file, args, unsaved_files); return ParseWithTu(vfs, perf, tu.get(), &index, file, args, unsaved_files);
} }
@ -2048,6 +2051,7 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
const std::vector<std::string>& args, const std::vector<std::string>& args,
const std::vector<CXUnsavedFile>& file_contents) { const std::vector<CXUnsavedFile>& file_contents) {
Timer timer; Timer timer;
timer.startTimer();
IndexerCallbacks callback = {0}; IndexerCallbacks callback = {0};
// Available callbacks: // Available callbacks:
@ -2085,13 +2089,12 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
<< " failed with errno=" << index_result; << " failed with errno=" << index_result;
return {}; return {};
} }
clang_IndexAction_dispose(index_action); clang_IndexAction_dispose(index_action);
ClangCursor(clang_getTranslationUnitCursor(tu->cx_tu)) ClangCursor(clang_getTranslationUnitCursor(tu->cx_tu))
.VisitChildren(&VisitMacroDefinitionAndExpansions, &param); .VisitChildren(&VisitMacroDefinitionAndExpansions, &param);
perf->index_build = timer.ElapsedMicrosecondsAndReset(); timer.stopTimer();
std::unordered_map<std::string, int> inc_to_line; std::unordered_map<std::string, int> inc_to_line;
// TODO // TODO

View File

@ -27,8 +27,6 @@ struct IndexFunc;
struct IndexVar; struct IndexVar;
struct QueryFile; struct QueryFile;
using RawId = uint32_t;
struct SymbolIdx { struct SymbolIdx {
Usr usr; Usr usr;
SymbolKind kind; SymbolKind kind;
@ -153,19 +151,9 @@ struct TypeDef : NameMixin<TypeDef> {
NtString hover; NtString hover;
NtString comments; NtString comments;
// While a class/type can technically have a separate declaration/definition,
// it doesn't really happen in practice. The declaration never contains
// comments or insightful information. The user always wants to jump from
// the declaration to the definition - never the other way around like in
// functions and (less often) variables.
//
// It's also difficult to identify a `class Foo;` statement with the clang
// indexer API (it's doable using cursor AST traversal), so we don't bother
// supporting the feature.
Maybe<Use> spell; Maybe<Use> spell;
Maybe<Use> extent; Maybe<Use> extent;
// Immediate parent types.
std::vector<Usr> bases; std::vector<Usr> bases;
// Types, functions, and variables defined in this type. // Types, functions, and variables defined in this type.
@ -221,8 +209,6 @@ struct VarDef : NameMixin<VarDef> {
std::string detailed_name; std::string detailed_name;
NtString hover; NtString hover;
NtString comments; NtString comments;
// TODO: definitions should be a list of ranges, since there can be more
// than one - when??
Maybe<Use> spell; Maybe<Use> spell;
Maybe<Use> extent; Maybe<Use> extent;

View File

@ -1,87 +0,0 @@
#include "lex_utils.h"
#include <doctest/doctest.h>
#include <algorithm>
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
// text documents.
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
// This is good enough and fails only for UTF-16 surrogate pairs.
int GetOffsetForPosition(lsPosition position, std::string_view content) {
size_t i = 0;
for (; position.line > 0 && i < content.size(); i++)
if (content[i] == '\n')
position.line--;
for (; position.character > 0 && i < content.size(); position.character--)
if (uint8_t(content[i++]) >= 128) {
// Skip 0b10xxxxxx
while (i < content.size() && uint8_t(content[i]) >= 128 &&
uint8_t(content[i]) < 192)
i++;
}
return int(i);
}
std::string_view LexIdentifierAroundPos(lsPosition position,
std::string_view content) {
int start = GetOffsetForPosition(position, content);
int end = start + 1;
char c;
// We search for :: before the cursor but not after to get the qualifier.
for (; start > 0; start--) {
c = content[start - 1];
if (isalnum(c) || c == '_')
;
else if (c == ':' && start > 1 && content[start - 2] == ':')
start--;
else
break;
}
for (; end < (int)content.size(); end++)
if (c = content[end], !(isalnum(c) || c == '_'))
break;
return content.substr(start, end - start);
}
// Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found.
int ReverseSubseqMatch(std::string_view pat,
std::string_view text,
int case_sensitivity) {
if (case_sensitivity == 1)
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
int j = pat.size();
if (!j)
return text.size();
for (int i = text.size(); i--;)
if ((case_sensitivity ? text[i] == pat[j - 1]
: tolower(text[i]) == tolower(pat[j - 1])) &&
!--j)
return i;
return -1;
}
TEST_SUITE("Offset") {
TEST_CASE("past end") {
std::string content = "foo";
int offset = GetOffsetForPosition(lsPosition{10, 10}, content);
REQUIRE(offset <= content.size());
}
TEST_CASE("in middle of content") {
std::string content = "abcdefghijk";
for (int i = 0; i < content.size(); ++i) {
int offset = GetOffsetForPosition(lsPosition{0, i}, content);
REQUIRE(i == offset);
}
}
TEST_CASE("at end of content") {
REQUIRE(GetOffsetForPosition(lsPosition{0, 0}, "") == 0);
REQUIRE(GetOffsetForPosition(lsPosition{0, 1}, "a") == 1);
}
}

View File

@ -1,16 +0,0 @@
#pragma once
#include "lsp.h"
#include <string_view>
#include <tuple>
// Utility method to map |position| to an offset inside of |content|.
int GetOffsetForPosition(lsPosition position, std::string_view content);
std::string_view LexIdentifierAroundPos(lsPosition position,
std::string_view content);
int ReverseSubseqMatch(std::string_view pat,
std::string_view text,
int case_sensitivity);

View File

@ -129,12 +129,10 @@ int main(int argc, char** argv) {
} }
} }
std::unordered_map<MethodType, Timer> request_times;
// The thread that reads from stdin and dispatchs commands to the main thread. // The thread that reads from stdin and dispatchs commands to the main thread.
pipeline::LaunchStdin(&request_times); pipeline::LaunchStdin();
// The thread that writes responses from the main thread to stdout. // The thread that writes responses from the main thread to stdout.
pipeline::LaunchStdout(&request_times); pipeline::LaunchStdout();
// Main thread which also spawns indexer threads upon the "initialize" request. // Main thread which also spawns indexer threads upon the "initialize" request.
pipeline::MainLoop(); pipeline::MainLoop();
} }

View File

@ -1,6 +1,5 @@
#include "message_handler.h" #include "message_handler.h"
#include "lex_utils.h"
#include "log.hh" #include "log.hh"
#include "project.h" #include "project.h"
#include "query_utils.h" #include "query_utils.h"

View File

@ -7,8 +7,6 @@
#include "working_files.h" #include "working_files.h"
using namespace ccls; using namespace ccls;
#include "lex_utils.h"
#include <regex> #include <regex>
namespace { namespace {

View File

@ -1,4 +1,3 @@
#include "lex_utils.h"
#include "message_handler.h" #include "message_handler.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"

View File

@ -1,72 +0,0 @@
#include "clang_format.h"
#include "lex_utils.h"
#include "message_handler.h"
#include "pipeline.hh"
using namespace ccls;
#include "working_files.h"
#include <loguru.hpp>
namespace {
MethodType kMethodType = "textDocument/rangeFormatting";
struct lsTextDocumentRangeFormattingParams {
lsTextDocumentIdentifier textDocument;
lsRange range;
lsFormattingOptions options;
};
MAKE_REFLECT_STRUCT(lsTextDocumentRangeFormattingParams,
textDocument,
range,
options);
struct In_TextDocumentRangeFormatting : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentRangeFormattingParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentRangeFormatting);
struct Out_TextDocumentRangeFormatting
: public lsOutMessage<Out_TextDocumentRangeFormatting> {
lsRequestId id;
std::vector<lsTextEdit> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentRangeFormatting, jsonrpc, id, result);
struct Handler_TextDocumentRangeFormatting
: BaseMessageHandler<In_TextDocumentRangeFormatting> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentRangeFormatting* request) override {
Out_TextDocumentRangeFormatting response;
response.id = request->id;
#if USE_CLANG_CXX
QueryFile* file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file)) {
return;
}
WorkingFile* working_file =
working_files->GetFileByFilename(file->def->path);
int start = GetOffsetForPosition(request->params.range.start,
working_file->buffer_content),
end = GetOffsetForPosition(request->params.range.end,
working_file->buffer_content);
response.result = ConvertClangReplacementsIntoTextEdits(
working_file->buffer_content,
ClangFormatDocument(working_file, start, end, request->params.options));
#else
LOG_S(WARNING) << "You must compile ccls with --use-clang-cxx to use "
"textDocument/rangeFormatting.";
// TODO: Fallback to execute the clang-format binary?
response.result = {};
#endif
pipeline::WriteStdout(kMethodType, response);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRangeFormatting);
} // namespace

View File

@ -1,5 +1,4 @@
#include "fuzzy_match.h" #include "fuzzy_match.h"
#include "lex_utils.h"
#include "message_handler.h" #include "message_handler.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.h" #include "query_utils.h"

View File

@ -11,10 +11,10 @@
#include "project.h" #include "project.h"
#include "query_utils.h" #include "query_utils.h"
#include "pipeline.hh" #include "pipeline.hh"
#include "timer.h"
#include <llvm/ADT/Twine.h> #include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
#include <llvm/Support/Timer.h>
using namespace llvm; using namespace llvm;
#include <thread> #include <thread>
@ -237,14 +237,15 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
// Write current index to disk if requested. // Write current index to disk if requested.
LOG_S(INFO) << "store index for " << path; LOG_S(INFO) << "store index for " << path;
Timer time;
{ {
Timer timer("write", "store index");
timer.startTimer();
std::string cache_path = GetCachePath(path); std::string cache_path = GetCachePath(path);
WriteToFile(cache_path, curr->file_contents); WriteToFile(cache_path, curr->file_contents);
WriteToFile(AppendSerializationFormat(cache_path), WriteToFile(AppendSerializationFormat(cache_path),
Serialize(g_config->cacheFormat, *curr)); Serialize(g_config->cacheFormat, *curr));
timer.stopTimer();
} }
perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
vfs->Reset(path_to_index); vfs->Reset(path_to_index);
if (entry.id >= 0) { if (entry.id >= 0) {
@ -255,7 +256,6 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
// Build delta update. // Build delta update.
IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get()); IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get());
perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")"; LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")";
on_indexed->PushBack({std::move(update), perf}, request.is_interactive); on_indexed->PushBack({std::move(update), perf}, request.is_interactive);
@ -264,15 +264,6 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
return true; return true;
} }
// This function returns true if e2e timing should be displayed for the given
// MethodId.
bool ShouldDisplayMethodTiming(MethodType type) {
return
type != kMethodType_TextDocumentPublishDiagnostics &&
type != kMethodType_CclsPublishInactiveRegions &&
type != kMethodType_Unknown;
}
} // namespace } // namespace
void Init() { void Init() {
@ -316,20 +307,22 @@ void Main_OnIndexed(DB* db,
return; return;
} }
Timer time; Timer timer("apply", "apply index");
timer.startTimer();
db->ApplyIndexUpdate(&response->update); db->ApplyIndexUpdate(&response->update);
timer.stopTimer();
// Update indexed content, inactive lines, and semantic highlighting. // Update indexed content, inactive lines, and semantic highlighting.
if (response->update.files_def_update) { if (response->update.files_def_update) {
auto& update = *response->update.files_def_update; auto& update = *response->update.files_def_update;
time.ResetAndPrint("apply index for " + update.value.path); LOG_S(INFO) << "apply index for " << update.first.path;
if (WorkingFile* working_file = if (WorkingFile* working_file =
working_files->GetFileByFilename(update.value.path)) { working_files->GetFileByFilename(update.first.path)) {
// Update indexed content. // Update indexed content.
working_file->SetIndexContent(update.file_content); working_file->SetIndexContent(update.second);
// Inactive lines. // Inactive lines.
EmitInactiveLines(working_file, update.value.inactive_regions); EmitInactiveLines(working_file, update.first.inactive_regions);
// Semantic highlighting. // Semantic highlighting.
int file_id = int file_id =
@ -340,8 +333,8 @@ void Main_OnIndexed(DB* db,
} }
} }
void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) { void LaunchStdin() {
std::thread([request_times]() { std::thread([]() {
set_thread_name("stdin"); set_thread_name("stdin");
while (true) { while (true) {
std::unique_ptr<InMessage> message; std::unique_ptr<InMessage> message;
@ -367,7 +360,6 @@ void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) {
// Cache |method_id| so we can access it after moving |message|. // Cache |method_id| so we can access it after moving |message|.
MethodType method_type = message->GetMethodType(); MethodType method_type = message->GetMethodType();
(*request_times)[method_type] = Timer();
on_request->PushBack(std::move(message)); on_request->PushBack(std::move(message));
@ -379,7 +371,7 @@ void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) {
}).detach(); }).detach();
} }
void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times) { void LaunchStdout() {
std::thread([=]() { std::thread([=]() {
set_thread_name("stdout"); set_thread_name("stdout");
@ -391,11 +383,6 @@ void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times) {
} }
for (auto& message : messages) { for (auto& message : messages) {
if (ShouldDisplayMethodTiming(message.method)) {
Timer time = (*request_times)[message.method];
time.ResetAndPrint("[e2e] Running " + std::string(message.method));
}
fwrite(message.content.c_str(), message.content.size(), 1, stdout); fwrite(message.content.c_str(), message.content.size(), 1, stdout);
fflush(stdout); fflush(stdout);
} }

View File

@ -2,7 +2,6 @@
#include "method.h" #include "method.h"
#include "query.h" #include "query.h"
#include "timer.h"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -17,8 +16,8 @@ struct lsBaseOutMessage;
namespace ccls::pipeline { namespace ccls::pipeline {
void Init(); void Init();
void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times); void LaunchStdin();
void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times); void LaunchStdout();
void Indexer_Main(DiagnosticsEngine* diag_engine, void Indexer_Main(DiagnosticsEngine* diag_engine,
VFS* vfs, VFS* vfs,
Project* project, Project* project,

View File

@ -148,7 +148,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
return a.range.start < b.range.start; return a.range.start < b.range.start;
}); });
return QueryFile::DefUpdate(def, indexed.file_contents); return {std::move(def), std::move(indexed.file_contents)};
} }
// Returns true if an element with the same file is found. // Returns true if an element with the same file is found.
@ -342,11 +342,11 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
int DB::Update(QueryFile::DefUpdate&& u) { int DB::Update(QueryFile::DefUpdate&& u) {
int id = files.size(); int id = files.size();
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.value.path), id); auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.first.path), id);
if (it.second) if (it.second)
files.emplace_back().id = id; files.emplace_back().id = id;
QueryFile& existing = files[it.first->second]; QueryFile& existing = files[it.first->second];
existing.def = u.value; existing.def = u.first;
return existing.id; return existing.id;
} }

View File

@ -7,21 +7,6 @@
#include <llvm/ADT/SmallVector.h> #include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringMap.h> #include <llvm/ADT/StringMap.h>
struct QueryFile;
struct QueryType;
struct QueryFunc;
struct QueryVar;
struct DB;
template <typename T>
struct WithFileContent {
T value;
std::string file_content;
WithFileContent(const T& value, const std::string& file_content)
: value(value), file_content(file_content) {}
};
struct QueryFile { struct QueryFile {
struct Def { struct Def {
std::string path; std::string path;
@ -39,7 +24,7 @@ struct QueryFile {
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
}; };
using DefUpdate = WithFileContent<Def>; using DefUpdate = std::pair<Def, std::string>;
int id = -1; int id = -1;
std::optional<Def> def; std::optional<Def> def;

View File

@ -134,6 +134,24 @@ std::optional<int64_t> LastWriteTime(const std::string& filename) {
return Status.getLastModificationTime().time_since_epoch().count(); return Status.getLastModificationTime().time_since_epoch().count();
} }
// Find discontinous |search| in |content|.
// Return |found| and the count of skipped chars before found.
int ReverseSubseqMatch(std::string_view pat,
std::string_view text,
int case_sensitivity) {
if (case_sensitivity == 1)
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
int j = pat.size();
if (!j)
return text.size();
for (int i = text.size(); i--;)
if ((case_sensitivity ? text[i] == pat[j - 1]
: tolower(text[i]) == tolower(pat[j - 1])) &&
!--j)
return i;
return -1;
}
std::string GetDefaultResourceDirectory() { std::string GetDefaultResourceDirectory() {
return DEFAULT_RESOURCE_DIRECTORY; return DEFAULT_RESOURCE_DIRECTORY;
} }

View File

@ -58,6 +58,10 @@ std::optional<std::string> ReadContent(const std::string& filename);
void WriteToFile(const std::string& filename, const std::string& content); void WriteToFile(const std::string& filename, const std::string& content);
std::optional<int64_t> LastWriteTime(const std::string& filename); std::optional<int64_t> LastWriteTime(const std::string& filename);
int ReverseSubseqMatch(std::string_view pat,
std::string_view text,
int case_sensitivity);
// http://stackoverflow.com/a/38140932 // http://stackoverflow.com/a/38140932
// //
// struct SomeHashKey { // struct SomeHashKey {

View File

@ -1,6 +1,5 @@
#include "working_files.h" #include "working_files.h"
#include "lex_utils.h"
#include "log.hh" #include "log.hh"
#include "position.h" #include "position.h"
@ -545,3 +544,46 @@ WorkingFiles::Snapshot WorkingFiles::AsSnapshot(
} }
return result; return result;
} }
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
// text documents.
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
// This is good enough and fails only for UTF-16 surrogate pairs.
int GetOffsetForPosition(lsPosition position, std::string_view content) {
size_t i = 0;
for (; position.line > 0 && i < content.size(); i++)
if (content[i] == '\n')
position.line--;
for (; position.character > 0 && i < content.size(); position.character--)
if (uint8_t(content[i++]) >= 128) {
// Skip 0b10xxxxxx
while (i < content.size() && uint8_t(content[i]) >= 128 &&
uint8_t(content[i]) < 192)
i++;
}
return int(i);
}
std::string_view LexIdentifierAroundPos(lsPosition position,
std::string_view content) {
int start = GetOffsetForPosition(position, content);
int end = start + 1;
char c;
// We search for :: before the cursor but not after to get the qualifier.
for (; start > 0; start--) {
c = content[start - 1];
if (isalnum(c) || c == '_')
;
else if (c == ':' && start > 1 && content[start - 2] == ':')
start--;
else
break;
}
for (; end < (int)content.size(); end++)
if (c = content[end], !(isalnum(c) || c == '_'))
break;
return content.substr(start, end - start);
}

View File

@ -117,3 +117,8 @@ struct WorkingFiles {
std::vector<std::unique_ptr<WorkingFile>> files; std::vector<std::unique_ptr<WorkingFile>> files;
std::mutex files_mutex; // Protects |files|. std::mutex files_mutex; // Protects |files|.
}; };
int GetOffsetForPosition(lsPosition position, std::string_view content);
std::string_view LexIdentifierAroundPos(lsPosition position,
std::string_view content);