2018-08-21 05:27:52 +00:00
|
|
|
/* Copyright 2017-2018 ccls Authors
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
==============================================================================*/
|
|
|
|
|
2017-12-05 07:57:41 +00:00
|
|
|
#pragma once
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
#include "clang_tu.h"
|
2018-03-20 02:51:42 +00:00
|
|
|
#include "lru_cache.h"
|
2018-02-24 00:12:39 +00:00
|
|
|
#include "lsp_completion.h"
|
|
|
|
#include "lsp_diagnostic.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "project.h"
|
2017-05-26 06:40:38 +00:00
|
|
|
#include "threaded_queue.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "working_files.h"
|
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
#include <clang/Frontend/CompilerInstance.h>
|
|
|
|
#include <clang/Frontend/FrontendActions.h>
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <functional>
|
2017-05-26 06:40:38 +00:00
|
|
|
#include <memory>
|
2017-05-10 04:52:15 +00:00
|
|
|
#include <mutex>
|
2017-05-26 06:40:38 +00:00
|
|
|
#include <string>
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
struct PreambleData {
|
|
|
|
PreambleData(clang::PrecompiledPreamble P) : Preamble(std::move(P)) {}
|
|
|
|
clang::PrecompiledPreamble Preamble;
|
|
|
|
};
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
struct CompletionSession
|
|
|
|
: public std::enable_shared_from_this<CompletionSession> {
|
2018-04-14 16:52:17 +00:00
|
|
|
// Translation unit for clang.
|
|
|
|
struct Tu {
|
|
|
|
// When |tu| was last parsed.
|
|
|
|
std::optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
|
|
|
|
last_parsed_at;
|
|
|
|
// Acquired when |tu| is being used.
|
|
|
|
std::mutex lock;
|
|
|
|
std::unique_ptr<ClangTranslationUnit> tu;
|
|
|
|
};
|
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
struct CU {
|
|
|
|
std::mutex lock;
|
|
|
|
std::shared_ptr<PreambleData> preamble;
|
|
|
|
};
|
|
|
|
|
2017-04-20 05:01:36 +00:00
|
|
|
Project::Entry file;
|
2018-08-28 05:42:40 +00:00
|
|
|
WorkingFiles *wfiles;
|
|
|
|
|
|
|
|
// TODO share
|
|
|
|
llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> FS =
|
|
|
|
clang::vfs::getRealFileSystem();
|
|
|
|
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
CU completion;
|
2018-04-14 16:52:17 +00:00
|
|
|
Tu diagnostics;
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
CompletionSession(const Project::Entry &file, WorkingFiles *wfiles,
|
|
|
|
std::shared_ptr<clang::PCHContainerOperations> PCH)
|
|
|
|
: file(file), wfiles(wfiles), PCH(PCH) {}
|
|
|
|
|
|
|
|
std::shared_ptr<PreambleData> GetPreamble();
|
|
|
|
void BuildPreamble(clang::CompilerInvocation &CI);
|
2017-03-26 21:40:34 +00:00
|
|
|
};
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
struct ClangCompleteManager {
|
2018-08-09 17:08:14 +00:00
|
|
|
using OnDiagnostic = std::function<void(
|
|
|
|
std::string path, std::vector<lsDiagnostic> diagnostics)>;
|
|
|
|
using OnComplete = std::function<void(
|
|
|
|
const std::vector<lsCompletionItem> &results, bool is_cached_result)>;
|
2018-02-22 07:13:42 +00:00
|
|
|
using OnDropped = std::function<void(lsRequestId request_id)>;
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
struct PreloadRequest {
|
2018-08-09 17:08:14 +00:00
|
|
|
PreloadRequest(const std::string &path)
|
2018-04-14 16:52:17 +00:00
|
|
|
: request_time(std::chrono::high_resolution_clock::now()), path(path) {}
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> request_time;
|
|
|
|
std::string path;
|
|
|
|
};
|
2017-04-17 01:22:59 +00:00
|
|
|
struct CompletionRequest {
|
2018-08-09 17:08:14 +00:00
|
|
|
CompletionRequest(const lsRequestId &id,
|
|
|
|
const lsTextDocumentIdentifier &document,
|
|
|
|
const lsPosition &position, const OnComplete &on_complete)
|
|
|
|
: id(id), document(document), position(position),
|
2018-04-14 16:52:17 +00:00
|
|
|
on_complete(on_complete) {}
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2018-02-22 07:13:42 +00:00
|
|
|
lsRequestId id;
|
2017-09-22 02:25:33 +00:00
|
|
|
lsTextDocumentIdentifier document;
|
2018-04-14 16:52:17 +00:00
|
|
|
lsPosition position;
|
|
|
|
OnComplete on_complete;
|
|
|
|
};
|
|
|
|
struct DiagnosticRequest {
|
|
|
|
lsTextDocumentIdentifier document;
|
2017-04-17 01:22:59 +00:00
|
|
|
};
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
ClangCompleteManager(Project *project, WorkingFiles *working_files,
|
|
|
|
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
// Start a code completion at the given location. |on_complete| will run when
|
|
|
|
// completion results are available. |on_complete| may run on any thread.
|
2018-08-09 17:08:14 +00:00
|
|
|
void CodeComplete(const lsRequestId &request_id,
|
|
|
|
const lsTextDocumentPositionParams &completion_location,
|
|
|
|
const OnComplete &on_complete);
|
2017-09-22 02:25:33 +00:00
|
|
|
// Request a diagnostics update.
|
2018-08-09 17:08:14 +00:00
|
|
|
void DiagnosticsUpdate(const lsTextDocumentIdentifier &document);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
// Notify the completion manager that |filename| has been viewed and we
|
|
|
|
// should begin preloading completion data.
|
2018-08-09 17:08:14 +00:00
|
|
|
void NotifyView(const std::string &filename);
|
2017-05-26 06:40:38 +00:00
|
|
|
// Notify the completion manager that |filename| has been edited.
|
2018-08-09 17:08:14 +00:00
|
|
|
void NotifyEdit(const std::string &filename);
|
2017-05-26 06:40:38 +00:00
|
|
|
// Notify the completion manager that |filename| has been saved. This
|
|
|
|
// triggers a reparse.
|
2018-08-09 17:08:14 +00:00
|
|
|
void NotifySave(const std::string &filename);
|
2017-10-17 18:43:33 +00:00
|
|
|
// Notify the completion manager that |filename| has been closed. Any existing
|
|
|
|
// completion session will be dropped.
|
2018-08-09 17:08:14 +00:00
|
|
|
void NotifyClose(const std::string &filename);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Ensures there is a completion or preloaded session. Returns true if a new
|
|
|
|
// session was created.
|
2018-08-09 17:08:14 +00:00
|
|
|
bool EnsureCompletionOrCreatePreloadSession(const std::string &filename);
|
2017-10-23 04:49:17 +00:00
|
|
|
// Tries to find an edit session for |filename|. This will move the session
|
|
|
|
// from view to edit.
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<CompletionSession> TryGetSession(const std::string &filename,
|
2017-10-23 07:28:21 +00:00
|
|
|
bool mark_as_completion,
|
|
|
|
bool create_if_needed);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2018-03-20 12:33:02 +00:00
|
|
|
// Flushes all saved sessions with the supplied filename
|
2018-08-09 17:08:14 +00:00
|
|
|
void FlushSession(const std::string &filename);
|
2018-03-20 12:33:02 +00:00
|
|
|
// Flushes all saved sessions
|
|
|
|
void FlushAllSessions(void);
|
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// TODO: make these configurable.
|
2017-10-23 07:28:21 +00:00
|
|
|
const int kMaxPreloadedSessions = 10;
|
|
|
|
const int kMaxCompletionSessions = 5;
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
// Global state.
|
2018-08-09 17:08:14 +00:00
|
|
|
Project *project_;
|
|
|
|
WorkingFiles *working_files_;
|
2017-06-10 04:13:16 +00:00
|
|
|
OnDiagnostic on_diagnostic_;
|
2018-02-22 07:13:42 +00:00
|
|
|
OnDropped on_dropped_;
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2017-12-04 02:23:14 +00:00
|
|
|
using LruSessionCache = LruCache<std::string, CompletionSession>;
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// 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_;
|
2017-05-26 06:40:38 +00:00
|
|
|
// Mutex which protects |view_sessions_| and |edit_sessions_|.
|
|
|
|
std::mutex sessions_lock_;
|
|
|
|
|
|
|
|
// Request a code completion at the given location.
|
2018-02-18 17:15:39 +00:00
|
|
|
ThreadedQueue<std::unique_ptr<CompletionRequest>> completion_request_;
|
2018-04-14 16:52:17 +00:00
|
|
|
ThreadedQueue<DiagnosticRequest> diagnostic_request_;
|
2017-05-26 06:40:38 +00:00
|
|
|
// Parse requests. The path may already be parsed, in which case it should be
|
|
|
|
// reparsed.
|
2018-04-14 16:52:17 +00:00
|
|
|
ThreadedQueue<PreloadRequest> preload_requests_;
|
2018-08-28 05:42:40 +00:00
|
|
|
|
|
|
|
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
2017-06-29 02:50:30 +00:00
|
|
|
};
|
2018-03-31 08:01:32 +00:00
|
|
|
|
|
|
|
// 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<std::string> cached_path_;
|
|
|
|
std::optional<lsPosition> cached_completion_position_;
|
|
|
|
std::vector<lsCompletionItem> cached_results_;
|
|
|
|
|
|
|
|
std::mutex mutex_;
|
|
|
|
|
|
|
|
void WithLock(std::function<void()> action);
|
|
|
|
bool IsCacheValid(lsTextDocumentPositionParams position);
|
|
|
|
};
|