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-09-23 20:31:06 +00:00
|
|
|
#include "clang_tu.hh"
|
2018-10-28 17:49:31 +00:00
|
|
|
#include "lsp.hh"
|
|
|
|
#include "project.hh"
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "threaded_queue.hh"
|
|
|
|
#include "working_files.hh"
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
#include <clang/Frontend/CompilerInstance.h>
|
|
|
|
#include <clang/Frontend/FrontendActions.h>
|
2018-09-11 05:37:01 +00:00
|
|
|
#include <clang/Sema/CodeCompleteOptions.h>
|
2018-08-28 05:42:40 +00:00
|
|
|
|
2018-11-27 05:00:30 +00:00
|
|
|
#include <algorithm>
|
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>
|
2018-11-27 05:00:30 +00:00
|
|
|
#include <vector>
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
namespace ccls {
|
2018-10-03 00:34:02 +00:00
|
|
|
struct PreambleData;
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
struct DiagBase {
|
|
|
|
Range range;
|
|
|
|
std::string message;
|
|
|
|
std::string file;
|
|
|
|
clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note;
|
|
|
|
unsigned category;
|
2018-09-22 08:37:00 +00:00
|
|
|
bool concerned = false;
|
2018-08-29 05:49:53 +00:00
|
|
|
};
|
|
|
|
struct Note : DiagBase {};
|
|
|
|
struct Diag : DiagBase {
|
|
|
|
std::vector<Note> notes;
|
2018-11-03 20:52:43 +00:00
|
|
|
std::vector<TextEdit> edits;
|
2018-08-29 05:49:53 +00:00
|
|
|
};
|
|
|
|
|
2018-11-03 20:52:43 +00:00
|
|
|
TextEdit ToTextEdit(const clang::SourceManager &SM,
|
2018-10-06 22:23:23 +00:00
|
|
|
const clang::LangOptions &L,
|
|
|
|
const clang::FixItHint &FixIt);
|
|
|
|
|
2018-11-27 05:00:30 +00:00
|
|
|
template <typename K, typename V> struct LruCache {
|
|
|
|
LruCache(int capacity) : capacity(capacity) {}
|
|
|
|
|
|
|
|
std::shared_ptr<V> 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<V> 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<V> value) {
|
|
|
|
if ((int)items.size() >= capacity)
|
|
|
|
items.pop_back();
|
|
|
|
items.emplace(items.begin(), key, std::move(value));
|
|
|
|
}
|
|
|
|
void Clear() { items.clear(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<std::pair<K, std::shared_ptr<V>>> items;
|
|
|
|
int capacity;
|
|
|
|
};
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
struct CompletionSession
|
|
|
|
: public std::enable_shared_from_this<CompletionSession> {
|
2018-08-29 05:49:53 +00:00
|
|
|
std::mutex mutex;
|
|
|
|
std::shared_ptr<PreambleData> preamble;
|
2018-08-28 05:42:40 +00:00
|
|
|
|
2017-04-20 05:01:36 +00:00
|
|
|
Project::Entry file;
|
2018-08-28 05:42:40 +00:00
|
|
|
WorkingFiles *wfiles;
|
2018-09-22 08:37:00 +00:00
|
|
|
bool inferred = false;
|
2018-08-28 05:42:40 +00:00
|
|
|
|
|
|
|
// TODO share
|
2018-10-10 16:52:41 +00:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
|
|
|
llvm::vfs::getRealFileSystem();
|
2018-08-28 05:42:40 +00:00
|
|
|
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
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();
|
2017-03-26 21:40:34 +00:00
|
|
|
};
|
|
|
|
|
2018-09-08 19:07:43 +00:00
|
|
|
struct CompletionManager {
|
2018-08-09 17:08:14 +00:00
|
|
|
using OnDiagnostic = std::function<void(
|
2018-11-03 20:52:43 +00:00
|
|
|
std::string path, std::vector<Diagnostic> diagnostics)>;
|
2018-09-11 05:37:01 +00:00
|
|
|
// If OptConsumer is nullptr, the request has been cancelled.
|
|
|
|
using OnComplete =
|
|
|
|
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
|
2018-11-03 20:52:43 +00:00
|
|
|
using OnDropped = std::function<void(RequestId request_id)>;
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
struct PreloadRequest {
|
2017-05-26 06:40:38 +00:00
|
|
|
std::string path;
|
|
|
|
};
|
2017-04-17 01:22:59 +00:00
|
|
|
struct CompletionRequest {
|
2018-11-03 20:52:43 +00:00
|
|
|
CompletionRequest(const RequestId &id,
|
|
|
|
const TextDocumentIdentifier &document,
|
2018-11-04 18:30:18 +00:00
|
|
|
const Position &position,
|
2018-09-11 05:37:01 +00:00
|
|
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
|
|
|
clang::CodeCompleteOptions CCOpts,
|
|
|
|
const OnComplete &on_complete)
|
2018-08-09 17:08:14 +00:00
|
|
|
: id(id), document(document), position(position),
|
2018-09-11 05:37:01 +00:00
|
|
|
Consumer(std::move(Consumer)), CCOpts(CCOpts),
|
2018-04-14 16:52:17 +00:00
|
|
|
on_complete(on_complete) {}
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2018-11-03 20:52:43 +00:00
|
|
|
RequestId id;
|
|
|
|
TextDocumentIdentifier document;
|
2018-11-04 18:30:18 +00:00
|
|
|
Position position;
|
2018-09-11 05:37:01 +00:00
|
|
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
|
|
|
|
clang::CodeCompleteOptions CCOpts;
|
2018-04-14 16:52:17 +00:00
|
|
|
OnComplete on_complete;
|
|
|
|
};
|
|
|
|
struct DiagnosticRequest {
|
2018-09-22 08:37:00 +00:00
|
|
|
std::string path;
|
|
|
|
int64_t wait_until;
|
|
|
|
int64_t debounce;
|
2017-04-17 01:22:59 +00:00
|
|
|
};
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2018-10-29 04:21:21 +00:00
|
|
|
CompletionManager(Project *project, WorkingFiles *wfiles,
|
2018-09-08 19:07:43 +00:00
|
|
|
OnDiagnostic on_diagnostic, OnDropped on_dropped);
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
// Request a diagnostics update.
|
2018-09-22 08:37:00 +00:00
|
|
|
void DiagnosticsUpdate(const std::string &path, int debounce);
|
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-09-08 19:07:43 +00:00
|
|
|
void NotifyView(const std::string &path);
|
2017-05-26 06:40:38 +00:00
|
|
|
// Notify the completion manager that |filename| has been saved. This
|
|
|
|
// triggers a reparse.
|
2018-09-08 19:07:43 +00:00
|
|
|
void NotifySave(const std::string &path);
|
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-09-22 08:37:00 +00:00
|
|
|
void OnClose(const std::string &path);
|
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-09-08 19:07:43 +00:00
|
|
|
bool EnsureCompletionOrCreatePreloadSession(const std::string &path);
|
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-29 05:49:53 +00:00
|
|
|
std::shared_ptr<ccls::CompletionSession>
|
2018-09-22 08:37:00 +00:00
|
|
|
TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
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_;
|
2018-10-29 04:21:21 +00:00
|
|
|
WorkingFiles *wfiles_;
|
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
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
using LruSessionCache = LruCache<std::string, ccls::CompletionSession>;
|
2017-12-04 02:23:14 +00:00
|
|
|
|
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.
|
2018-09-22 08:37:00 +00:00
|
|
|
LruSessionCache preloads;
|
2017-10-23 07:28:21 +00:00
|
|
|
// 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_|.
|
2018-09-22 08:37:00 +00:00
|
|
|
LruSessionCache sessions;
|
2017-05-26 06:40:38 +00:00
|
|
|
// Mutex which protects |view_sessions_| and |edit_sessions_|.
|
|
|
|
std::mutex sessions_lock_;
|
|
|
|
|
2018-09-22 08:37:00 +00:00
|
|
|
std::mutex diag_mutex;
|
|
|
|
std::unordered_map<std::string, int64_t> next_diag;
|
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// 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.
|
2018-09-11 05:37:01 +00:00
|
|
|
template <typename T>
|
|
|
|
struct CompleteConsumerCache {
|
2018-03-31 08:01:32 +00:00
|
|
|
// NOTE: Make sure to access these variables under |WithLock|.
|
2018-09-11 05:37:01 +00:00
|
|
|
std::optional<std::string> path;
|
2018-11-04 18:30:18 +00:00
|
|
|
std::optional<Position> position;
|
2018-09-11 05:37:01 +00:00
|
|
|
T result;
|
2018-03-31 08:01:32 +00:00
|
|
|
|
2018-09-11 05:37:01 +00:00
|
|
|
std::mutex mutex;
|
2018-03-31 08:01:32 +00:00
|
|
|
|
2018-09-11 05:37:01 +00:00
|
|
|
void WithLock(std::function<void()> action) {
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
action();
|
|
|
|
}
|
2018-11-04 18:30:18 +00:00
|
|
|
bool IsCacheValid(const std::string path, Position position) {
|
2018-09-11 05:37:01 +00:00
|
|
|
std::lock_guard lock(mutex);
|
2018-10-06 22:23:23 +00:00
|
|
|
return this->path == path && this->position == position;
|
2018-09-11 05:37:01 +00:00
|
|
|
}
|
2018-03-31 08:01:32 +00:00
|
|
|
};
|
2018-10-28 17:49:31 +00:00
|
|
|
} // namespace ccls
|