mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 15:45:08 +00:00
Publish diagnostics of inferred files and change diagnostics.{onChange,onOpen,onSave} from bool to debounce time
This commit is contained in:
parent
a5b8def411
commit
bf698b85d4
@ -6,6 +6,7 @@
|
|||||||
#include "clang_utils.h"
|
#include "clang_utils.h"
|
||||||
#include "filesystem.hh"
|
#include "filesystem.hh"
|
||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
|
#include "match.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include <clang/Frontend/CompilerInstance.h>
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
@ -20,7 +21,10 @@ using namespace clang;
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <ratio>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
namespace chrono = std::chrono;
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
namespace {
|
namespace {
|
||||||
@ -68,22 +72,34 @@ class StoreDiags : public DiagnosticConsumer {
|
|||||||
const LangOptions *LangOpts;
|
const LangOptions *LangOpts;
|
||||||
std::optional<Diag> last;
|
std::optional<Diag> last;
|
||||||
std::vector<Diag> output;
|
std::vector<Diag> output;
|
||||||
|
std::string path;
|
||||||
|
std::unordered_map<unsigned, bool> FID2concerned;
|
||||||
void Flush() {
|
void Flush() {
|
||||||
if (!last)
|
if (!last)
|
||||||
return;
|
return;
|
||||||
bool mentions = last->inside_main || last->edits.size();
|
bool mentions = last->concerned || last->edits.size();
|
||||||
if (!mentions)
|
if (!mentions)
|
||||||
for (auto &N : last->notes)
|
for (auto &N : last->notes)
|
||||||
if (N.inside_main)
|
if (N.concerned)
|
||||||
mentions = true;
|
mentions = true;
|
||||||
if (mentions)
|
if (mentions)
|
||||||
output.push_back(std::move(*last));
|
output.push_back(std::move(*last));
|
||||||
last.reset();
|
last.reset();
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
StoreDiags(std::string path) : path(std::move(path)) {}
|
||||||
std::vector<Diag> Take() {
|
std::vector<Diag> Take() {
|
||||||
return std::move(output);
|
return std::move(output);
|
||||||
}
|
}
|
||||||
|
bool IsConcerned(const SourceManager &SM, SourceLocation L) {
|
||||||
|
FileID FID = SM.getFileID(L);
|
||||||
|
auto it = FID2concerned.try_emplace(FID.getHashValue());
|
||||||
|
if (it.second) {
|
||||||
|
const FileEntry *FE = SM.getFileEntryForID(FID);
|
||||||
|
it.first->second = FE && FileName(*FE) == path;
|
||||||
|
}
|
||||||
|
return it.first->second;
|
||||||
|
}
|
||||||
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
||||||
LangOpts = &Opts;
|
LangOpts = &Opts;
|
||||||
}
|
}
|
||||||
@ -96,24 +112,25 @@ public:
|
|||||||
SourceLocation L = Info.getLocation();
|
SourceLocation L = Info.getLocation();
|
||||||
if (!L.isValid()) return;
|
if (!L.isValid()) return;
|
||||||
const SourceManager &SM = Info.getSourceManager();
|
const SourceManager &SM = Info.getSourceManager();
|
||||||
bool inside_main = SM.isInMainFile(L);
|
StringRef Filename = SM.getFilename(Info.getLocation());
|
||||||
|
bool concerned = IsConcerned(SM, Info.getLocation());
|
||||||
auto fillDiagBase = [&](DiagBase &d) {
|
auto fillDiagBase = [&](DiagBase &d) {
|
||||||
llvm::SmallString<64> Message;
|
llvm::SmallString<64> Message;
|
||||||
Info.FormatDiagnostic(Message);
|
Info.FormatDiagnostic(Message);
|
||||||
d.range =
|
d.range =
|
||||||
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
||||||
d.message = Message.str();
|
d.message = Message.str();
|
||||||
d.inside_main = inside_main;
|
d.concerned = concerned;
|
||||||
d.file = SM.getFilename(Info.getLocation());
|
d.file = Filename;
|
||||||
d.level = Level;
|
d.level = Level;
|
||||||
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
||||||
};
|
};
|
||||||
|
|
||||||
auto addFix = [&](bool SyntheticMessage) -> bool {
|
auto addFix = [&](bool SyntheticMessage) -> bool {
|
||||||
if (!inside_main)
|
if (!concerned)
|
||||||
return false;
|
return false;
|
||||||
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
||||||
if (!SM.isInMainFile(FixIt.RemoveRange.getBegin()))
|
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
|
||||||
return false;
|
return false;
|
||||||
lsTextEdit edit;
|
lsTextEdit edit;
|
||||||
edit.newText = FixIt.CodeToInsert;
|
edit.newText = FixIt.CodeToInsert;
|
||||||
@ -146,9 +163,10 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
|||||||
CompletionSession &session, std::unique_ptr<CompilerInvocation> CI,
|
CompletionSession &session, std::unique_ptr<CompilerInvocation> CI,
|
||||||
DiagnosticConsumer &DC, const WorkingFiles::Snapshot &snapshot,
|
DiagnosticConsumer &DC, const WorkingFiles::Snapshot &snapshot,
|
||||||
std::vector<std::unique_ptr<llvm::MemoryBuffer>> &Bufs) {
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> &Bufs) {
|
||||||
|
std::string main = ResolveIfRelative(session.file.directory, CI->getFrontendOpts().Inputs[0].getFile());
|
||||||
for (auto &file : snapshot.files) {
|
for (auto &file : snapshot.files) {
|
||||||
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
|
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
|
||||||
if (file.filename == session.file.filename) {
|
if (file.filename == main)
|
||||||
if (auto Preamble = session.GetPreamble()) {
|
if (auto Preamble = session.GetPreamble()) {
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
#if LLVM_VERSION_MAJOR >= 7
|
||||||
Preamble->Preamble.OverridePreamble(*CI, session.FS,
|
Preamble->Preamble.OverridePreamble(*CI, session.FS,
|
||||||
@ -157,15 +175,11 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
|||||||
Preamble->Preamble.AddImplicitPreamble(*CI, session.FS,
|
Preamble->Preamble.AddImplicitPreamble(*CI, session.FS,
|
||||||
Bufs.back().get());
|
Bufs.back().get());
|
||||||
#endif
|
#endif
|
||||||
} else {
|
continue;
|
||||||
CI->getPreprocessorOpts().addRemappedFile(
|
|
||||||
CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get());
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
CI->getPreprocessorOpts().addRemappedFile(file.filename,
|
CI->getPreprocessorOpts().addRemappedFile(file.filename,
|
||||||
Bufs.back().get());
|
Bufs.back().get());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
||||||
Clang->setInvocation(std::move(CI));
|
Clang->setInvocation(std::move(CI));
|
||||||
@ -190,52 +204,53 @@ bool Parse(CompilerInstance &Clang) {
|
|||||||
|
|
||||||
void CompletionPreloadMain(CompletionManager *manager) {
|
void CompletionPreloadMain(CompletionManager *manager) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Fetching the completion request blocks until we have a request.
|
|
||||||
auto request = manager->preload_requests_.Dequeue();
|
auto request = manager->preload_requests_.Dequeue();
|
||||||
|
|
||||||
// If we don't get a session then that means we don't care about the file
|
bool is_open = false;
|
||||||
// anymore - abandon the request.
|
std::shared_ptr<CompletionSession> session =
|
||||||
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
|
manager->TryGetSession(request.path, true, &is_open);
|
||||||
request.path, false /*mark_as_completion*/, false /*create_if_needed*/);
|
|
||||||
if (!session)
|
if (!session)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// For inferred session, don't build preamble because changes in a.h will
|
||||||
|
// invalidate it.
|
||||||
|
if (!session->inferred) {
|
||||||
const auto &args = session->file.args;
|
const auto &args = session->file.args;
|
||||||
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
|
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
|
||||||
{StripFileType(session->file.filename)});
|
{StripFileType(session->file.filename)});
|
||||||
|
|
||||||
LOG_S(INFO) << "create completion session for " << session->file.filename;
|
|
||||||
if (std::unique_ptr<CompilerInvocation> CI =
|
if (std::unique_ptr<CompilerInvocation> CI =
|
||||||
BuildCompilerInvocation(args, session->FS))
|
BuildCompilerInvocation(args, session->FS))
|
||||||
session->BuildPreamble(*CI);
|
session->BuildPreamble(*CI, request.path);
|
||||||
if (g_config->diagnostics.onSave) {
|
}
|
||||||
|
int debounce =
|
||||||
|
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
||||||
|
if (debounce >= 0) {
|
||||||
lsTextDocumentIdentifier document;
|
lsTextDocumentIdentifier document;
|
||||||
document.uri = lsDocumentUri::FromPath(request.path);
|
document.uri = lsDocumentUri::FromPath(request.path);
|
||||||
manager->diagnostic_request_.PushBack({document}, true);
|
manager->DiagnosticsUpdate(request.path, debounce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionMain(CompletionManager *completion_manager) {
|
void CompletionMain(CompletionManager *manager) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Fetching the completion request blocks until we have a request.
|
// Fetching the completion request blocks until we have a request.
|
||||||
std::unique_ptr<CompletionManager::CompletionRequest> request =
|
std::unique_ptr<CompletionManager::CompletionRequest> request =
|
||||||
completion_manager->completion_request_.Dequeue();
|
manager->completion_request_.Dequeue();
|
||||||
|
|
||||||
// Drop older requests if we're not buffering.
|
// Drop older requests if we're not buffering.
|
||||||
while (g_config->completion.dropOldRequests &&
|
while (g_config->completion.dropOldRequests &&
|
||||||
!completion_manager->completion_request_.IsEmpty()) {
|
!manager->completion_request_.IsEmpty()) {
|
||||||
completion_manager->on_dropped_(request->id);
|
manager->on_dropped_(request->id);
|
||||||
request->Consumer.reset();
|
request->Consumer.reset();
|
||||||
request->on_complete(nullptr);
|
request->on_complete(nullptr);
|
||||||
request = completion_manager->completion_request_.Dequeue();
|
request = manager->completion_request_.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string path = request->document.uri.GetPath();
|
std::string path = request->document.uri.GetPath();
|
||||||
|
|
||||||
std::shared_ptr<CompletionSession> session =
|
std::shared_ptr<CompletionSession> session =
|
||||||
completion_manager->TryGetSession(path, true /*mark_as_completion*/,
|
manager->TryGetSession(path, false);
|
||||||
true /*create_if_needed*/);
|
|
||||||
|
|
||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> CI =
|
||||||
BuildCompilerInvocation(session->file.args, session->FS);
|
BuildCompilerInvocation(session->file.args, session->FS);
|
||||||
@ -251,7 +266,7 @@ void CompletionMain(CompletionManager *completion_manager) {
|
|||||||
|
|
||||||
DiagnosticConsumer DC;
|
DiagnosticConsumer DC;
|
||||||
WorkingFiles::Snapshot snapshot =
|
WorkingFiles::Snapshot snapshot =
|
||||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
||||||
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
||||||
if (!Clang)
|
if (!Clang)
|
||||||
@ -285,25 +300,31 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
|
void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
|
||||||
if (d.inside_main)
|
if (d.concerned)
|
||||||
OS << llvm::sys::path::filename(d.file);
|
OS << llvm::sys::path::filename(d.file);
|
||||||
else
|
else
|
||||||
OS << d.file;
|
OS << d.file;
|
||||||
auto pos = d.range.start;
|
auto pos = d.range.start;
|
||||||
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
||||||
<< (d.inside_main ? " " : "\n");
|
<< (d.concerned ? " " : "\n");
|
||||||
OS << diagLeveltoString(d.level) << ": " << d.message;
|
OS << diagLeveltoString(d.level) << ": " << d.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagnosticMain(CompletionManager *manager) {
|
void DiagnosticMain(CompletionManager *manager) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Fetching the completion request blocks until we have a request.
|
|
||||||
CompletionManager::DiagnosticRequest request =
|
CompletionManager::DiagnosticRequest request =
|
||||||
manager->diagnostic_request_.Dequeue();
|
manager->diagnostic_request_.Dequeue();
|
||||||
std::string path = request.document.uri.GetPath();
|
const std::string &path = request.path;
|
||||||
|
int64_t wait = request.wait_until -
|
||||||
|
chrono::duration_cast<chrono::milliseconds>(
|
||||||
|
chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
if (wait > 0)
|
||||||
|
std::this_thread::sleep_for(chrono::duration<int64_t, std::milli>(
|
||||||
|
std::min(wait, request.debounce)));
|
||||||
|
|
||||||
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
|
std::shared_ptr<CompletionSession> session =
|
||||||
path, true /*mark_as_completion*/, true /*create_if_needed*/);
|
manager->TryGetSession(path, false);
|
||||||
|
|
||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> CI =
|
||||||
BuildCompilerInvocation(session->file.args, session->FS);
|
BuildCompilerInvocation(session->file.args, session->FS);
|
||||||
@ -311,7 +332,7 @@ void DiagnosticMain(CompletionManager *manager) {
|
|||||||
continue;
|
continue;
|
||||||
CI->getDiagnosticOpts().IgnoreWarnings = false;
|
CI->getDiagnosticOpts().IgnoreWarnings = false;
|
||||||
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
||||||
StoreDiags DC;
|
StoreDiags DC(path);
|
||||||
WorkingFiles::Snapshot snapshot =
|
WorkingFiles::Snapshot snapshot =
|
||||||
manager->working_files_->AsSnapshot({StripFileType(path)});
|
manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
||||||
@ -347,9 +368,12 @@ void DiagnosticMain(CompletionManager *manager) {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<Diag> diags = DC.Take();
|
||||||
|
if (std::shared_ptr<PreambleData> preamble = session->GetPreamble())
|
||||||
|
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
|
||||||
std::vector<lsDiagnostic> ls_diags;
|
std::vector<lsDiagnostic> ls_diags;
|
||||||
for (auto &d : DC.Take()) {
|
for (auto &d : diags) {
|
||||||
if (!d.inside_main)
|
if (!d.concerned)
|
||||||
continue;
|
continue;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
llvm::raw_string_ostream OS(buf);
|
llvm::raw_string_ostream OS(buf);
|
||||||
@ -364,7 +388,7 @@ void DiagnosticMain(CompletionManager *manager) {
|
|||||||
OS.flush();
|
OS.flush();
|
||||||
ls_diag.message = std::move(buf);
|
ls_diag.message = std::move(buf);
|
||||||
for (auto &n : d.notes) {
|
for (auto &n : d.notes) {
|
||||||
if (!n.inside_main)
|
if (!n.concerned)
|
||||||
continue;
|
continue;
|
||||||
lsDiagnostic &ls_diag1 = ls_diags.emplace_back();
|
lsDiagnostic &ls_diag1 = ls_diags.emplace_back();
|
||||||
Fill(n, ls_diag1);
|
Fill(n, ls_diag1);
|
||||||
@ -374,6 +398,12 @@ void DiagnosticMain(CompletionManager *manager) {
|
|||||||
ls_diag1.message = std::move(buf);
|
ls_diag1.message = std::move(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(session->wfiles->files_mutex);
|
||||||
|
if (WorkingFile *wfile = session->wfiles->GetFileByFilenameNoLock(path))
|
||||||
|
wfile->diagnostics_ = ls_diags;
|
||||||
|
}
|
||||||
manager->on_diagnostic_(path, ls_diags);
|
manager->on_diagnostic_(path, ls_diags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,9 +415,10 @@ std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
|
|||||||
return preamble;
|
return preamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
void CompletionSession::BuildPreamble(CompilerInvocation &CI,
|
||||||
|
const std::string &main) {
|
||||||
std::shared_ptr<PreambleData> OldP = GetPreamble();
|
std::shared_ptr<PreambleData> OldP = GetPreamble();
|
||||||
std::string content = wfiles->GetContent(file.filename);
|
std::string content = wfiles->GetContent(main);
|
||||||
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
||||||
llvm::MemoryBuffer::getMemBuffer(content);
|
llvm::MemoryBuffer::getMemBuffer(content);
|
||||||
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
||||||
@ -401,7 +432,7 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
|||||||
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
|
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
StoreDiags DC;
|
StoreDiags DC(main);
|
||||||
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
||||||
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
||||||
PreambleCallbacks PP;
|
PreambleCallbacks PP;
|
||||||
@ -421,8 +452,8 @@ CompletionManager::CompletionManager(Project *project,
|
|||||||
OnDropped on_dropped)
|
OnDropped on_dropped)
|
||||||
: project_(project), working_files_(working_files),
|
: project_(project), working_files_(working_files),
|
||||||
on_diagnostic_(on_diagnostic), on_dropped_(on_dropped),
|
on_diagnostic_(on_diagnostic), on_dropped_(on_dropped),
|
||||||
preloaded_sessions_(kMaxPreloadedSessions),
|
preloads(kMaxPreloadedSessions),
|
||||||
completion_sessions_(kMaxCompletionSessions),
|
sessions(kMaxCompletionSessions),
|
||||||
PCH(std::make_shared<PCHContainerOperations>()) {
|
PCH(std::make_shared<PCHContainerOperations>()) {
|
||||||
std::thread([&]() {
|
std::thread([&]() {
|
||||||
set_thread_name("comp");
|
set_thread_name("comp");
|
||||||
@ -441,22 +472,28 @@ CompletionManager::CompletionManager(Project *project,
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::CodeComplete(
|
void CompletionManager::DiagnosticsUpdate(const std::string &path,
|
||||||
const lsRequestId &id,
|
int debounce) {
|
||||||
const lsTextDocumentPositionParams &completion_location,
|
static GroupMatch match(g_config->diagnostics.whitelist,
|
||||||
const OnComplete &on_complete) {
|
g_config->diagnostics.blacklist);
|
||||||
|
if (!match.IsMatch(path))
|
||||||
|
return;
|
||||||
|
int64_t now = chrono::duration_cast<chrono::milliseconds>(
|
||||||
|
chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
bool flag = false;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(diag_mutex);
|
||||||
|
int64_t &next = next_diag[path];
|
||||||
|
auto &d = g_config->diagnostics;
|
||||||
|
if (next <= now ||
|
||||||
|
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
|
||||||
|
next = now + debounce;
|
||||||
|
flag = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void CompletionManager::DiagnosticsUpdate(
|
if (flag)
|
||||||
const lsTextDocumentIdentifier &document) {
|
diagnostic_request_.PushBack({path, now + debounce, debounce}, false);
|
||||||
bool has = false;
|
|
||||||
diagnostic_request_.Iterate([&](const DiagnosticRequest &request) {
|
|
||||||
if (request.document.uri == document.uri)
|
|
||||||
has = true;
|
|
||||||
});
|
|
||||||
if (!has)
|
|
||||||
diagnostic_request_.PushBack(DiagnosticRequest{document},
|
|
||||||
true /*priority*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::NotifyView(const std::string &path) {
|
void CompletionManager::NotifyView(const std::string &path) {
|
||||||
@ -466,94 +503,67 @@ void CompletionManager::NotifyView(const std::string &path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::NotifySave(const std::string &filename) {
|
void CompletionManager::NotifySave(const std::string &filename) {
|
||||||
//
|
|
||||||
// On save, always reparse.
|
|
||||||
//
|
|
||||||
|
|
||||||
EnsureCompletionOrCreatePreloadSession(filename);
|
EnsureCompletionOrCreatePreloadSession(filename);
|
||||||
preload_requests_.PushBack(PreloadRequest{filename}, true);
|
preload_requests_.PushBack(PreloadRequest{filename}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::NotifyClose(const std::string &filename) {
|
void CompletionManager::OnClose(const std::string &filename) {
|
||||||
//
|
|
||||||
// On close, we clear any existing CompletionSession instance.
|
|
||||||
//
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||||
|
preloads.TryTake(filename);
|
||||||
// Take and drop. It's okay if we don't actually drop the file, it'll
|
sessions.TryTake(filename);
|
||||||
// eventually get pushed out of the caches as the user opens other files.
|
|
||||||
auto preloaded_ptr = preloaded_sessions_.TryTake(filename);
|
|
||||||
LOG_IF_S(INFO, !!preloaded_ptr)
|
|
||||||
<< "Dropped preloaded-based code completion session for " << filename;
|
|
||||||
auto completion_ptr = completion_sessions_.TryTake(filename);
|
|
||||||
LOG_IF_S(INFO, !!completion_ptr)
|
|
||||||
<< "Dropped completion-based code completion session for " << filename;
|
|
||||||
|
|
||||||
// We should never have both a preloaded and completion session.
|
|
||||||
assert((preloaded_ptr && completion_ptr) == false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
|
bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
|
||||||
const std::string &path) {
|
const std::string &path) {
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||||
|
if (preloads.TryGet(path) || sessions.TryGet(path))
|
||||||
// Check for an existing CompletionSession.
|
|
||||||
if (preloaded_sessions_.TryGet(path) ||
|
|
||||||
completion_sessions_.TryGet(path)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// No CompletionSession, create new one.
|
// No CompletionSession, create new one.
|
||||||
auto session = std::make_shared<ccls::CompletionSession>(
|
auto session = std::make_shared<ccls::CompletionSession>(
|
||||||
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
||||||
preloaded_sessions_.Insert(session->file.filename, session);
|
if (session->file.filename != path) {
|
||||||
|
session->inferred = true;
|
||||||
|
session->file.filename = path;
|
||||||
|
}
|
||||||
|
preloads.Insert(path, session);
|
||||||
|
LOG_S(INFO) << "create preload session for " << path;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ccls::CompletionSession>
|
std::shared_ptr<ccls::CompletionSession>
|
||||||
CompletionManager::TryGetSession(const std::string &path,
|
CompletionManager::TryGetSession(const std::string &path, bool preload,
|
||||||
bool mark_as_completion,
|
bool *is_open) {
|
||||||
bool create_if_needed) {
|
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||||
|
std::shared_ptr<ccls::CompletionSession> session = preloads.TryGet(path);
|
||||||
|
|
||||||
// Try to find a preloaded session.
|
if (session) {
|
||||||
std::shared_ptr<ccls::CompletionSession> preloaded =
|
if (!preload) {
|
||||||
preloaded_sessions_.TryGet(path);
|
preloads.TryTake(path);
|
||||||
|
sessions.Insert(path, session);
|
||||||
if (preloaded) {
|
if (is_open)
|
||||||
// If this request is for a completion, we should move it to
|
*is_open = true;
|
||||||
// |completion_sessions|.
|
|
||||||
if (mark_as_completion) {
|
|
||||||
preloaded_sessions_.TryTake(path);
|
|
||||||
completion_sessions_.Insert(path, preloaded);
|
|
||||||
}
|
}
|
||||||
return preloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find a completion session. If none create one.
|
|
||||||
std::shared_ptr<ccls::CompletionSession> session =
|
|
||||||
completion_sessions_.TryGet(path);
|
|
||||||
if (!session && create_if_needed) {
|
|
||||||
session = std::make_shared<ccls::CompletionSession>(
|
|
||||||
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
|
||||||
completion_sessions_.Insert(path, session);
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::FlushSession(const std::string &path) {
|
session = sessions.TryGet(path);
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
if (!session && !preload) {
|
||||||
|
session = std::make_shared<ccls::CompletionSession>(
|
||||||
|
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
||||||
|
sessions.Insert(path, session);
|
||||||
|
LOG_S(INFO) << "create session for " << path;
|
||||||
|
if (is_open)
|
||||||
|
*is_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
preloaded_sessions_.TryTake(path);
|
return session;
|
||||||
completion_sessions_.TryTake(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::FlushAllSessions() {
|
void CompletionManager::FlushAllSessions() {
|
||||||
LOG_S(INFO) << "flush all clang complete sessions";
|
LOG_S(INFO) << "flush all clang complete sessions";
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||||
|
|
||||||
preloaded_sessions_.Clear();
|
preloads.Clear();
|
||||||
completion_sessions_.Clear();
|
sessions.Clear();
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ struct DiagBase {
|
|||||||
std::string file;
|
std::string file;
|
||||||
clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note;
|
clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note;
|
||||||
unsigned category;
|
unsigned category;
|
||||||
bool inside_main = false;
|
bool concerned = false;
|
||||||
};
|
};
|
||||||
struct Note : DiagBase {};
|
struct Note : DiagBase {};
|
||||||
struct Diag : DiagBase {
|
struct Diag : DiagBase {
|
||||||
@ -46,10 +46,10 @@ struct CompletionSession
|
|||||||
: public std::enable_shared_from_this<CompletionSession> {
|
: public std::enable_shared_from_this<CompletionSession> {
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::shared_ptr<PreambleData> preamble;
|
std::shared_ptr<PreambleData> preamble;
|
||||||
std::vector<Diag> diags;
|
|
||||||
|
|
||||||
Project::Entry file;
|
Project::Entry file;
|
||||||
WorkingFiles *wfiles;
|
WorkingFiles *wfiles;
|
||||||
|
bool inferred = false;
|
||||||
|
|
||||||
// TODO share
|
// TODO share
|
||||||
llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> FS =
|
llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> FS =
|
||||||
@ -61,7 +61,7 @@ struct CompletionSession
|
|||||||
: file(file), wfiles(wfiles), PCH(PCH) {}
|
: file(file), wfiles(wfiles), PCH(PCH) {}
|
||||||
|
|
||||||
std::shared_ptr<PreambleData> GetPreamble();
|
std::shared_ptr<PreambleData> GetPreamble();
|
||||||
void BuildPreamble(clang::CompilerInvocation &CI);
|
void BuildPreamble(clang::CompilerInvocation &CI, const std::string &main);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,19 +95,16 @@ struct CompletionManager {
|
|||||||
OnComplete on_complete;
|
OnComplete on_complete;
|
||||||
};
|
};
|
||||||
struct DiagnosticRequest {
|
struct DiagnosticRequest {
|
||||||
lsTextDocumentIdentifier document;
|
std::string path;
|
||||||
|
int64_t wait_until;
|
||||||
|
int64_t debounce;
|
||||||
};
|
};
|
||||||
|
|
||||||
CompletionManager(Project *project, WorkingFiles *working_files,
|
CompletionManager(Project *project, WorkingFiles *working_files,
|
||||||
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
||||||
|
|
||||||
// Start a code completion at the given location. |on_complete| will run when
|
|
||||||
// completion results are available. |on_complete| may run on any thread.
|
|
||||||
void CodeComplete(const lsRequestId &request_id,
|
|
||||||
const lsTextDocumentPositionParams &completion_location,
|
|
||||||
const OnComplete &on_complete);
|
|
||||||
// Request a diagnostics update.
|
// Request a diagnostics update.
|
||||||
void DiagnosticsUpdate(const lsTextDocumentIdentifier &document);
|
void DiagnosticsUpdate(const std::string &path, int debounce);
|
||||||
|
|
||||||
// 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.
|
||||||
@ -117,7 +114,7 @@ struct CompletionManager {
|
|||||||
void NotifySave(const std::string &path);
|
void NotifySave(const std::string &path);
|
||||||
// Notify the completion manager that |filename| has been closed. Any existing
|
// Notify the completion manager that |filename| has been closed. Any existing
|
||||||
// completion session will be dropped.
|
// completion session will be dropped.
|
||||||
void NotifyClose(const std::string &path);
|
void OnClose(const std::string &path);
|
||||||
|
|
||||||
// Ensures there is a completion or preloaded session. Returns true if a new
|
// Ensures there is a completion or preloaded session. Returns true if a new
|
||||||
// session was created.
|
// session was created.
|
||||||
@ -125,11 +122,8 @@ struct CompletionManager {
|
|||||||
// Tries to find an edit session for |filename|. This will move the session
|
// Tries to find an edit session for |filename|. This will move the session
|
||||||
// from view to edit.
|
// from view to edit.
|
||||||
std::shared_ptr<ccls::CompletionSession>
|
std::shared_ptr<ccls::CompletionSession>
|
||||||
TryGetSession(const std::string &path, bool mark_as_completion,
|
TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr);
|
||||||
bool create_if_needed);
|
|
||||||
|
|
||||||
// Flushes all saved sessions with the supplied filename
|
|
||||||
void FlushSession(const std::string &path);
|
|
||||||
// Flushes all saved sessions
|
// Flushes all saved sessions
|
||||||
void FlushAllSessions(void);
|
void FlushAllSessions(void);
|
||||||
|
|
||||||
@ -147,14 +141,17 @@ struct CompletionManager {
|
|||||||
|
|
||||||
// CompletionSession instances which are preloaded, ie, files which the user
|
// CompletionSession instances which are preloaded, ie, files which the user
|
||||||
// has viewed but not requested code completion for.
|
// has viewed but not requested code completion for.
|
||||||
LruSessionCache preloaded_sessions_;
|
LruSessionCache preloads;
|
||||||
// CompletionSession instances which the user has actually performed
|
// CompletionSession instances which the user has actually performed
|
||||||
// completion on. This is more rare so these instances tend to stay alive
|
// completion on. This is more rare so these instances tend to stay alive
|
||||||
// much longer than the ones in |preloaded_sessions_|.
|
// much longer than the ones in |preloaded_sessions_|.
|
||||||
LruSessionCache completion_sessions_;
|
LruSessionCache sessions;
|
||||||
// Mutex which protects |view_sessions_| and |edit_sessions_|.
|
// Mutex which protects |view_sessions_| and |edit_sessions_|.
|
||||||
std::mutex sessions_lock_;
|
std::mutex sessions_lock_;
|
||||||
|
|
||||||
|
std::mutex diag_mutex;
|
||||||
|
std::unordered_map<std::string, int64_t> next_diag;
|
||||||
|
|
||||||
// Request a code completion at the given location.
|
// Request a code completion at the given location.
|
||||||
ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_;
|
ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_;
|
||||||
ThreadedQueue<DiagnosticRequest> diagnostic_request_;
|
ThreadedQueue<DiagnosticRequest> diagnostic_request_;
|
||||||
|
26
src/config.h
26
src/config.h
@ -144,20 +144,18 @@ struct Config {
|
|||||||
// blacklisted files.
|
// blacklisted files.
|
||||||
std::vector<std::string> blacklist;
|
std::vector<std::string> blacklist;
|
||||||
|
|
||||||
// How often should ccls publish diagnostics in completion?
|
// Time to wait before computing diagnostics for textDocument/didChange.
|
||||||
// -1: never
|
// -1: disable diagnostics on change
|
||||||
// 0: as often as possible
|
// 0: immediately
|
||||||
// xxx: at most every xxx milliseconds
|
// positive (e.g. 500): wait for 500 milliseconds. didChange requests in
|
||||||
int frequencyMs = 0;
|
// this period of time will only cause one computation.
|
||||||
|
int onChange = 1000;
|
||||||
|
|
||||||
// If true, diagnostics will be reported for textDocument/didChange.
|
// Time to wait before computing diagnostics for textDocument/didOpen.
|
||||||
bool onChange = true;
|
int onOpen = 0;
|
||||||
|
|
||||||
// If true, diagnostics will be reported for textDocument/didOpen.
|
// Time to wait before computing diagnostics for textDocument/didSave.
|
||||||
bool onOpen = true;
|
int onSave = 0;
|
||||||
|
|
||||||
// If true, diagnostics will be reported for textDocument/didSave.
|
|
||||||
bool onSave = true;
|
|
||||||
|
|
||||||
bool spellChecking = true;
|
bool spellChecking = true;
|
||||||
|
|
||||||
@ -246,8 +244,8 @@ MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
|
|||||||
dropOldRequests, duplicateOptional, filterAndSort,
|
dropOldRequests, duplicateOptional, filterAndSort,
|
||||||
includeBlacklist, includeMaxPathSize,
|
includeBlacklist, includeMaxPathSize,
|
||||||
includeSuffixWhitelist, includeWhitelist);
|
includeSuffixWhitelist, includeWhitelist);
|
||||||
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
|
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave,
|
||||||
onOpen, onSave, spellChecking, whitelist)
|
spellChecking, whitelist)
|
||||||
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
|
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
|
||||||
MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion,
|
MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion,
|
||||||
multiVersionBlacklist, multiVersionWhitelist, onChange,
|
multiVersionBlacklist, multiVersionWhitelist, onChange,
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
struct CompletionManager;
|
struct CompletionManager;
|
||||||
struct Config;
|
struct Config;
|
||||||
class DiagnosticsPublisher;
|
|
||||||
struct GroupMatch;
|
struct GroupMatch;
|
||||||
struct VFS;
|
struct VFS;
|
||||||
struct IncludeComplete;
|
struct IncludeComplete;
|
||||||
@ -78,7 +77,6 @@ struct MessageHandler {
|
|||||||
DB *db = nullptr;
|
DB *db = nullptr;
|
||||||
MultiQueueWaiter *waiter = nullptr;
|
MultiQueueWaiter *waiter = nullptr;
|
||||||
Project *project = nullptr;
|
Project *project = nullptr;
|
||||||
DiagnosticsPublisher *diag_pub = nullptr;
|
|
||||||
VFS *vfs = nullptr;
|
VFS *vfs = nullptr;
|
||||||
SemanticHighlight *highlight = nullptr;
|
SemanticHighlight *highlight = nullptr;
|
||||||
WorkingFiles *working_files = nullptr;
|
WorkingFiles *working_files = nullptr;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright 2017-2018 ccls Authors
|
// Copyright 2017-2018 ccls Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#include "clang_complete.hh"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "message_handler.h"
|
#include "message_handler.h"
|
||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
@ -38,6 +39,7 @@ struct Handler_CclsReload : BaseMessageHandler<In_CclsReload> {
|
|||||||
vfs->Clear();
|
vfs->Clear();
|
||||||
db->clear();
|
db->clear();
|
||||||
project->Index(working_files, lsRequestId());
|
project->Index(working_files, lsRequestId());
|
||||||
|
clang_complete->FlushAllSessions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +474,6 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
|
|||||||
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
|
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
|
||||||
}
|
}
|
||||||
|
|
||||||
diag_pub->Init();
|
|
||||||
idx::Init();
|
idx::Init();
|
||||||
highlight->Init();
|
highlight->Init();
|
||||||
|
|
||||||
|
@ -30,9 +30,8 @@ struct Handler_TextDocumentDidChange
|
|||||||
if (g_config->index.onChange)
|
if (g_config->index.onChange)
|
||||||
pipeline::Index(path, {}, IndexMode::OnChange);
|
pipeline::Index(path, {}, IndexMode::OnChange);
|
||||||
clang_complete->NotifyView(path);
|
clang_complete->NotifyView(path);
|
||||||
if (g_config->diagnostics.onChange)
|
if (g_config->diagnostics.onChange >= 0)
|
||||||
clang_complete->DiagnosticsUpdate(
|
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
|
||||||
params.textDocument.AsTextDocumentIdentifier());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);
|
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);
|
||||||
|
@ -35,7 +35,7 @@ struct Handler_TextDocumentDidClose
|
|||||||
|
|
||||||
// Remove internal state.
|
// Remove internal state.
|
||||||
working_files->OnClose(request->params.textDocument);
|
working_files->OnClose(request->params.textDocument);
|
||||||
clang_complete->NotifyClose(path);
|
clang_complete->OnClose(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);
|
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);
|
||||||
|
@ -59,14 +59,10 @@ struct Handler_TextDocumentDidOpen
|
|||||||
project->SetArgsForFile(args, path);
|
project->SetArgsForFile(args, path);
|
||||||
|
|
||||||
// Submit new index request if it is not a header file.
|
// Submit new index request if it is not a header file.
|
||||||
if (SourceFileLanguage(path) != LanguageId::Unknown) {
|
if (SourceFileLanguage(path) != LanguageId::Unknown)
|
||||||
pipeline::Index(path, args, IndexMode::Normal);
|
pipeline::Index(path, args, IndexMode::Normal);
|
||||||
clang_complete->FlushSession(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
clang_complete->NotifyView(path);
|
clang_complete->NotifyView(path);
|
||||||
if (g_config->diagnostics.onOpen)
|
|
||||||
clang_complete->DiagnosticsUpdate({params.textDocument.uri});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
|
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);
|
||||||
|
@ -52,12 +52,12 @@ struct Handler_WorkspaceDidChangeWatchedFiles
|
|||||||
if (mode == IndexMode::Normal)
|
if (mode == IndexMode::Normal)
|
||||||
clang_complete->NotifySave(path);
|
clang_complete->NotifySave(path);
|
||||||
else
|
else
|
||||||
clang_complete->FlushSession(path);
|
clang_complete->OnClose(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case lsFileChangeType::Deleted:
|
case lsFileChangeType::Deleted:
|
||||||
pipeline::Index(path, {}, mode);
|
pipeline::Index(path, {}, mode);
|
||||||
clang_complete->FlushSession(path);
|
clang_complete->OnClose(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,41 +27,6 @@ using namespace llvm;
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void DiagnosticsPublisher::Init() {
|
|
||||||
frequencyMs_ = g_config->diagnostics.frequencyMs;
|
|
||||||
match_ = std::make_unique<GroupMatch>(g_config->diagnostics.whitelist,
|
|
||||||
g_config->diagnostics.blacklist);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiagnosticsPublisher::Publish(WorkingFiles *working_files,
|
|
||||||
std::string path,
|
|
||||||
std::vector<lsDiagnostic> diagnostics) {
|
|
||||||
bool good = true;
|
|
||||||
// Cache diagnostics so we can show fixits.
|
|
||||||
working_files->DoActionOnFile(path, [&](WorkingFile *working_file) {
|
|
||||||
if (working_file) {
|
|
||||||
good = working_file->diagnostics_.empty();
|
|
||||||
working_file->diagnostics_ = diagnostics;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
int64_t now =
|
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
|
||||||
.count();
|
|
||||||
if (frequencyMs_ >= 0 &&
|
|
||||||
(nextPublish_ <= now || (!good && diagnostics.empty())) &&
|
|
||||||
match_->IsMatch(path)) {
|
|
||||||
nextPublish_ = now + frequencyMs_;
|
|
||||||
|
|
||||||
Out_TextDocumentPublishDiagnostics out;
|
|
||||||
out.params.uri = lsDocumentUri::FromPath(path);
|
|
||||||
out.params.diagnostics = diagnostics;
|
|
||||||
ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics,
|
|
||||||
out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VFS::Clear() {
|
void VFS::Clear() {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
state.clear();
|
state.clear();
|
||||||
@ -485,12 +450,15 @@ void MainLoop() {
|
|||||||
SemanticHighlight highlight;
|
SemanticHighlight highlight;
|
||||||
WorkingFiles working_files;
|
WorkingFiles working_files;
|
||||||
VFS vfs;
|
VFS vfs;
|
||||||
DiagnosticsPublisher diag_pub;
|
|
||||||
|
|
||||||
CompletionManager clang_complete(
|
CompletionManager clang_complete(
|
||||||
&project, &working_files,
|
&project, &working_files,
|
||||||
[&](std::string path, std::vector<lsDiagnostic> diagnostics) {
|
[&](std::string path, std::vector<lsDiagnostic> diagnostics) {
|
||||||
diag_pub.Publish(&working_files, path, diagnostics);
|
Out_TextDocumentPublishDiagnostics out;
|
||||||
|
out.params.uri = lsDocumentUri::FromPath(path);
|
||||||
|
out.params.diagnostics = diagnostics;
|
||||||
|
ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics,
|
||||||
|
out);
|
||||||
},
|
},
|
||||||
[](lsRequestId id) {
|
[](lsRequestId id) {
|
||||||
if (id.Valid()) {
|
if (id.Valid()) {
|
||||||
@ -511,7 +479,6 @@ void MainLoop() {
|
|||||||
handler->db = &db;
|
handler->db = &db;
|
||||||
handler->waiter = indexer_waiter;
|
handler->waiter = indexer_waiter;
|
||||||
handler->project = &project;
|
handler->project = &project;
|
||||||
handler->diag_pub = &diag_pub;
|
|
||||||
handler->vfs = &vfs;
|
handler->vfs = &vfs;
|
||||||
handler->highlight = &highlight;
|
handler->highlight = &highlight;
|
||||||
handler->working_files = &working_files;
|
handler->working_files = &working_files;
|
||||||
|
@ -19,18 +19,6 @@ struct Project;
|
|||||||
struct WorkingFiles;
|
struct WorkingFiles;
|
||||||
struct lsBaseOutMessage;
|
struct lsBaseOutMessage;
|
||||||
|
|
||||||
class DiagnosticsPublisher {
|
|
||||||
std::unique_ptr<GroupMatch> match_;
|
|
||||||
int64_t nextPublish_ = 0;
|
|
||||||
int frequencyMs_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void Init();
|
|
||||||
void Publish(WorkingFiles* working_files,
|
|
||||||
std::string path,
|
|
||||||
std::vector<lsDiagnostic> diagnostics);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VFS {
|
struct VFS {
|
||||||
struct State {
|
struct State {
|
||||||
int64_t timestamp;
|
int64_t timestamp;
|
||||||
|
@ -54,15 +54,6 @@ enum OptionClass {
|
|||||||
Separate,
|
Separate,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ResolveIfRelative(const std::string &directory,
|
|
||||||
const std::string &path) {
|
|
||||||
if (sys::path::is_absolute(path))
|
|
||||||
return path;
|
|
||||||
SmallString<256> Ret;
|
|
||||||
sys::path::append(Ret, directory, path);
|
|
||||||
return NormalizePath(Ret.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProjectProcessor {
|
struct ProjectProcessor {
|
||||||
ProjectConfig *config;
|
ProjectConfig *config;
|
||||||
std::unordered_set<size_t> command_set;
|
std::unordered_set<size_t> command_set;
|
||||||
|
@ -105,6 +105,15 @@ std::string EscapeFileName(std::string path) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ResolveIfRelative(const std::string &directory,
|
||||||
|
const std::string &path) {
|
||||||
|
if (sys::path::is_absolute(path))
|
||||||
|
return path;
|
||||||
|
SmallString<256> Ret;
|
||||||
|
sys::path::append(Ret, directory, path);
|
||||||
|
return NormalizePath(Ret.str());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string &path) {
|
std::optional<int64_t> LastWriteTime(const std::string &path) {
|
||||||
sys::fs::file_status Status;
|
sys::fs::file_status Status;
|
||||||
if (sys::fs::status(path, Status))
|
if (sys::fs::status(path, Status))
|
||||||
|
@ -58,6 +58,9 @@ void EnsureEndsInSlash(std::string &path);
|
|||||||
// e.g. foo/bar.c => foo_bar.c
|
// e.g. foo/bar.c => foo_bar.c
|
||||||
std::string EscapeFileName(std::string path);
|
std::string EscapeFileName(std::string path);
|
||||||
|
|
||||||
|
std::string ResolveIfRelative(const std::string &directory,
|
||||||
|
const std::string &path);
|
||||||
|
|
||||||
std::optional<int64_t> LastWriteTime(const std::string &path);
|
std::optional<int64_t> LastWriteTime(const std::string &path);
|
||||||
std::optional<std::string> ReadContent(const std::string &filename);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user