Publish diagnostics (#164)
This commit is contained in:
215
src/Server/Document.cpp
Normal file
215
src/Server/Document.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "Support/Logger.h"
|
||||
#include "Server/Server.h"
|
||||
#include "Compiler/Compilation.h"
|
||||
#include "Feature/Diagnostic.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
async::Task<OpenFile*> Server::add_document(std::string path, std::string content) {
|
||||
auto& openFile = opening_files[path];
|
||||
openFile.content = content;
|
||||
|
||||
auto& task = openFile.ast_build_task;
|
||||
|
||||
/// If there is already an AST build task, cancel it.
|
||||
if(!task.empty()) {
|
||||
task.cancel();
|
||||
task.dispose();
|
||||
}
|
||||
|
||||
/// Create and schedule a new task.
|
||||
task = build_ast(std::move(path), std::move(content));
|
||||
co_await task;
|
||||
|
||||
co_return &opening_files[path];
|
||||
}
|
||||
|
||||
async::Task<> Server::build_pch(std::string path, std::string content) {
|
||||
auto bound = computePreambleBound(content);
|
||||
|
||||
auto openFile = &opening_files[path];
|
||||
bool outdated = true;
|
||||
if(openFile->pch) {
|
||||
/// FIXME:
|
||||
/// outdated = co_await isPCHOutdated(path, llvm::StringRef(content).substr(0, bound));
|
||||
}
|
||||
|
||||
/// If not need update, return directly.
|
||||
if(!outdated) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
/// The actual PCH build task.
|
||||
constexpr static auto PCHBuildTask =
|
||||
[](Server& server,
|
||||
std::string path,
|
||||
std::uint32_t bound,
|
||||
std::string content,
|
||||
std::shared_ptr<std::vector<Diagnostic>> diagnostics) -> async::Task<bool> {
|
||||
if(!fs::exists(config::cache.dir)) {
|
||||
auto error = fs::create_directories(config::cache.dir);
|
||||
if(error) {
|
||||
log::warn("Fail to create directory for PCH building: {}", config::cache.dir);
|
||||
co_return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Everytime we build a new pch, the old diagnostics should be discarded.
|
||||
diagnostics->clear();
|
||||
|
||||
CompilationParams params;
|
||||
params.outPath = path::join(config::cache.dir, path::filename(path) + ".pch");
|
||||
params.arguments = server.database.get_command(path, true, true).arguments;
|
||||
params.diagnostics = diagnostics;
|
||||
params.add_remapped_file(path, content, bound);
|
||||
|
||||
PCHInfo info;
|
||||
|
||||
std::string command;
|
||||
for(auto argument: params.arguments) {
|
||||
command += " ";
|
||||
command += argument;
|
||||
}
|
||||
|
||||
log::info("Start building PCH for {}, command: [{}]", path, command);
|
||||
|
||||
/// PCH file is written until destructing, Add a single block
|
||||
/// for it.
|
||||
bool cond = co_await async::submit([&] {
|
||||
auto result = compile(params, info);
|
||||
if(!result) {
|
||||
log::warn("Building PCH fails for {}, Because: {}", path, result.error());
|
||||
|
||||
for(auto& diagnostic: *diagnostics) {
|
||||
log::warn("{}", diagnostic.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// TODO: index PCH.
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if(!cond) {
|
||||
co_return false;
|
||||
}
|
||||
|
||||
auto& openFile = server.opening_files[path];
|
||||
/// Update the built PCH info.
|
||||
openFile.pch = std::move(info);
|
||||
/// Resume waiters on this event.
|
||||
openFile.pch_built_event.set();
|
||||
openFile.pch_built_event.clear();
|
||||
|
||||
co_return true;
|
||||
};
|
||||
|
||||
openFile = &opening_files[path];
|
||||
|
||||
/// If there is already an PCH build task, cancel it.
|
||||
auto& task = openFile->pch_build_task;
|
||||
if(!task.empty()) {
|
||||
task.cancel();
|
||||
task.dispose();
|
||||
}
|
||||
|
||||
/// Schedule the new building task.
|
||||
task = PCHBuildTask(*this, path, bound, std::move(content), openFile->diagnostics);
|
||||
|
||||
if(co_await task) {
|
||||
log::info("Building PCH successfully for {}", path);
|
||||
|
||||
/// Dispose the task so that it will destroyed when task complete.
|
||||
task.dispose();
|
||||
}
|
||||
|
||||
/// TODO: report diagnostics in the preamble.
|
||||
}
|
||||
|
||||
async::Task<> Server::build_ast(std::string path, std::string content) {
|
||||
auto file = &opening_files[path];
|
||||
|
||||
/// Try get the lock, the waiter on the lock will be resumed when
|
||||
/// guard is destroyed.
|
||||
auto guard = co_await file->ast_built_lock.try_lock();
|
||||
|
||||
/// PCH is already updated.
|
||||
co_await build_pch(path, content);
|
||||
|
||||
auto pch = opening_files[path].pch;
|
||||
if(!pch) {
|
||||
log::fatal("Expected PCH built at this point");
|
||||
}
|
||||
|
||||
file = &opening_files[path];
|
||||
CompilationParams params;
|
||||
params.arguments = database.get_command(path, true, true).arguments;
|
||||
params.add_remapped_file(path, content);
|
||||
params.pch = {pch->path, pch->preamble.size()};
|
||||
file->diagnostics->clear();
|
||||
params.diagnostics = file->diagnostics;
|
||||
|
||||
/// Check result
|
||||
auto ast = co_await async::submit([&] { return compile(params); });
|
||||
if(!ast) {
|
||||
/// FIXME: Fails needs cancel waiting tasks.
|
||||
log::warn("Building AST fails for {}, Beacuse: {}", path, ast.error());
|
||||
co_return;
|
||||
}
|
||||
|
||||
/// FIXME: Index the source file.
|
||||
/// co_await indexer.index(*ast);
|
||||
|
||||
file = &opening_files[path];
|
||||
/// Update built AST info.
|
||||
file->ast = std::make_shared<CompilationUnit>(std::move(*ast));
|
||||
/// Dispose the task so that it will destroyed when task complete.
|
||||
file->ast_build_task.dispose();
|
||||
|
||||
log::info("Building AST successfully for {}", path);
|
||||
}
|
||||
|
||||
async::Task<> Server::on_did_open(proto::DidOpenTextDocumentParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
auto file = co_await add_document(path, std::move(params.textDocument.text));
|
||||
if(file->diagnostics) {
|
||||
auto guard = co_await file->ast_built_lock.try_lock();
|
||||
file = &opening_files[path];
|
||||
auto diagnostics = feature::diagnostics(kind, mapping, *file->ast);
|
||||
co_await notify("textDocument/publishDiagnostics",
|
||||
json::Object{
|
||||
{"uri", mapping.to_uri(path) },
|
||||
{"diagnostics", std::move(diagnostics)},
|
||||
});
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::Task<> Server::on_did_change(proto::DidChangeTextDocumentParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
auto file = co_await add_document(path, std::move(params.contentChanges[0].text));
|
||||
if(file->diagnostics) {
|
||||
auto guard = co_await file->ast_built_lock.try_lock();
|
||||
file = &opening_files[path];
|
||||
auto diagnostics = feature::diagnostics(kind, mapping, *file->ast);
|
||||
co_await notify("textDocument/publishDiagnostics",
|
||||
json::Object{
|
||||
{"uri", mapping.to_uri(path) },
|
||||
{"diagnostics", std::move(diagnostics)},
|
||||
});
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::Task<> Server::on_did_save(proto::DidSaveTextDocumentParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::Task<> Server::on_did_close(proto::DidCloseTextDocumentParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
co_return;
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
Reference in New Issue
Block a user