#pragma once #include "clang_cursor.h" #include "clang_translation_unit.h" #include "lru_cache.h" #include "lsp_completion.h" #include "lsp_diagnostic.h" #include "project.h" #include "threaded_queue.h" #include "working_files.h" #include #include #include #include #include struct CompletionSession : public std::enable_shared_from_this { Project::Entry file; WorkingFiles* working_files; ClangIndex index; // When |tu| was last parsed. std::optional> tu_last_parsed_at; // Acquired when |tu| is being used. std::mutex tu_lock; // The active translation unit. std::unique_ptr tu; CompletionSession(const Project::Entry& file, WorkingFiles* working_files); ~CompletionSession(); }; struct ClangCompleteManager { using OnDiagnostic = std::function diagnostics)>; using OnIndex = std::function& unsaved, const std::string& path, const std::vector& args)>; using OnComplete = std::function& results, bool is_cached_result)>; using OnDropped = std::function; struct ParseRequest { ParseRequest(const std::string& path); std::chrono::time_point request_time; std::string path; }; struct CompletionRequest { CompletionRequest(const lsRequestId& id, const lsTextDocumentIdentifier& document, bool emit_diagnostics); CompletionRequest(const lsRequestId& id, const lsTextDocumentIdentifier& document, const lsPosition& position, const OnComplete& on_complete, bool emit_diagnostics); lsRequestId id; lsTextDocumentIdentifier document; std::optional position; OnComplete on_complete; // May be null/empty. bool emit_diagnostics = false; }; ClangCompleteManager(Project* project, WorkingFiles* working_files, OnDiagnostic on_diagnostic, OnIndex on_index, 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 lsRequestId& request_id, const lsTextDocumentIdentifier& document); // Notify the completion manager that |filename| has been viewed and we // should begin preloading completion data. void NotifyView(const std::string& filename); // Notify the completion manager that |filename| has been edited. void NotifyEdit(const std::string& filename); // Notify the completion manager that |filename| has been saved. This // triggers a reparse. void NotifySave(const std::string& filename); // Notify the completion manager that |filename| has been closed. Any existing // completion session will be dropped. void NotifyClose(const std::string& filename); // Ensures there is a completion or preloaded session. Returns true if a new // session was created. bool EnsureCompletionOrCreatePreloadSession(const std::string& filename); // Tries to find an edit session for |filename|. This will move the session // from view to edit. std::shared_ptr TryGetSession(const std::string& filename, bool mark_as_completion, bool create_if_needed); // Flushes all saved sessions with the supplied filename void FlushSession(const std::string& filename); // Flushes all saved sessions void FlushAllSessions(void); // TODO: make these configurable. const int kMaxPreloadedSessions = 10; const int kMaxCompletionSessions = 5; // Global state. Project* project_; WorkingFiles* working_files_; OnDiagnostic on_diagnostic_; OnIndex on_index_; OnDropped on_dropped_; using LruSessionCache = LruCache; // CompletionSession instances which are preloaded, ie, files which the user // has viewed but not requested code completion for. LruSessionCache preloaded_sessions_; // 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_; // Mutex which protects |view_sessions_| and |edit_sessions_|. std::mutex sessions_lock_; // Request a code completion at the given location. ThreadedQueue> completion_request_; // Parse requests. The path may already be parsed, in which case it should be // reparsed. ThreadedQueue parse_requests_; }; // 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. struct CodeCompleteCache { // NOTE: Make sure to access these variables under |WithLock|. std::optional cached_path_; std::optional cached_completion_position_; std::vector cached_results_; std::mutex mutex_; void WithLock(std::function action); bool IsCacheValid(lsTextDocumentPositionParams position); };