// Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 #pragma once #include "clang_tu.hh" #include "lsp.hh" #include "project.hh" #include "threaded_queue.hh" #include "working_files.hh" #include #include #include #include #include #include #include #include #include namespace ccls { struct PreambleData; struct DiagBase { Range range; std::string message; std::string file; clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note; unsigned category; bool concerned = false; }; struct Note : DiagBase {}; struct Diag : DiagBase { std::vector notes; std::vector edits; }; TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L, const clang::FixItHint &FixIt); template struct LruCache { std::shared_ptr Get(const K &key) { for (auto it = items.begin(); it != items.end(); ++it) if (it->first == key) { auto x = std::move(*it); std::move_backward(items.begin(), it, it + 1); items[0] = std::move(x); return items[0].second; } return nullptr; } std::shared_ptr Take(const K &key) { for (auto it = items.begin(); it != items.end(); ++it) if (it->first == key) { auto x = std::move(it->second); items.erase(it); return x; } return nullptr; } void Insert(const K &key, std::shared_ptr value) { if ((int)items.size() >= capacity) items.pop_back(); items.emplace(items.begin(), key, std::move(value)); } void Clear() { items.clear(); } void SetCapacity(int cap) { capacity = cap; } private: std::vector>> items; int capacity = 1; }; struct Session { std::mutex mutex; std::shared_ptr preamble; Project::Entry file; WorkingFiles *wfiles; bool inferred = false; // TODO share llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem(); std::shared_ptr PCH; Session(const Project::Entry &file, WorkingFiles *wfiles, std::shared_ptr PCH) : file(file), wfiles(wfiles), PCH(PCH) {} std::shared_ptr GetPreamble(); }; struct SemaManager { using OnDiagnostic = std::function diagnostics)>; // If OptConsumer is nullptr, the request has been cancelled. using OnComplete = std::function; using OnDropped = std::function; struct PreambleTask { std::string path; bool from_diag = false; }; struct CompTask { CompTask(const RequestId &id, const std::string &path, const Position &position, std::unique_ptr Consumer, clang::CodeCompleteOptions CCOpts, const OnComplete &on_complete) : id(id), path(path), position(position), Consumer(std::move(Consumer)), CCOpts(CCOpts), on_complete(on_complete) {} RequestId id; std::string path; Position position; std::unique_ptr Consumer; clang::CodeCompleteOptions CCOpts; OnComplete on_complete; }; struct DiagTask { std::string path; int64_t wait_until; int64_t debounce; }; SemaManager(Project *project, WorkingFiles *wfiles, OnDiagnostic on_diagnostic, OnDropped on_dropped); void ScheduleDiag(const std::string &path, int debounce); void OnView(const std::string &path); void OnSave(const std::string &path); void OnClose(const std::string &path); std::shared_ptr EnsureSession(const std::string &path, bool *created = nullptr); void Clear(void); // Global state. Project *project_; WorkingFiles *wfiles; OnDiagnostic on_diagnostic_; OnDropped on_dropped_; std::mutex mutex; LruCache sessions; std::mutex diag_mutex; std::unordered_map next_diag; ThreadedQueue> comp_tasks; ThreadedQueue diag_tasks; ThreadedQueue preamble_tasks; std::shared_ptr PCH; }; // Cached completion information, so we can give fast completion results when // the user erases a character. vscode will resend the completion request if // that happens. template struct CompleteConsumerCache { std::mutex mutex; std::string path; Position position; T result; template void WithLock(Fn &&fn) { std::lock_guard lock(mutex); fn(); } bool IsCacheValid(const std::string path, Position position) { std::lock_guard lock(mutex); return this->path == path && this->position == position; } }; } // namespace ccls