some update.
This commit is contained in:
@@ -22,7 +22,8 @@ TODO:
|
||||
FIXME:
|
||||
- 为每个 Token 都提供语义高亮: https://github.com/clangd/clangd/issues/1115
|
||||
- no-self-contained: https://github.com/clangd/clangd/issues/45
|
||||
|
||||
- 提供更好的模板代码补全(需要在索引文件里面记录模板实例化),https://github.com/clangd/clangd/issues/443,然后补全的时候获取`.`号前面的表达式类型,之后再这里查找
|
||||
- 支持模块:https://github.com/clangd/clangd/issues/1293
|
||||
## 性能优化
|
||||
|
||||
TODO: 寻找核心优化点
|
||||
@@ -43,6 +44,7 @@ TODO:
|
||||
- [支持离线索引](https://github.com/clangd/clangd/issues/587)
|
||||
- 另外请见 https://discourse.llvm.org/t/using-background-and-static-indexes-simultaneously-for-large-codebases/3706/7
|
||||
|
||||
|
||||
## 详细的支持功能列表
|
||||
|
||||
需要具体到哪些功能要做
|
||||
|
||||
88
clice.md
88
clice.md
@@ -27,52 +27,52 @@
|
||||
|
||||
### Language Features
|
||||
|
||||
首先对 LSP 支持的功能进行概览:LSP 3.17 currently:
|
||||
首先对 LSP 支持的功能进行概览: LSP 3.17 currently:
|
||||
|
||||
- Goto Declaration:跳转到声明
|
||||
- Goto Definition:跳转到定义
|
||||
- Goto Type Definition:跳转到类型定义
|
||||
- Goto Implementation:跳转到实现
|
||||
- Find References:查找所有引用
|
||||
- Prepare Call Hierarchy:没搞懂
|
||||
- Call Hierarchy Incoming Calls:没搞懂
|
||||
- Call Hierarchy Outgoing Calls:没搞懂
|
||||
- Prepare Type Hierarchy:没搞懂
|
||||
- Type Hierarchy Supertypes:没搞懂
|
||||
- Type Hierarchy Subtypes:没搞懂
|
||||
- Document Highlights:没搞懂
|
||||
- Document Link:没搞懂
|
||||
- Document Link Resolve:没搞懂
|
||||
- Hover:悬停提示
|
||||
- Code Lens:没搞懂
|
||||
- Code Lens Refresh Request:没搞懂
|
||||
- Folding Range:把某段代码折叠起来
|
||||
- Selection Range:没搞懂
|
||||
- Document Symbols:没搞懂
|
||||
- Semantic Tokens:用于语义高亮
|
||||
- Inline Value:没搞懂
|
||||
- Inline Value Refresh:没搞懂
|
||||
- Inlay Hint:用于内嵌提示,比如函数参数或者`auto`的类型
|
||||
- Inlay Hint Resolve:没搞懂
|
||||
- Inlay Hint Refresh:刷新内嵌提示
|
||||
- Monikers:没搞懂
|
||||
- Completion:代码补全
|
||||
- Completion Item Resolve:解决重载函数的代码补全
|
||||
- PublishDiagnostics Notification:发出诊断信息
|
||||
- Pull Diagnostics:没搞懂
|
||||
- Signature Help Request:请求函数签名信息
|
||||
- Code Action:重构等操作(还有那个 quick fix)
|
||||
- Code Action Resolve:没搞懂
|
||||
- Document Color:没搞懂
|
||||
- Color Presentation:没搞懂
|
||||
- Document Formatting:格式化
|
||||
- Document Range Formatting:只格式化某个部分
|
||||
- Document on Type Formatting:没搞懂
|
||||
- Rename:重命名
|
||||
- Prepare Rename:解决重命名
|
||||
- Linked Editing Range:没搞懂
|
||||
- Goto Declaration: 跳转到声明
|
||||
- Goto Definition: 跳转到定义
|
||||
- Goto Type Definition: 跳转到类型定义
|
||||
- Goto Implementation: 跳转到实现
|
||||
- Find References: 查找所有引用
|
||||
- Prepare Call Hierarchy: 没搞懂
|
||||
- Call Hierarchy Incoming Calls: 没搞懂
|
||||
- Call Hierarchy Outgoing Calls: 没搞懂
|
||||
- Prepare Type Hierarchy: 没搞懂
|
||||
- Type Hierarchy Supertypes: 没搞懂
|
||||
- Type Hierarchy Subtypes: 没搞懂
|
||||
- Document Highlights: 没搞懂
|
||||
- Document Link: 用来处理文件链接跳转,例如点击一个文件名跳转到文件
|
||||
- Document Link Resolve: 没搞懂
|
||||
- Hover: 悬停提示
|
||||
- Code Lens: 没搞懂
|
||||
- Code Lens Refresh Request: 没搞懂
|
||||
- Folding Range: 把某段代码折叠起来
|
||||
- Selection Range: 没搞懂
|
||||
- Document Symbols: 列出文档中的所有符号,可以用于 vscode 左边那个 Outline
|
||||
- Semantic Tokens: 用于语义高亮
|
||||
- Inline Value: 没搞懂
|
||||
- Inline Value Refresh: 没搞懂
|
||||
- Inlay Hint: 用于内嵌提示,比如函数参数或者`auto`的类型
|
||||
- Inlay Hint Resolve: 没搞懂
|
||||
- Inlay Hint Refresh: 刷新内嵌提示
|
||||
- Monikers: 没搞懂
|
||||
- Completion: 代码补全
|
||||
- Completion Item Resolve: 解决重载函数的代码补全
|
||||
- PublishDiagnostics Notification: 发出诊断信息
|
||||
- Pull Diagnostics: 没搞懂
|
||||
- Signature Help Request: 请求函数签名信息
|
||||
- Code Action: 重构等操作(还有那个 quick fix)
|
||||
- Code Action Resolve: 没搞懂
|
||||
- Document Color: 没搞懂
|
||||
- Color Presentation: 没搞懂
|
||||
- Document Formatting: 格式化
|
||||
- Document Range Formatting: 只格式化某个部分
|
||||
- Document on Type Formatting: 没搞懂
|
||||
- Rename: 重命名
|
||||
- Prepare Rename: 解决重命名
|
||||
- Linked Editing Range: 没搞懂
|
||||
|
||||
这些任务从最终实现的角度来说可以主要分成三种:
|
||||
这些任务从最终实现的角度来说可以主要分成三种:
|
||||
1. CodeCompletion 这个需要利用 CodeCompletionConsumer 调用 Clang 提供的接口来实现,然而我们实际上可以做一些更加复杂的分析(clangd 目前没有做)。比如判断当前的是不是在 Template 语境下从而决定补全`sizeof`的时候要不要补全`...`。在补全成员的时候,似乎我们也可以获取`expr.f`中的父对象的类型,从而根据它的类型来做一些补全。有待进一步研究。
|
||||
2. Semantic Tokens 等基于当前 AST 的操作,则是遍历 AST 渲染 Token 即可。
|
||||
3. 剩下很多的,例如 Find References 等等等查询功能,都是在已经索引好的文件中进行查询,不需要对语法树进行什么改动。
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include <clang/Basic/Diagnostic.h>
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
@@ -5,4 +7,25 @@
|
||||
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
||||
#include <clang/Sema/Sema.h>
|
||||
#include <clang/Tooling/CompilationDatabase.h>
|
||||
#include <clang/Tooling/Syntax/Tokens.h>
|
||||
#include <clang/Tooling/Syntax/Tokens.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
using clang::CompilerInstance;
|
||||
using clang::CompilerInvocation;
|
||||
|
||||
template <typename T>
|
||||
union uninitialized {
|
||||
T value;
|
||||
|
||||
uninitialized() {}
|
||||
|
||||
~uninitialized() { value.~T(); }
|
||||
|
||||
template <typename... Args>
|
||||
auto& construct(Args&&... args) {
|
||||
return *new (&value) T{std::forward<Args>(args)...};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
|
||||
35
include/Clang/CompileDatabase.h
Normal file
35
include/Clang/CompileDatabase.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <Clang/Clang.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
class CompileDatabase {
|
||||
private:
|
||||
std::unique_ptr<clang::tooling::CompilationDatabase> database;
|
||||
|
||||
public:
|
||||
static auto& instance() {
|
||||
static CompileDatabase instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void load(std::string_view path) {
|
||||
std::string error;
|
||||
database = clang::tooling::CompilationDatabase::loadFromDirectory(path, error);
|
||||
if(!database) {
|
||||
llvm::errs() << "Failed to load compilation database. " << error << "\n";
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
auto lookup(std::string_view path) {
|
||||
auto commands = database->getCompileCommands(path);
|
||||
llvm::ArrayRef command = commands[0].CommandLine;
|
||||
std::vector<const char*> args = {command.front().c_str(), "-Xclang", "-no-round-trip-args"};
|
||||
for(auto& arg: command.drop_front()) {
|
||||
args.push_back(arg.c_str());
|
||||
}
|
||||
return args;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
inline auto createCompilerInvocation() {}
|
||||
|
||||
} // namespace clice
|
||||
|
||||
9
include/Clang/Diagnostics.h
Normal file
9
include/Clang/Diagnostics.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
class Diagnostic {};
|
||||
|
||||
class DiagnosticConsumer : clang::DiagnosticConsumer {};
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,7 +1,49 @@
|
||||
#include "Diagnostics.h"
|
||||
#include "Preamble.h"
|
||||
#include "CompileDatabase.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
class ParsedAST {
|
||||
private:
|
||||
using Decl = clang::Decl*;
|
||||
using PathRef = llvm::StringRef;
|
||||
using TokenBuffer = clang::syntax::TokenBuffer;
|
||||
using ASTConsumer = std::unique_ptr<clang::ASTConsumer>;
|
||||
|
||||
struct FrontendAction : public clang::ASTFrontendAction {
|
||||
ASTConsumer CreateASTConsumer(CompilerInstance& instance, PathRef file) override;
|
||||
};
|
||||
|
||||
private:
|
||||
/// path of translation unit
|
||||
PathRef path;
|
||||
/// llvm version
|
||||
std::string version;
|
||||
/// headers part of the tu, when a file is loaded, we will build the preamble, the reuse it.
|
||||
Preamble preamble;
|
||||
/// some extra info
|
||||
std::vector<Decl> topLevelDecls;
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
/// core members for clang frontend
|
||||
uninitialized<TokenBuffer> tokens;
|
||||
clang::SyntaxOnlyAction action;
|
||||
CompilerInstance instance;
|
||||
|
||||
ParsedAST() = default;
|
||||
|
||||
public:
|
||||
std::unique_ptr<ParsedAST> build(std::string_view path,
|
||||
const std::shared_ptr<CompilerInvocation>& invocation,
|
||||
const std::shared_ptr<Preamble>& preamble);
|
||||
|
||||
auto& Tokens() { return tokens.value; }
|
||||
|
||||
auto& Diagnostics() { return diagnostics; }
|
||||
|
||||
auto& ASTContext() { return instance.getASTContext(); }
|
||||
|
||||
auto& TranslationUnit() { return *instance.getASTContext().getTranslationUnitDecl(); }
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
namespace {
|
||||
#include "Clang.h"
|
||||
|
||||
class Preamble {};
|
||||
namespace clice{
|
||||
|
||||
class Preamble {
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
10
include/LSP/Scheduler.h
Normal file
10
include/LSP/Scheduler.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <Clang/ParsedAST.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
class Scheduler {
|
||||
std::mutex mutex;
|
||||
llvm::StringMap<std::unique_ptr<ParsedAST>> parsedASTs;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,15 +1,22 @@
|
||||
#include <uv.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace clice {
|
||||
|
||||
// global server instance
|
||||
extern class Server server;
|
||||
|
||||
/// core class responsible for starting the server
|
||||
class Server {
|
||||
static uv_loop_t* loop;
|
||||
static uv_pipe_t stdin_pipe;
|
||||
uv_loop_t* loop;
|
||||
uv_pipe_t stdin_pipe;
|
||||
|
||||
public:
|
||||
static int Initialize();
|
||||
static int Exit();
|
||||
int initialize();
|
||||
int exit();
|
||||
|
||||
public:
|
||||
void handle_message(std::string_view message);
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
|
||||
53
src/Clang/ParsedAST.cpp
Normal file
53
src/Clang/ParsedAST.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <Clang/ParsedAST.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
std::unique_ptr<ParsedAST> ParsedAST::build(std::string_view path,
|
||||
const std::shared_ptr<CompilerInvocation>& invocation,
|
||||
const std::shared_ptr<Preamble>& preamble) {
|
||||
auto AST = new ParsedAST();
|
||||
AST->path = path;
|
||||
|
||||
// some settings for CompilerInstance
|
||||
auto& instance = AST->instance;
|
||||
instance.setInvocation(invocation);
|
||||
instance.createDiagnostics();
|
||||
|
||||
if(!instance.createTarget()) {
|
||||
llvm::errs() << "Failed to create target\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
if(auto manager = instance.createFileManager()) {
|
||||
instance.createSourceManager(*manager);
|
||||
} else {
|
||||
llvm::errs() << "Failed to create file manager\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
instance.createPreprocessor(clang::TranslationUnitKind::TU_Complete);
|
||||
|
||||
instance.createASTContext();
|
||||
|
||||
// start FrontendAction
|
||||
const auto& input = instance.getFrontendOpts().Inputs[0];
|
||||
auto& action = AST->action;
|
||||
|
||||
if(!action.BeginSourceFile(instance, input)) {
|
||||
llvm::errs() << "Failed to begin source file\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
clang::syntax::TokenCollector collector = {instance.getPreprocessor()};
|
||||
|
||||
if(llvm::Error error = action.Execute()) {
|
||||
llvm::errs() << "Failed to execute action: " << error << "\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
AST->tokens.construct(std::move(collector).consume());
|
||||
|
||||
return std::unique_ptr<ParsedAST>(AST);
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,176 +1,170 @@
|
||||
#include <Clang/Clang.h>
|
||||
|
||||
namespace tooling = clang::tooling;
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<tooling::CompilationDatabase> datebase;
|
||||
|
||||
auto GetCommands(std::string_view path, std::string_view compile_commands_path)
|
||||
-> std::vector<tooling::CompileCommand> {
|
||||
if (!datebase) {
|
||||
std::string error;
|
||||
datebase = tooling::CompilationDatabase::loadFromDirectory(
|
||||
compile_commands_path, error);
|
||||
auto GetCommands(std::string_view path,
|
||||
std::string_view compile_commands_path) -> std::vector<tooling::CompileCommand> {
|
||||
if(!datebase) {
|
||||
std::string error;
|
||||
datebase = tooling::CompilationDatabase::loadFromDirectory(compile_commands_path, error);
|
||||
|
||||
if (!datebase) {
|
||||
llvm::errs() << "Failed to load compilation database. " << error << "\n";
|
||||
std::terminate();
|
||||
if(!datebase) {
|
||||
llvm::errs() << "Failed to load compilation database. " << error << "\n";
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return datebase->getCompileCommands(path);
|
||||
return datebase->getCompileCommands(path);
|
||||
}
|
||||
|
||||
auto createDiagnostic() {
|
||||
clang::DiagnosticOptions DiagOpts;
|
||||
clang::DiagnosticOptions DiagOpts;
|
||||
|
||||
clang::TextDiagnosticPrinter *DiagClient =
|
||||
new clang::TextDiagnosticPrinter(llvm::errs(), &DiagOpts);
|
||||
clang::TextDiagnosticPrinter* DiagClient =
|
||||
new clang::TextDiagnosticPrinter(llvm::errs(), &DiagOpts);
|
||||
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID =
|
||||
new clang::DiagnosticIDs();
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID = new clang::DiagnosticIDs();
|
||||
|
||||
return clang::DiagnosticsEngine(DiagID, &DiagOpts, DiagClient);
|
||||
return clang::DiagnosticsEngine(DiagID, &DiagOpts, DiagClient);
|
||||
}
|
||||
|
||||
auto createInvocation(std::string_view path,
|
||||
std::string_view compile_commands) {
|
||||
auto commands = GetCommands(path, compile_commands);
|
||||
llvm::ArrayRef command = commands[0].CommandLine;
|
||||
auto createInvocation(std::string_view path, std::string_view compile_commands) {
|
||||
auto commands = GetCommands(path, compile_commands);
|
||||
llvm::ArrayRef command = commands[0].CommandLine;
|
||||
|
||||
std::vector<const char *> args = {command.front().c_str(), "-Xclang",
|
||||
"-no-round-trip-args"};
|
||||
std::vector<const char*> args = {command.front().c_str(), "-Xclang", "-no-round-trip-args"};
|
||||
|
||||
for (auto &arg : command.drop_front()) {
|
||||
args.push_back(arg.c_str());
|
||||
}
|
||||
for(auto& arg: command.drop_front()) {
|
||||
args.push_back(arg.c_str());
|
||||
}
|
||||
|
||||
static auto engine = createDiagnostic();
|
||||
auto invocation = clang::createInvocation(args);
|
||||
static auto engine = createDiagnostic();
|
||||
auto invocation = clang::createInvocation(args);
|
||||
|
||||
// set input file
|
||||
auto &inputs = invocation->getFrontendOpts().Inputs;
|
||||
inputs.push_back(
|
||||
clang::FrontendInputFile(path, clang::InputKind{clang::Language::CXX}));
|
||||
// set input file
|
||||
auto& inputs = invocation->getFrontendOpts().Inputs;
|
||||
inputs.push_back(clang::FrontendInputFile(path, clang::InputKind{clang::Language::CXX}));
|
||||
|
||||
return invocation;
|
||||
return invocation;
|
||||
}
|
||||
|
||||
struct DiagnosticConsumer : clang::DiagnosticConsumer {
|
||||
void BeginSourceFile(const clang::LangOptions &LangOpts,
|
||||
const clang::Preprocessor *PP) override {}
|
||||
void BeginSourceFile(const clang::LangOptions& LangOpts,
|
||||
const clang::Preprocessor* PP) override {}
|
||||
|
||||
void EndSourceFile() override {}
|
||||
void EndSourceFile() override {}
|
||||
|
||||
void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
|
||||
const clang::Diagnostic &Info) override {
|
||||
if (DiagLevel == clang::DiagnosticsEngine::Level::Note) {
|
||||
return;
|
||||
void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
|
||||
const clang::Diagnostic& Info) override {
|
||||
if(DiagLevel == clang::DiagnosticsEngine::Level::Note) {
|
||||
return;
|
||||
}
|
||||
|
||||
llvm::errs() << "Diagnostic: ";
|
||||
llvm::SmallVector<char> buf;
|
||||
Info.FormatDiagnostic(buf);
|
||||
llvm::errs().write(buf.data(), buf.size());
|
||||
llvm::errs() << "\n";
|
||||
}
|
||||
|
||||
llvm::errs() << "Diagnostic: ";
|
||||
llvm::SmallVector<char> buf;
|
||||
Info.FormatDiagnostic(buf);
|
||||
llvm::errs().write(buf.data(), buf.size());
|
||||
llvm::errs() << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
auto createInstance(std::string_view path, std::string_view compile_commands) {
|
||||
std::unique_ptr<clang::CompilerInstance> instance =
|
||||
std::make_unique<clang::CompilerInstance>();
|
||||
std::unique_ptr<clang::CompilerInstance> instance = std::make_unique<clang::CompilerInstance>();
|
||||
|
||||
auto invocation = createInvocation(path, compile_commands);
|
||||
instance->setInvocation(
|
||||
std::make_shared<clang::CompilerInvocation>(*invocation));
|
||||
auto invocation = createInvocation(path, compile_commands);
|
||||
instance->setInvocation(std::make_shared<clang::CompilerInvocation>(*invocation));
|
||||
|
||||
instance->createDiagnostics(new DiagnosticConsumer(), true);
|
||||
instance->createDiagnostics(new DiagnosticConsumer(), true);
|
||||
|
||||
if (!instance->createTarget()) {
|
||||
llvm::errs() << "Failed to create target\n";
|
||||
std::terminate();
|
||||
}
|
||||
if(!instance->createTarget()) {
|
||||
llvm::errs() << "Failed to create target\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
if (auto manager = instance->createFileManager()) {
|
||||
instance->createSourceManager(*manager);
|
||||
} else {
|
||||
llvm::errs() << "Failed to create file manager\n";
|
||||
std::terminate();
|
||||
}
|
||||
if(auto manager = instance->createFileManager()) {
|
||||
instance->createSourceManager(*manager);
|
||||
} else {
|
||||
llvm::errs() << "Failed to create file manager\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete);
|
||||
instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete);
|
||||
|
||||
instance->createASTContext();
|
||||
instance->createASTContext();
|
||||
|
||||
return instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
class AST {
|
||||
clang::FrontendAction *action;
|
||||
clang::CompilerInstance *instance;
|
||||
clang::syntax::TokenBuffer *tokens;
|
||||
clang::FrontendAction* action;
|
||||
clang::CompilerInstance* instance;
|
||||
clang::syntax::TokenBuffer* tokens;
|
||||
|
||||
private:
|
||||
AST() = default;
|
||||
AST() = default;
|
||||
|
||||
public:
|
||||
AST(const AST &) = delete;
|
||||
AST(const AST&) = delete;
|
||||
|
||||
AST(AST &&other) noexcept
|
||||
: action(other.action), instance(other.instance), tokens(other.tokens) {
|
||||
other.action = nullptr;
|
||||
other.instance = nullptr;
|
||||
other.tokens = nullptr;
|
||||
}
|
||||
|
||||
~AST() {
|
||||
if (action) {
|
||||
action->EndSourceFile();
|
||||
delete action;
|
||||
delete instance;
|
||||
delete tokens;
|
||||
}
|
||||
}
|
||||
|
||||
static AST create(std::string_view path, std::string_view compile_commands) {
|
||||
AST ast;
|
||||
|
||||
ast.instance = createInstance(path, compile_commands).release();
|
||||
ast.action = new clang::SyntaxOnlyAction();
|
||||
|
||||
const auto &input = ast.instance->getFrontendOpts().Inputs[0];
|
||||
|
||||
if (!ast.action->BeginSourceFile(*ast.instance, input)) {
|
||||
llvm::errs() << "Failed to begin source file\n";
|
||||
std::terminate();
|
||||
AST(AST&& other) noexcept :
|
||||
action(other.action), instance(other.instance), tokens(other.tokens) {
|
||||
other.action = nullptr;
|
||||
other.instance = nullptr;
|
||||
other.tokens = nullptr;
|
||||
}
|
||||
|
||||
clang::syntax::TokenCollector collector = {ast.instance->getPreprocessor()};
|
||||
|
||||
if (llvm::Error error = ast.action->Execute()) {
|
||||
llvm::errs() << "Failed to execute action: " << error << "\n";
|
||||
std::terminate();
|
||||
~AST() {
|
||||
if(action) {
|
||||
action->EndSourceFile();
|
||||
delete action;
|
||||
delete instance;
|
||||
delete tokens;
|
||||
}
|
||||
}
|
||||
|
||||
ast.tokens = new auto(std::move(collector).consume());
|
||||
static AST create(std::string_view path, std::string_view compile_commands) {
|
||||
AST ast;
|
||||
|
||||
return ast;
|
||||
}
|
||||
ast.instance = createInstance(path, compile_commands).release();
|
||||
ast.action = new clang::SyntaxOnlyAction();
|
||||
|
||||
auto &getASTContext() { return instance->getASTContext(); }
|
||||
const auto& input = ast.instance->getFrontendOpts().Inputs[0];
|
||||
|
||||
auto &getSourceManager() { return instance->getSourceManager(); }
|
||||
if(!ast.action->BeginSourceFile(*ast.instance, input)) {
|
||||
llvm::errs() << "Failed to begin source file\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
auto getTokens() { return tokens->expandedTokens(); }
|
||||
clang::syntax::TokenCollector collector = {ast.instance->getPreprocessor()};
|
||||
|
||||
if(llvm::Error error = ast.action->Execute()) {
|
||||
llvm::errs() << "Failed to execute action: " << error << "\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
ast.tokens = new auto(std::move(collector).consume());
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
auto& getASTContext() { return instance->getASTContext(); }
|
||||
|
||||
auto& getSourceManager() { return instance->getSourceManager(); }
|
||||
|
||||
auto getTokens() { return tokens->expandedTokens(); }
|
||||
};
|
||||
|
||||
struct Visitor : clang::RecursiveASTVisitor<Visitor> {
|
||||
bool VisitTranslationUnitDecl(clang::TranslationUnitDecl *tu) {
|
||||
tu->dump();
|
||||
return true;
|
||||
}
|
||||
bool VisitTranslationUnitDecl(clang::TranslationUnitDecl* tu) {
|
||||
tu->dump();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitCXXMethodDecl(clang::CXXMethodDecl *decl) { return true; }
|
||||
bool VisitCXXMethodDecl(clang::CXXMethodDecl* decl) { return true; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
@@ -1,44 +1,55 @@
|
||||
#include <simdjson.h>
|
||||
#include <LSP/Server.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
uv_loop_t* Server::loop;
|
||||
uv_pipe_t Server::stdin_pipe;
|
||||
Server server;
|
||||
|
||||
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
if(nread > 0) {
|
||||
printf("Read: %s\n", buf->base);
|
||||
} else if(nread < 0) {
|
||||
if(nread != UV_EOF) {
|
||||
fprintf(stderr, "Read error: %s\n", uv_err_name(nread));
|
||||
}
|
||||
|
||||
if(!uv_is_closing((uv_handle_t*)&pipe)) {
|
||||
uv_close((uv_handle_t*)&pipe, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if(buf->base) {
|
||||
free(buf->base);
|
||||
}
|
||||
}
|
||||
|
||||
static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = (char*)malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
int Server::Initialize() {
|
||||
int Server::initialize() {
|
||||
loop = uv_default_loop();
|
||||
|
||||
// initialize pipe and bind to stdin
|
||||
uv_pipe_init(loop, &stdin_pipe, 0);
|
||||
uv_pipe_open(&stdin_pipe, 0);
|
||||
|
||||
auto alloc_buffer = [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = (char*)std::malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
};
|
||||
|
||||
auto on_read = [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
if(nread > 0) {
|
||||
server.handle_message(std::string_view(buf->base, nread));
|
||||
} else if(nread < 0) {
|
||||
// TODO: error handling
|
||||
}
|
||||
std::free(buf->base);
|
||||
};
|
||||
|
||||
uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, on_read);
|
||||
|
||||
// start the event loop
|
||||
return uv_run(loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
int Server::Exit() {
|
||||
uv_close((uv_handle_t*)&stdin_pipe, NULL);
|
||||
return uv_run(loop, UV_RUN_DEFAULT);
|
||||
int Server::exit() {
|
||||
// close the pipe
|
||||
// uv_close((uv_handle_t*)&stdin_pipe, NULL);
|
||||
|
||||
// stop the event loop
|
||||
uv_stop(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Server::handle_message(std::string_view message) {
|
||||
const char* json = R"({
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"city": "New York"
|
||||
})";
|
||||
|
||||
simdjson::dom::parser parser;
|
||||
|
||||
} // namespace clice
|
||||
|
||||
} // namespace clice
|
||||
|
||||
80
src/main.cpp
80
src/main.cpp
@@ -1,8 +1,82 @@
|
||||
#include <LSP/Server.h>
|
||||
#include <coroutine>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
uv_loop_t* loop = nullptr;
|
||||
|
||||
struct Task {
|
||||
struct promise_type {
|
||||
Task get_return_object() { return Task{Handle::from_promise(*this)}; }
|
||||
|
||||
std::suspend_never initial_suspend() { return {}; }
|
||||
|
||||
std::suspend_always final_suspend() noexcept { return {}; }
|
||||
|
||||
void return_void() {}
|
||||
|
||||
void unhandled_exception() { std::terminate(); }
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
std::coroutine_handle<promise_type> coro;
|
||||
|
||||
static Handle from_promise(promise_type& p) {
|
||||
return Handle{std::coroutine_handle<promise_type>::from_promise(p)};
|
||||
}
|
||||
|
||||
~Handle() {
|
||||
if(coro)
|
||||
coro.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
Handle h;
|
||||
};
|
||||
|
||||
struct uv_awaitable {
|
||||
uv_work_t req;
|
||||
std::function<void()> work_fn;
|
||||
std::coroutine_handle<> handle;
|
||||
|
||||
bool await_ready() { return false; }
|
||||
|
||||
void await_suspend(std::coroutine_handle<> handle) {
|
||||
this->handle = handle;
|
||||
req.data = this;
|
||||
std::cout << "Scheduling work..." << std::endl;
|
||||
uv_queue_work(
|
||||
loop,
|
||||
&req,
|
||||
[](uv_work_t* req) {
|
||||
auto* self = static_cast<uv_awaitable*>(req->data);
|
||||
self->work_fn();
|
||||
},
|
||||
[](uv_work_t* req, int status) {
|
||||
auto& handle = static_cast<uv_awaitable*>(req->data)->handle;
|
||||
if(handle.done()) {
|
||||
std::cout << "Work done" << std::endl;
|
||||
} else {
|
||||
handle.resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto await_resume() { return 1; }
|
||||
};
|
||||
|
||||
Task async_factorial(int n) {
|
||||
long result = 1;
|
||||
auto s = co_await uv_awaitable{.work_fn = [&result, n] {
|
||||
for(int i = 1; i <= n; ++i) {
|
||||
result *= i;
|
||||
}
|
||||
}};
|
||||
std::cout << "Factorial of " << n << " is " << result << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace clice;
|
||||
Server::Initialize();
|
||||
Server::Exit();
|
||||
auto& server = clice::server;
|
||||
server.initialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user