For completion, only tell clang about relevant dirty working files.

The heuristic may not be perfect, but it is probably good enough.
clang_codeCompleteAt seems to get slower as more and more CXUnsavedFile
instances are uploaded.
This commit is contained in:
Jacob Dufault 2018-01-12 09:37:33 -08:00
parent 27dad9a277
commit 93ba0c532b
5 changed files with 70 additions and 31 deletions

View File

@ -298,7 +298,8 @@ void TryEnsureDocumentParsed(ClangCompleteManager* manager,
args.push_back("-fspell-checking");
}
WorkingFilesSnapshot snapshot = session->working_files->AsSnapshot();
WorkingFiles::Snapshot snapshot =
session->working_files->AsSnapshot({StripFileType(session->file.filename)});
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
LOG_S(INFO) << "Creating completion session with arguments "
@ -389,9 +390,12 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
if (!session->tu)
continue;
WorkingFilesSnapshot snapshot =
completion_manager->working_files_->AsSnapshot();
timer.Reset();
WorkingFiles::Snapshot snapshot =
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
timer.ResetAndPrint("[complete] Creating WorkingFile snapshot");
// Emit code completion data.
if (request->position) {

View File

@ -86,6 +86,13 @@ bool EndsWithAny(const std::string& value,
[&value](const std::string& ending) { return EndsWith(value, ending); });
}
bool FindAnyPartial(const std::string& value,
const std::vector<std::string>& values) {
return std::any_of(
std::begin(values), std::end(values),
[&value](const std::string& v) { return value.find(v) != std::string::npos; });
}
std::string GetBaseName(const std::string& path) {
size_t last_slash = path.find_last_of('/');
if (last_slash != std::string::npos && (last_slash + 1) < path.size())
@ -93,6 +100,13 @@ std::string GetBaseName(const std::string& path) {
return path;
}
std::string StripFileType(const std::string& path) {
size_t last_period = path.find_last_of('.');
if (last_period != std::string::npos)
return path.substr(0, last_period);
return path;
}
// See http://stackoverflow.com/a/29752943
std::string ReplaceAll(const std::string& source,
const std::string& from,
@ -609,3 +623,12 @@ TEST_SUITE("Update \\n to \\r\\n") {
REQUIRE(UpdateToRnNewlines("f1\r\nfo2\r\nfoo3") == "f1\r\nfo2\r\nfoo3");
}
}
TEST_SUITE("StripFileType") {
TEST_CASE("all") {
REQUIRE(StripFileType("") == "");
REQUIRE(StripFileType("bar") == "bar");
REQUIRE(StripFileType("bar.cc") == "bar");
REQUIRE(StripFileType("foo/bar.cc") == "foo/bar");
}
}

View File

@ -27,9 +27,13 @@ bool StartsWithAny(const std::string& value,
const std::vector<std::string>& startings);
bool EndsWithAny(const std::string& value,
const std::vector<std::string>& endings);
bool FindAnyPartial(const std::string& value,
const std::vector<std::string>& values);
// Returns the basename of |path|, ie, "foo/bar.cc" => "bar.cc".
std::string GetBaseName(const std::string& path);
// Returns |path| without the filetype, ie, "foo/bar.cc" => "foo/bar".
std::string StripFileType(const std::string& path);
std::string ReplaceAll(const std::string& source,
const std::string& from,

View File

@ -31,6 +31,20 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
} // namespace
std::vector<CXUnsavedFile> WorkingFiles::Snapshot::AsUnsavedFiles() const {
std::vector<CXUnsavedFile> result;
result.reserve(files.size());
for (auto& file : files) {
CXUnsavedFile unsaved;
unsaved.Filename = file.filename.c_str();
unsaved.Contents = file.content.c_str();
unsaved.Length = (unsigned long)file.content.size();
result.push_back(unsaved);
}
return result;
}
WorkingFile::WorkingFile(const std::string& filename,
const std::string& buffer_content)
: filename(filename), buffer_content(buffer_content) {
@ -358,27 +372,16 @@ void WorkingFiles::OnClose(const lsTextDocumentItem& close) {
<< " because it was not open";
}
std::vector<CXUnsavedFile> WorkingFilesSnapshot::AsUnsavedFiles() const {
std::vector<CXUnsavedFile> result;
result.reserve(files.size());
for (auto& file : files) {
CXUnsavedFile unsaved;
unsaved.Filename = file.filename.c_str();
unsaved.Contents = file.content.c_str();
unsaved.Length = (unsigned long)file.content.size();
result.push_back(unsaved);
}
return result;
}
WorkingFilesSnapshot WorkingFiles::AsSnapshot() {
WorkingFiles::Snapshot WorkingFiles::AsSnapshot(
const std::vector<std::string>& filter_paths) {
std::lock_guard<std::mutex> lock(files_mutex);
WorkingFilesSnapshot result;
Snapshot result;
result.files.reserve(files.size());
for (auto& file : files)
result.files.push_back({file->filename, file->buffer_content});
for (const auto& file : files) {
if (filter_paths.empty() || FindAnyPartial(file->filename, filter_paths))
result.files.push_back({file->filename, file->buffer_content});
}
return result;
}

View File

@ -75,16 +75,18 @@ struct WorkingFile {
std::string* existing_completion) const;
};
struct WorkingFilesSnapshot {
std::vector<CXUnsavedFile> AsUnsavedFiles() const;
struct File {
std::string filename;
std::string content;
};
std::vector<File> files;
};
struct WorkingFiles {
struct Snapshot {
struct File {
std::string filename;
std::string content;
};
std::vector<CXUnsavedFile> AsUnsavedFiles() const;
std::vector<File> files;
};
//
// :: IMPORTANT :: All methods in this class are guarded by a single lock.
//
@ -104,7 +106,10 @@ struct WorkingFiles {
void OnChange(const lsTextDocumentDidChangeParams& change);
void OnClose(const lsTextDocumentItem& close);
WorkingFilesSnapshot AsSnapshot();
// If |filter_paths| is non-empty, only files which contain any of the given
// strings. For example, {"foo", "bar"} means that every result has either the
// string "foo" or "bar" contained within it.
Snapshot AsSnapshot(const std::vector<std::string>& filter_paths);
// Use unique_ptrs so we can handout WorkingFile ptrs and not have them
// invalidated if we resize files.