Publish diagnostics of inferred files and change diagnostics.{onChange,onOpen,onSave} from bool to debounce time

This commit is contained in:
Fangrui Song 2018-09-22 01:37:00 -07:00
parent a5b8def411
commit bf698b85d4
15 changed files with 185 additions and 228 deletions

View File

@ -6,6 +6,7 @@
#include "clang_utils.h"
#include "filesystem.hh"
#include "log.hh"
#include "match.h"
#include "platform.h"
#include <clang/Frontend/CompilerInstance.h>
@ -20,7 +21,10 @@ using namespace clang;
using namespace llvm;
#include <algorithm>
#include <chrono>
#include <ratio>
#include <thread>
namespace chrono = std::chrono;
namespace ccls {
namespace {
@ -68,22 +72,34 @@ class StoreDiags : public DiagnosticConsumer {
const LangOptions *LangOpts;
std::optional<Diag> last;
std::vector<Diag> output;
std::string path;
std::unordered_map<unsigned, bool> FID2concerned;
void Flush() {
if (!last)
return;
bool mentions = last->inside_main || last->edits.size();
bool mentions = last->concerned || last->edits.size();
if (!mentions)
for (auto &N : last->notes)
if (N.inside_main)
if (N.concerned)
mentions = true;
if (mentions)
output.push_back(std::move(*last));
last.reset();
}
public:
StoreDiags(std::string path) : path(std::move(path)) {}
std::vector<Diag> Take() {
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 {
LangOpts = &Opts;
}
@ -96,24 +112,25 @@ public:
SourceLocation L = Info.getLocation();
if (!L.isValid()) return;
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) {
llvm::SmallString<64> Message;
Info.FormatDiagnostic(Message);
d.range =
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
d.message = Message.str();
d.inside_main = inside_main;
d.file = SM.getFilename(Info.getLocation());
d.concerned = concerned;
d.file = Filename;
d.level = Level;
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
};
auto addFix = [&](bool SyntheticMessage) -> bool {
if (!inside_main)
if (!concerned)
return false;
for (const FixItHint &FixIt : Info.getFixItHints()) {
if (!SM.isInMainFile(FixIt.RemoveRange.getBegin()))
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
return false;
lsTextEdit edit;
edit.newText = FixIt.CodeToInsert;
@ -146,9 +163,10 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
CompletionSession &session, std::unique_ptr<CompilerInvocation> CI,
DiagnosticConsumer &DC, const WorkingFiles::Snapshot &snapshot,
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) {
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
if (file.filename == session.file.filename) {
if (file.filename == main)
if (auto Preamble = session.GetPreamble()) {
#if LLVM_VERSION_MAJOR >= 7
Preamble->Preamble.OverridePreamble(*CI, session.FS,
@ -157,14 +175,10 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
Preamble->Preamble.AddImplicitPreamble(*CI, session.FS,
Bufs.back().get());
#endif
} else {
CI->getPreprocessorOpts().addRemappedFile(
CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get());
continue;
}
} else {
CI->getPreprocessorOpts().addRemappedFile(file.filename,
Bufs.back().get());
}
CI->getPreprocessorOpts().addRemappedFile(file.filename,
Bufs.back().get());
}
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
@ -190,52 +204,53 @@ bool Parse(CompilerInstance &Clang) {
void CompletionPreloadMain(CompletionManager *manager) {
while (true) {
// Fetching the completion request blocks until we have a request.
auto request = manager->preload_requests_.Dequeue();
// If we don't get a session then that means we don't care about the file
// anymore - abandon the request.
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
request.path, false /*mark_as_completion*/, false /*create_if_needed*/);
bool is_open = false;
std::shared_ptr<CompletionSession> session =
manager->TryGetSession(request.path, true, &is_open);
if (!session)
continue;
const auto &args = session->file.args;
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
{StripFileType(session->file.filename)});
LOG_S(INFO) << "create completion session for " << session->file.filename;
if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, session->FS))
session->BuildPreamble(*CI);
if (g_config->diagnostics.onSave) {
// For inferred session, don't build preamble because changes in a.h will
// invalidate it.
if (!session->inferred) {
const auto &args = session->file.args;
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
{StripFileType(session->file.filename)});
if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, session->FS))
session->BuildPreamble(*CI, request.path);
}
int debounce =
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
if (debounce >= 0) {
lsTextDocumentIdentifier document;
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) {
// Fetching the completion request blocks until we have a request.
std::unique_ptr<CompletionManager::CompletionRequest> request =
completion_manager->completion_request_.Dequeue();
manager->completion_request_.Dequeue();
// Drop older requests if we're not buffering.
while (g_config->completion.dropOldRequests &&
!completion_manager->completion_request_.IsEmpty()) {
completion_manager->on_dropped_(request->id);
!manager->completion_request_.IsEmpty()) {
manager->on_dropped_(request->id);
request->Consumer.reset();
request->on_complete(nullptr);
request = completion_manager->completion_request_.Dequeue();
request = manager->completion_request_.Dequeue();
}
std::string path = request->document.uri.GetPath();
std::shared_ptr<CompletionSession> session =
completion_manager->TryGetSession(path, true /*mark_as_completion*/,
true /*create_if_needed*/);
manager->TryGetSession(path, false);
std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(session->file.args, session->FS);
@ -251,7 +266,7 @@ void CompletionMain(CompletionManager *completion_manager) {
DiagnosticConsumer DC;
WorkingFiles::Snapshot snapshot =
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
manager->working_files_->AsSnapshot({StripFileType(path)});
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
if (!Clang)
@ -285,25 +300,31 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
}
void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
if (d.inside_main)
if (d.concerned)
OS << llvm::sys::path::filename(d.file);
else
OS << d.file;
auto pos = d.range.start;
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
<< (d.inside_main ? " " : "\n");
<< (d.concerned ? " " : "\n");
OS << diagLeveltoString(d.level) << ": " << d.message;
}
void DiagnosticMain(CompletionManager *manager) {
while (true) {
// Fetching the completion request blocks until we have a request.
CompletionManager::DiagnosticRequest request =
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(
path, true /*mark_as_completion*/, true /*create_if_needed*/);
std::shared_ptr<CompletionSession> session =
manager->TryGetSession(path, false);
std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(session->file.args, session->FS);
@ -311,7 +332,7 @@ void DiagnosticMain(CompletionManager *manager) {
continue;
CI->getDiagnosticOpts().IgnoreWarnings = false;
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
StoreDiags DC;
StoreDiags DC(path);
WorkingFiles::Snapshot snapshot =
manager->working_files_->AsSnapshot({StripFileType(path)});
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
@ -347,9 +368,12 @@ void DiagnosticMain(CompletionManager *manager) {
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;
for (auto &d : DC.Take()) {
if (!d.inside_main)
for (auto &d : diags) {
if (!d.concerned)
continue;
std::string buf;
llvm::raw_string_ostream OS(buf);
@ -364,7 +388,7 @@ void DiagnosticMain(CompletionManager *manager) {
OS.flush();
ls_diag.message = std::move(buf);
for (auto &n : d.notes) {
if (!n.inside_main)
if (!n.concerned)
continue;
lsDiagnostic &ls_diag1 = ls_diags.emplace_back();
Fill(n, ls_diag1);
@ -374,6 +398,12 @@ void DiagnosticMain(CompletionManager *manager) {
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);
}
}
@ -385,9 +415,10 @@ std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
return preamble;
}
void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
void CompletionSession::BuildPreamble(CompilerInvocation &CI,
const std::string &main) {
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 =
llvm::MemoryBuffer::getMemBuffer(content);
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
@ -401,7 +432,7 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
#endif
StoreDiags DC;
StoreDiags DC(main);
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
PreambleCallbacks PP;
@ -421,8 +452,8 @@ CompletionManager::CompletionManager(Project *project,
OnDropped on_dropped)
: project_(project), working_files_(working_files),
on_diagnostic_(on_diagnostic), on_dropped_(on_dropped),
preloaded_sessions_(kMaxPreloadedSessions),
completion_sessions_(kMaxCompletionSessions),
preloads(kMaxPreloadedSessions),
sessions(kMaxCompletionSessions),
PCH(std::make_shared<PCHContainerOperations>()) {
std::thread([&]() {
set_thread_name("comp");
@ -441,22 +472,28 @@ CompletionManager::CompletionManager(Project *project,
.detach();
}
void CompletionManager::CodeComplete(
const lsRequestId &id,
const lsTextDocumentPositionParams &completion_location,
const OnComplete &on_complete) {
}
void CompletionManager::DiagnosticsUpdate(
const lsTextDocumentIdentifier &document) {
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::DiagnosticsUpdate(const std::string &path,
int debounce) {
static GroupMatch match(g_config->diagnostics.whitelist,
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;
}
}
if (flag)
diagnostic_request_.PushBack({path, now + debounce, debounce}, false);
}
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) {
//
// On save, always reparse.
//
EnsureCompletionOrCreatePreloadSession(filename);
preload_requests_.PushBack(PreloadRequest{filename}, true);
}
void CompletionManager::NotifyClose(const std::string &filename) {
//
// On close, we clear any existing CompletionSession instance.
//
void CompletionManager::OnClose(const std::string &filename) {
std::lock_guard<std::mutex> lock(sessions_lock_);
// Take and drop. It's okay if we don't actually drop the file, it'll
// 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);
preloads.TryTake(filename);
sessions.TryTake(filename);
}
bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
const std::string &path) {
std::lock_guard<std::mutex> lock(sessions_lock_);
// Check for an existing CompletionSession.
if (preloaded_sessions_.TryGet(path) ||
completion_sessions_.TryGet(path)) {
if (preloads.TryGet(path) || sessions.TryGet(path))
return false;
}
// No CompletionSession, create new one.
auto session = std::make_shared<ccls::CompletionSession>(
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;
}
std::shared_ptr<ccls::CompletionSession>
CompletionManager::TryGetSession(const std::string &path,
bool mark_as_completion,
bool create_if_needed) {
CompletionManager::TryGetSession(const std::string &path, bool preload,
bool *is_open) {
std::lock_guard<std::mutex> lock(sessions_lock_);
std::shared_ptr<ccls::CompletionSession> session = preloads.TryGet(path);
// Try to find a preloaded session.
std::shared_ptr<ccls::CompletionSession> preloaded =
preloaded_sessions_.TryGet(path);
if (preloaded) {
// If this request is for a completion, we should move it to
// |completion_sessions|.
if (mark_as_completion) {
preloaded_sessions_.TryTake(path);
completion_sessions_.Insert(path, preloaded);
if (session) {
if (!preload) {
preloads.TryTake(path);
sessions.Insert(path, session);
if (is_open)
*is_open = true;
}
return preloaded;
return session;
}
// 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 = sessions.TryGet(path);
if (!session && !preload) {
session = std::make_shared<ccls::CompletionSession>(
project_->FindCompilationEntryForFile(path), working_files_, PCH);
completion_sessions_.Insert(path, session);
sessions.Insert(path, session);
LOG_S(INFO) << "create session for " << path;
if (is_open)
*is_open = true;
}
return session;
}
void CompletionManager::FlushSession(const std::string &path) {
std::lock_guard<std::mutex> lock(sessions_lock_);
preloaded_sessions_.TryTake(path);
completion_sessions_.TryTake(path);
}
void CompletionManager::FlushAllSessions() {
LOG_S(INFO) << "flush all clang complete sessions";
std::lock_guard<std::mutex> lock(sessions_lock_);
preloaded_sessions_.Clear();
completion_sessions_.Clear();
preloads.Clear();
sessions.Clear();
}

View File

@ -27,7 +27,7 @@ struct DiagBase {
std::string file;
clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note;
unsigned category;
bool inside_main = false;
bool concerned = false;
};
struct Note : DiagBase {};
struct Diag : DiagBase {
@ -46,10 +46,10 @@ struct CompletionSession
: public std::enable_shared_from_this<CompletionSession> {
std::mutex mutex;
std::shared_ptr<PreambleData> preamble;
std::vector<Diag> diags;
Project::Entry file;
WorkingFiles *wfiles;
bool inferred = false;
// TODO share
llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> FS =
@ -61,7 +61,7 @@ struct CompletionSession
: file(file), wfiles(wfiles), PCH(PCH) {}
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;
};
struct DiagnosticRequest {
lsTextDocumentIdentifier document;
std::string path;
int64_t wait_until;
int64_t debounce;
};
CompletionManager(Project *project, WorkingFiles *working_files,
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.
void DiagnosticsUpdate(const lsTextDocumentIdentifier &document);
void DiagnosticsUpdate(const std::string &path, int debounce);
// Notify the completion manager that |filename| has been viewed and we
// should begin preloading completion data.
@ -117,7 +114,7 @@ struct CompletionManager {
void NotifySave(const std::string &path);
// Notify the completion manager that |filename| has been closed. Any existing
// 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
// session was created.
@ -125,11 +122,8 @@ struct CompletionManager {
// Tries to find an edit session for |filename|. This will move the session
// from view to edit.
std::shared_ptr<ccls::CompletionSession>
TryGetSession(const std::string &path, bool mark_as_completion,
bool create_if_needed);
TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr);
// Flushes all saved sessions with the supplied filename
void FlushSession(const std::string &path);
// Flushes all saved sessions
void FlushAllSessions(void);
@ -147,14 +141,17 @@ struct CompletionManager {
// CompletionSession instances which are preloaded, ie, files which the user
// has viewed but not requested code completion for.
LruSessionCache preloaded_sessions_;
LruSessionCache preloads;
// CompletionSession instances which the user has actually performed
// completion on. This is more rare so these instances tend to stay alive
// much longer than the ones in |preloaded_sessions_|.
LruSessionCache completion_sessions_;
LruSessionCache sessions;
// Mutex which protects |view_sessions_| and |edit_sessions_|.
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.
ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_;
ThreadedQueue<DiagnosticRequest> diagnostic_request_;

View File

@ -144,20 +144,18 @@ struct Config {
// blacklisted files.
std::vector<std::string> blacklist;
// How often should ccls publish diagnostics in completion?
// -1: never
// 0: as often as possible
// xxx: at most every xxx milliseconds
int frequencyMs = 0;
// Time to wait before computing diagnostics for textDocument/didChange.
// -1: disable diagnostics on change
// 0: immediately
// positive (e.g. 500): wait for 500 milliseconds. didChange requests in
// this period of time will only cause one computation.
int onChange = 1000;
// If true, diagnostics will be reported for textDocument/didChange.
bool onChange = true;
// Time to wait before computing diagnostics for textDocument/didOpen.
int onOpen = 0;
// If true, diagnostics will be reported for textDocument/didOpen.
bool onOpen = true;
// If true, diagnostics will be reported for textDocument/didSave.
bool onSave = true;
// Time to wait before computing diagnostics for textDocument/didSave.
int onSave = 0;
bool spellChecking = true;
@ -246,8 +244,8 @@ MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
dropOldRequests, duplicateOptional, filterAndSort,
includeBlacklist, includeMaxPathSize,
includeSuffixWhitelist, includeWhitelist);
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
onOpen, onSave, spellChecking, whitelist)
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave,
spellChecking, whitelist)
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion,
multiVersionBlacklist, multiVersionWhitelist, onChange,

View File

@ -15,7 +15,6 @@
struct CompletionManager;
struct Config;
class DiagnosticsPublisher;
struct GroupMatch;
struct VFS;
struct IncludeComplete;
@ -78,7 +77,6 @@ struct MessageHandler {
DB *db = nullptr;
MultiQueueWaiter *waiter = nullptr;
Project *project = nullptr;
DiagnosticsPublisher *diag_pub = nullptr;
VFS *vfs = nullptr;
SemanticHighlight *highlight = nullptr;
WorkingFiles *working_files = nullptr;

View File

@ -1,6 +1,7 @@
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
#include "clang_complete.hh"
#include "match.h"
#include "message_handler.h"
#include "pipeline.hh"
@ -38,6 +39,7 @@ struct Handler_CclsReload : BaseMessageHandler<In_CclsReload> {
vfs->Clear();
db->clear();
project->Index(working_files, lsRequestId());
clang_complete->FlushAllSessions();
return;
}

View File

@ -474,7 +474,6 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped);
}
diag_pub->Init();
idx::Init();
highlight->Init();

View File

@ -30,9 +30,8 @@ struct Handler_TextDocumentDidChange
if (g_config->index.onChange)
pipeline::Index(path, {}, IndexMode::OnChange);
clang_complete->NotifyView(path);
if (g_config->diagnostics.onChange)
clang_complete->DiagnosticsUpdate(
params.textDocument.AsTextDocumentIdentifier());
if (g_config->diagnostics.onChange >= 0)
clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange);

View File

@ -35,7 +35,7 @@ struct Handler_TextDocumentDidClose
// Remove internal state.
working_files->OnClose(request->params.textDocument);
clang_complete->NotifyClose(path);
clang_complete->OnClose(path);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose);

View File

@ -59,14 +59,10 @@ struct Handler_TextDocumentDidOpen
project->SetArgsForFile(args, path);
// 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);
clang_complete->FlushSession(path);
}
clang_complete->NotifyView(path);
if (g_config->diagnostics.onOpen)
clang_complete->DiagnosticsUpdate({params.textDocument.uri});
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);

View File

@ -52,12 +52,12 @@ struct Handler_WorkspaceDidChangeWatchedFiles
if (mode == IndexMode::Normal)
clang_complete->NotifySave(path);
else
clang_complete->FlushSession(path);
clang_complete->OnClose(path);
break;
}
case lsFileChangeType::Deleted:
pipeline::Index(path, {}, mode);
clang_complete->FlushSession(path);
clang_complete->OnClose(path);
break;
}
}

View File

@ -27,41 +27,6 @@ using namespace llvm;
#include <unistd.h>
#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() {
std::lock_guard lock(mutex);
state.clear();
@ -485,12 +450,15 @@ void MainLoop() {
SemanticHighlight highlight;
WorkingFiles working_files;
VFS vfs;
DiagnosticsPublisher diag_pub;
CompletionManager clang_complete(
&project, &working_files,
[&](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) {
if (id.Valid()) {
@ -511,7 +479,6 @@ void MainLoop() {
handler->db = &db;
handler->waiter = indexer_waiter;
handler->project = &project;
handler->diag_pub = &diag_pub;
handler->vfs = &vfs;
handler->highlight = &highlight;
handler->working_files = &working_files;

View File

@ -19,18 +19,6 @@ struct Project;
struct WorkingFiles;
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 State {
int64_t timestamp;

View File

@ -54,15 +54,6 @@ enum OptionClass {
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 {
ProjectConfig *config;
std::unordered_set<size_t> command_set;

View File

@ -105,6 +105,15 @@ std::string EscapeFileName(std::string 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) {
sys::fs::file_status Status;
if (sys::fs::status(path, Status))

View File

@ -58,6 +58,9 @@ void EnsureEndsInSlash(std::string &path);
// e.g. foo/bar.c => foo_bar.c
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<std::string> ReadContent(const std::string &filename);
void WriteToFile(const std::string &filename, const std::string &content);