From b2fc464d865732867bde7e97f3a297ad5f33b2ed Mon Sep 17 00:00:00 2001 From: ykiko Date: Sun, 15 Sep 2024 13:50:49 +0800 Subject: [PATCH] some update. --- CMakeLists.txt | 4 +- docs/SemanticTokens-example.cpp | 28 ++++ docs/roadmap.md | 3 +- include/Protocol/Document.h | 8 +- include/Server/Logger.h | 45 ++++++ include/Server/Server.h | 2 - include/Server/Transport .h | 34 ----- src/AST/ParsedAST.cpp | 4 +- src/Feature/SemanticTokens.cpp | 252 ++++++++++++++++++++----------- src/Server/Scheduler.cpp | 1 - src/Server/Server.cpp | 180 +++++++++++++++++----- src/Server/Transport.cpp | 128 ---------------- tests/Feature/SemanticTokens.cpp | 45 ++++++ tests/test.cpp | 29 ++-- 14 files changed, 450 insertions(+), 313 deletions(-) create mode 100644 docs/SemanticTokens-example.cpp create mode 100644 include/Server/Logger.h delete mode 100644 include/Server/Transport .h delete mode 100644 src/Server/Transport.cpp create mode 100644 tests/Feature/SemanticTokens.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec0a07fb..ca3666eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,7 +93,7 @@ if(CLICE_ENABLE_TEST) target_link_libraries(clice_test PRIVATE gtest_main) target_clang(clice_test) - file(GLOB_RECURSE AST_SRC_FILES "${CMAKE_SOURCE_DIR}/src/AST/*.cpp") - file(GLOB_RECURSE TEST_SRC_FILES "${CMAKE_SOURCE_DIR}/tests/*.cpp") + file(GLOB_RECURSE AST_SRC_FILES "${CMAKE_SOURCE_DIR}/src/AST/*.cpp" "${CMAKE_SOURCE_DIR}/src/Feature/*.cpp") + file(GLOB_RECURSE TEST_SRC_FILES "${CMAKE_SOURCE_DIR}/tests/*/*.cpp") target_sources(clice_test PRIVATE ${AST_SRC_FILES} ${TEST_SRC_FILES}) endif() diff --git a/docs/SemanticTokens-example.cpp b/docs/SemanticTokens-example.cpp new file mode 100644 index 00000000..3c0f2c84 --- /dev/null +++ b/docs/SemanticTokens-example.cpp @@ -0,0 +1,28 @@ +// This is an example file that shows the usage of semantic tokens. +// clang-format off + + + char character = 'a'; +// ^ ^ +// keyword character + + int number = 1; +// ^ ^ +// keyword number + + const char* string = "Hello, World!"; +// ^ ^ +// keyword string + + #pragma once +// ^ ^ +// directive pragma + + #include +// ^ ^ +// directive header + + #define MAX 100 +// ^ ^ +// directive macro + diff --git a/docs/roadmap.md b/docs/roadmap.md index a7bc2f83..10972418 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,4 +1,5 @@ - implement basic server and client - implement SemanticTokens - implement CodeCompletion -- use `TreeTransform` to simplify `DependentNameResolver` \ No newline at end of file +- use `TreeTransform` to simplify `DependentNameResolver` +- refactor `clang/lib/SemaComplete.cpp` \ No newline at end of file diff --git a/include/Protocol/Document.h b/include/Protocol/Document.h index c0441dd7..0baf49d5 100644 --- a/include/Protocol/Document.h +++ b/include/Protocol/Document.h @@ -9,14 +9,18 @@ struct DidOpenTextDocumentParams { TextDocumentItem textDocument; }; +struct TextDocumentContentChangeEvent { + /// The new text of the whole document. + string text; +}; + struct DidChangeTextDocumentParams { /// The document that did change. The version number points /// to the version after all provided content changes have /// been applied. VersionedTextDocumentIdentifier textDocument; - // FIXME: - // std::vector contentChanges; + std::vector contentChanges; }; } // namespace clice::protocol diff --git a/include/Server/Logger.h b/include/Server/Logger.h new file mode 100644 index 00000000..8cbef85f --- /dev/null +++ b/include/Server/Logger.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace clice::logger { + +inline auto init(std::string_view type, std::string_view filepath) { + if(type == "file") { + llvm::SmallString<128> temp; + temp.append(path::parent_path(path::parent_path(filepath))); + path::append(temp, "logs"); + + auto error = llvm::sys::fs::make_absolute(temp); + auto now = std::chrono::system_clock::now(); + std::time_t now_c = std::chrono::system_clock::to_time_t(now); + std::tm now_tm = *std::localtime(&now_c); + + std::ostringstream timeStream; + timeStream << std::put_time(&now_tm, "%Y-%m-%d_%H-%M-%S"); + + std::string logFileName = "clice_" + timeStream.str() + ".log"; + path::append(temp, logFileName); + + auto logger = spdlog::basic_logger_mt("clice", temp.c_str()); + logger->flush_on(spdlog::level::trace); + spdlog::set_default_logger(logger); + } else if(type == "console") { + auto logger = spdlog::stdout_color_mt("clice"); + logger->flush_on(spdlog::level::trace); + spdlog::set_default_logger(logger); + } +} + +using spdlog::info; +using spdlog::error; +using spdlog::warn; +using spdlog::debug; +using spdlog::trace; + +} // namespace clice::logger diff --git a/include/Server/Server.h b/include/Server/Server.h index 156b9bd0..08b5a4ec 100644 --- a/include/Server/Server.h +++ b/include/Server/Server.h @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace clice { @@ -14,7 +13,6 @@ struct Server { Scheduler scheduler; CompilationDatabase CDB; llvm::StringMap handlers; - std::unique_ptr transport; static Server instance; diff --git a/include/Server/Transport .h b/include/Server/Transport .h deleted file mode 100644 index af086da8..00000000 --- a/include/Server/Transport .h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -namespace clice { - -class Transport { -public: - virtual void write(std::string_view message) = 0; -}; - -class Pipe : public Transport { -public: - Pipe(uv_loop_t* loop, void (*callback)(std::string_view)); - - void write(std::string_view message) override; - -private: - uv_pipe_t stdin_pipe; - uv_pipe_t stdout_pipe; -}; - -class Socket : public Transport { -public: - Socket(uv_loop_t* loop, void (*callback)(std::string_view), const char* host, unsigned int port); - - void write(std::string_view message) override; - -private: - uv_tcp_t server; -}; - -} // namespace clice diff --git a/src/AST/ParsedAST.cpp b/src/AST/ParsedAST.cpp index 74770afb..3de9ebe0 100644 --- a/src/AST/ParsedAST.cpp +++ b/src/AST/ParsedAST.cpp @@ -21,7 +21,7 @@ std::unique_ptr ParsedAST::build(llvm::StringRef filename, auto invocation = std::make_shared(); invocation = clang::createInvocation(args, options); - auto buffer = llvm::MemoryBuffer::getMemBuffer(content, filename); + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content, filename); if(preamble) { auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false); @@ -42,7 +42,7 @@ std::unique_ptr ParsedAST::build(llvm::StringRef filename, auto instance = std::make_unique(); instance->setInvocation(std::move(invocation)); - instance->createDiagnostics(new Diagnostic(), true); + instance->createDiagnostics(new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true); // if(auto remappingVSF = // createVFSFromCompilerInvocation(instance->getInvocation(), instance->getDiagnostics(), vfs)) { diff --git a/src/Feature/SemanticTokens.cpp b/src/Feature/SemanticTokens.cpp index 963f284f..92337929 100644 --- a/src/Feature/SemanticTokens.cpp +++ b/src/Feature/SemanticTokens.cpp @@ -5,15 +5,16 @@ namespace clice::feature { namespace { -#define Traverse(NAME) bool Traverse##NAME(clang::NAME* node) -#define WalkUpFrom(NAME) bool WalkUpFrom##NAME(clang::NAME* node) -#define VISIT(NAME) bool Visit##NAME(clang::NAME* node) -#define VISIT_TYPE(NAME) bool Visit##NAME(clang::NAME node) - struct SemanticToken { - std::uint32_t modifiers = 0; - clang::SourceRange range; + clang::SourceLocation begin; + std::size_t length; protocol::SemanticTokenType type; + std::uint32_t modifiers = 0; + + SemanticToken& addModifier(protocol::SemanticTokenModifier modifier) { + modifiers |= 1 << static_cast(modifier); + return *this; + } }; static bool isKeyword(clang::tok::TokenKind kind, llvm::StringRef text, const clang::LangOptions& option) { @@ -165,81 +166,76 @@ static bool isKeyword(clang::tok::TokenKind kind, llvm::StringRef text, const cl class HighlightBuilder { public: - HighlightBuilder(const ParsedAST& parsedAST, llvm::StringRef filename) { - auto fileID = parsedAST.getFileID(filename); - auto tokens = parsedAST.spelledTokens(fileID); - for(auto& token: tokens) { - SemanticToken semanticToken; - semanticToken.range = clang::SourceRange(token.location(), token.endLocation()); - if(isKeyword(token.kind(), token.text(parsedAST.sourceManager), parsedAST.context.getLangOpts())) { - semanticToken.type = protocol::SemanticTokenType::Keyword; - } else { - semanticToken.type = protocol::SemanticTokenType::Number; - } - semanticToken.modifiers = 0; - result.emplace_back(std::move(semanticToken)); + HighlightBuilder(const ParsedAST& parsedAST, llvm::StringRef filename) : AST(parsedAST), filename(filename) {} + + SemanticToken& addToken(protocol::SemanticTokenType type, clang::SourceLocation begin, std::size_t length) { + result.emplace_back(begin, length, type, 0); + return result.back(); + } + + SemanticToken& addToken(protocol::SemanticTokenType type, clang::SourceLocation loc) { + auto token = AST.tokenBuffer.spelledTokenContaining(loc); + if(token) { + return addToken(type, token->location(), token->length()); } } - std::vector build() { return std::move(result); } + // FIXME: source range can be a multi-line range, split it into multiple tokens + // SemanticToken& addToken(protocol::SemanticTokenType type, clang::SourceRange range) { + // SemanticToken token; + // token.begin = range.getBegin(); + // token.length = + // AST.sourceManager.getFileOffset(range.getEnd()) - AST.sourceManager.getFileOffset(range.getBegin()); + // token.type = type; + // result.emplace_back(std::move(token)); + // return result.back(); + //} + + void addAngle(clang::SourceLocation left, clang::SourceLocation right) {} + + std::vector build(); private: + const ParsedAST& AST; + llvm::StringRef filename; std::vector result; }; -class Highlighter : public clang::RecursiveASTVisitor { +/// Collect highlight information from AST. +class HighlightCollector : public clang::RecursiveASTVisitor { public: - Highlighter(ParsedAST& ast) : - fileManager(ast.fileManager), preproc(ast.preproc), sourceManager(ast.sourceManager), context(ast.context), - tokenBuffer(ast.tokenBuffer) {} + HighlightCollector(const ParsedAST& AST, HighlightBuilder& builder) : AST(AST), builder(builder) {} - std::vector highlight(llvm::StringRef filepath) { - std::vector result; + // Traverse(TranslationUnitDecl) { + // for(auto decl: node->decls()) { + // // FIXME: some decls are located in their parents' file + // // e.g. `ClassTemplateSpecializationDecl`, find and exclude them + // node->getLexicalDeclContext(); + // // if(sourceManager.isInFileID(decl->getLocation(), fileID)) { + // // TraverseDecl(decl); + // // } + // } + // return true; + // } - auto entry = fileManager.getFileRef(filepath); - if(auto error = entry.takeError()) { - // TODO: - } - auto fileID = sourceManager.translateFile(entry.get()); +#define Traverse(NAME) bool Traverse##NAME(clang::NAME* node) +#define WalkUpFrom(NAME) bool WalkUpFrom##NAME(clang::NAME* node) +#define VISIT(NAME) bool Visit##NAME(clang::NAME* node) +#define VISIT_TYPE(NAME) bool Visit##NAME(clang::NAME node) - this->fileID = fileID; - this->result = &result; - - // highlight from tokens - // TODO: use TokenBuffer to get tokens - - // TODO: highlight from directive - - // highlight from AST - TraverseDecl(context.getTranslationUnitDecl()); - - return {}; - } - -private: - void addAngle(clang::SourceLocation left, clang::SourceLocation right) {} - -public: - Traverse(TranslationUnitDecl) { - for(auto decl: node->decls()) { - // FIXME: some decls are located in their parents' file - // e.g. `ClassTemplateSpecializationDecl`, find and exclude them - node->getLexicalDeclContext(); - if(sourceManager.isInFileID(decl->getLocation(), fileID)) { - TraverseDecl(decl); - } - } - return true; - } - - WalkUpFrom(NamespaceDecl) {} + // WalkUpFrom(NamespaceDecl) {} VISIT(ImportDecl) {} + VISIT(NamespaceDecl) { + builder.addToken(protocol::SemanticTokenType::Namespace, node->getLocation()); + return true; + } + VISIT(DeclaratorDecl) { for(unsigned i = 0; i < node->getNumTemplateParameterLists(); ++i) { if(auto params = node->getTemplateParameterList(i)) { - addAngle(params->getLAngleLoc(), params->getRAngleLoc()); + builder.addAngle(params->getLAngleLoc(), params->getRAngleLoc()); } } return true; @@ -248,7 +244,7 @@ public: VISIT(TagDecl) { for(unsigned i = 0; i < node->getNumTemplateParameterLists(); ++i) { if(auto params = node->getTemplateParameterList(i)) { - addAngle(params->getLAngleLoc(), params->getRAngleLoc()); + builder.addAngle(params->getLAngleLoc(), params->getRAngleLoc()); } } return true; @@ -256,105 +252,177 @@ public: VISIT(FunctionDecl) { if(auto args = node->getTemplateSpecializationArgsAsWritten()) { - addAngle(args->getLAngleLoc(), args->getRAngleLoc()); + builder.addAngle(args->getLAngleLoc(), args->getRAngleLoc()); } + builder.addToken(protocol::SemanticTokenType::Function, node->getLocation()); return true; } VISIT(TemplateDecl) { if(auto params = node->getTemplateParameters()) { - addAngle(params->getLAngleLoc(), params->getRAngleLoc()); + builder.addAngle(params->getLAngleLoc(), params->getRAngleLoc()); } return true; } VISIT(ClassTemplateSpecializationDecl) { if(auto args = node->getTemplateArgsAsWritten()) { - addAngle(args->getLAngleLoc(), args->getRAngleLoc()); + builder.addAngle(args->getLAngleLoc(), args->getRAngleLoc()); } return true; } VISIT(ClassTemplatePartialSpecializationDecl) { if(auto params = node->getTemplateParameters()) { - addAngle(params->getLAngleLoc(), params->getRAngleLoc()); + builder.addAngle(params->getLAngleLoc(), params->getRAngleLoc()); } return true; } VISIT(VarTemplateSpecializationDecl) { if(auto args = node->getTemplateArgsAsWritten()) { - addAngle(args->LAngleLoc, args->RAngleLoc); + builder.addAngle(args->LAngleLoc, args->RAngleLoc); } return true; } VISIT(VarTemplatePartialSpecializationDecl) { if(auto params = node->getTemplateParameters()) { - addAngle(params->getLAngleLoc(), params->getRAngleLoc()); + builder.addAngle(params->getLAngleLoc(), params->getRAngleLoc()); } return true; } VISIT(CXXNamedCastExpr) { - addAngle(node->getAngleBrackets().getBegin(), node->getAngleBrackets().getEnd()); + builder.addAngle(node->getAngleBrackets().getBegin(), node->getAngleBrackets().getEnd()); return true; } VISIT(OverloadExpr) { - addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); return true; } VISIT(CXXDependentScopeMemberExpr) { - addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); return true; } VISIT(DependentScopeDeclRefExpr) { - addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + builder.addAngle(node->getLAngleLoc(), node->getRAngleLoc()); + return true; + } + + VISIT_TYPE(DependentNameTypeLoc) { + // DependentNameType: `typename T::type` + // ^~~~ highlight this + builder.addToken(protocol::SemanticTokenType::Type, node.getNameLoc()); return true; } VISIT_TYPE(TemplateSpecializationTypeLoc) { - addAngle(node.getLAngleLoc(), node.getRAngleLoc()); + node.dump(); + builder.addAngle(node.getLAngleLoc(), node.getRAngleLoc()); return true; } VISIT_TYPE(DependentTemplateSpecializationTypeLoc) { - addAngle(node.getLAngleLoc(), node.getRAngleLoc()); + builder.addAngle(node.getLAngleLoc(), node.getRAngleLoc()); return true; } -private: - clang::FileManager& fileManager; - clang::Preprocessor& preproc; - clang::SourceManager& sourceManager; - clang::ASTContext& context; - clang::syntax::TokenBuffer& tokenBuffer; + bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc loc) { + if(clang::NestedNameSpecifier* NNS = loc.getNestedNameSpecifier()) { + if(NNS->getKind() == clang::NestedNameSpecifier::Identifier) { + // NestedNameSpecifier: `T::type::` + // ^~~~ highlight this + builder.addToken(protocol::SemanticTokenType::Type, loc.getLocalBeginLoc()); + } + } + return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(loc); + } - clang::FileID fileID; - std::vector* result; +private: + const ParsedAST& AST; + HighlightBuilder& builder; }; +std::vector HighlightBuilder::build() { + auto fileID = AST.getFileID(filename); + auto tokens = AST.spelledTokens(fileID); + + // highlight from tokens. + for(auto& token: tokens) { + protocol::SemanticTokenType type = protocol::SemanticTokenType::LAST_TYPE; + + switch(token.kind()) { + case clang::tok::TokenKind::numeric_constant: { + type = protocol::SemanticTokenType::Number; + break; + } + case clang::tok::char_constant: + case clang::tok::wide_char_constant: + case clang::tok::utf8_char_constant: + case clang::tok::utf16_char_constant: + case clang::tok::utf32_char_constant: { + type = protocol::SemanticTokenType::Character; + break; + } + case clang::tok::string_literal: + case clang::tok::wide_string_literal: + case clang::tok::utf8_string_literal: + case clang::tok::utf16_string_literal: + case clang::tok::utf32_string_literal: { + type = protocol::SemanticTokenType::String; + break; + } + default: { + if(isKeyword(token.kind(), token.text(AST.sourceManager), AST.context.getLangOpts())) { + type = protocol::SemanticTokenType::Keyword; + break; + } + } + } + + if(type != protocol::SemanticTokenType::LAST_TYPE) { + addToken(type, token.location(), token.length()); + } + } + + // TODO: highlight from preprocessor. + + // highlight from AST. + HighlightCollector collector(AST, *this); + collector.TraverseTranslationUnitDecl(AST.context.getTranslationUnitDecl()); + // AST.context.getTranslationUnitDecl()->dump(); + + llvm::sort(result, [](const SemanticToken& lhs, const SemanticToken& rhs) { + return lhs.begin < rhs.begin; + }); + + return std::move(result); +} + } // namespace protocol::SemanticTokens semanticTokens(const ParsedAST& AST, llvm::StringRef filename) { HighlightBuilder builder(AST, filename); std::vector tokens = builder.build(); + // for(auto& token: tokens) { + // spdlog::info("{}", token.dump(AST.sourceManager)); + // } + protocol::SemanticTokens result; unsigned int last_line = 0; unsigned int last_column = 0; /// FXIME: resolve position encoding - for(std::size_t index = 0; index < tokens.size(); ++index) { - auto& token = tokens[index]; - auto line = AST.sourceManager.getPresumedLineNumber(token.range.getBegin()) - 1; - auto column = AST.sourceManager.getPresumedColumnNumber(token.range.getBegin()) - 1; - auto length = AST.sourceManager.getPresumedColumnNumber(token.range.getEnd()) - column; + for(auto& token: tokens) { + auto line = AST.sourceManager.getPresumedLineNumber(token.begin) - 1; + auto column = AST.sourceManager.getPresumedColumnNumber(token.begin) - 1; result.data.push_back(line - last_line); - result.data.push_back(column > last_column ? column - last_column : column); - result.data.push_back(length); + result.data.push_back(line == last_line ? column - last_column : column); + result.data.push_back(token.length); result.data.push_back(token.type); result.data.push_back(token.modifiers); diff --git a/src/Server/Scheduler.cpp b/src/Server/Scheduler.cpp index 871098a4..852f1377 100644 --- a/src/Server/Scheduler.cpp +++ b/src/Server/Scheduler.cpp @@ -16,7 +16,6 @@ void Scheduler::dispatch(json::Value id, std::string_view method, json::Value va if(method == "textDocument/didOpen") { auto params = json::deserialize(value); auto AST = ParsedAST::build("main.cpp", params.textDocument.text, compileArgs); - spdlog::info("read file: {}", params.textDocument.text); files[params.textDocument.uri].ast = std::move(AST); } else if(method == "textDocument/didChange") { auto params = json::deserialize(value); diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index 1a28b173..ac1816cf 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -1,27 +1,147 @@ #include -#include -#include -#include -#include -#include -#include #include -#include -#include +#include +#include +#include namespace clice { Server Server::instance; -static uv_loop_t* loop; +namespace { + +class MessageBuffer { + std::vector buffer; + std::size_t max = 0; + +public: + void write(std::string_view message) { buffer.insert(buffer.end(), message.begin(), message.end()); } + + std::string_view read() { + std::string_view view = std::string_view(buffer.data(), buffer.size()); + auto start = view.find("Content-Length: ") + 16; + auto end = view.find("\r\n\r\n"); + + if(start != std::string_view::npos && end != std::string_view::npos) { + std::size_t length = std::stoul(std::string(view.substr(start, end - start))); + if(view.size() >= length + end + 4) { + this->max = length + end + 4; + return view.substr(end + 4, length); + } + } + + return {}; + } + + void clear() { + if(max != 0) { + buffer.erase(buffer.begin(), buffer.begin() + max); + max = 0; + } + } +}; + +uv_loop_t* unique_loop; +uv_idle_t idle; + +static llvm::SmallVector buffer; + +void alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buffer.resize(size); + buf->base = buffer.data(); + buf->len = buffer.size(); +} + +MessageBuffer messageBuffer; + +void onRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + if(nread > 0) { + messageBuffer.write(std::string_view(buf->base, nread)); + } else if(nread < 0) { + logger::error("Read error: {}", uv_strerror(nread)); + } +} + +struct Pipe { + inline static uv_pipe_t stdin; + inline static uv_pipe_t stdout; + + static void initialize() { + uv_pipe_init(unique_loop, &stdin, 0); + uv_pipe_open(&stdin, 0); + + uv_pipe_init(unique_loop, &stdout, 0); + uv_pipe_open(&stdout, 1); + + uv_read_start(reinterpret_cast(&stdin), alloc, onRead); + } + + inline static uv_buf_t buf; + inline static uv_write_t req; + + static void write(std::string_view message) { + buf.base = const_cast(message.data()); + buf.len = message.size(); + int state = uv_write(&req, reinterpret_cast(&stdout), &buf, 1, nullptr); + if(state < 0) { + logger::error("Error writing to client: {}", uv_strerror(state)); + } + } +}; + +struct Socket { + inline static uv_tcp_t server; + inline static uv_tcp_t client; + + static void initialize(const char* ip, int port) { + uv_tcp_init(unique_loop, &server); + + sockaddr_in addr; + uv_ip4_addr(ip, port, &addr); + + uv_tcp_bind(&server, reinterpret_cast(&addr), 0); + uv_listen(reinterpret_cast(&server), 1, [](uv_stream_t* server, int status) { + if(status < 0) { + logger::error("Listen error: {}", uv_strerror(status)); + return; + } + + uv_tcp_init(unique_loop, &client); + uv_accept(server, reinterpret_cast(&client)); + uv_read_start(reinterpret_cast(&client), alloc, onRead); + }); + } + + inline static uv_buf_t buf; + inline static uv_write_t req; + + static void write(std::string_view message) { + buf.base = const_cast(message.data()); + buf.len = message.size(); + int state = uv_write(&req, reinterpret_cast(&client), &buf, 1, nullptr); + if(state < 0) { + logger::error("Error writing to client: {}", uv_strerror(state)); + } + } +}; + +} // namespace Server::Server() { + handlers.try_emplace("initialize", [](json::Value id, json::Value value) { auto result = instance.initialize(json::deserialize(value)); instance.response(std::move(id), json::serialize(result)); }); } +void eventloop(uv_idle_t* handle) { + if(auto message = messageBuffer.read(); !message.empty()) { + Server::instance.handleMessage(message); + messageBuffer.clear(); + } +} + auto Server::initialize(protocol::InitializeParams params) -> protocol::InitializeResult { instance.option.parse("/home/ykiko/C++/test"); @@ -34,37 +154,17 @@ int Server::run(int argc, const char** argv) { option.argc = argc; option.argv = argv; - llvm::SmallString<128> temp; - temp.append(path::parent_path(path::parent_path(argv[0]))); - path::append(temp, "logs"); + logger::init("console", argv[0]); - auto error = llvm::sys::fs::make_absolute(temp); - auto now = std::chrono::system_clock::now(); - std::time_t now_c = std::chrono::system_clock::to_time_t(now); - std::tm now_tm = *std::localtime(&now_c); + unique_loop = uv_default_loop(); + Socket::initialize("127.0.0.1", 50505); - std::ostringstream timeStream; - timeStream << std::put_time(&now_tm, "%Y-%m-%d_%H-%M-%S"); // 格式化为 "YYYY-MM-DD_HH-MM-SS" + uv_idle_init(unique_loop, &idle); + uv_idle_start(&idle, eventloop); - std::string logFileName = "clice_" + timeStream.str() + ".log"; - path::append(temp, logFileName); + uv_run(unique_loop, UV_RUN_DEFAULT); - auto logger = spdlog::stdout_color_mt("clice"); - logger->flush_on(spdlog::level::trace); - spdlog::set_default_logger(logger); - - loop = uv_default_loop(); - transport = std::make_unique( - loop, - [](std::string_view message) { - instance.handleMessage(message); - }, - "127.0.0.1", - 50505); - - uv_run(loop, UV_RUN_DEFAULT); - - uv_loop_close(loop); + uv_loop_close(unique_loop); return 0; } @@ -72,10 +172,10 @@ int Server::run(int argc, const char** argv) { void Server::handleMessage(std::string_view message) { auto result = json::parse(message); if(!result) { - spdlog::error("Error parsing JSON: {}", llvm::toString(result.takeError())); + logger::error("Error parsing JSON: {}", llvm::toString(result.takeError())); } - spdlog::info("Received message: {}", message); + logger::info("Received message: {}", message); auto input = result->getAsObject(); auto id = input->get("id"); std::string_view method = input->get("method")->getAsString().value(); @@ -109,8 +209,8 @@ void Server::response(json::Value id, json::Value result) { s = "Content-Length: " + std::to_string(s.size()) + "\r\n\r\n" + s; // FIXME: use more flexible way to do this. - transport->write(s); - spdlog::info("Response: {}", s); + Socket::write(s); + logger::info("Response: {}", s); } } // namespace clice diff --git a/src/Server/Transport.cpp b/src/Server/Transport.cpp deleted file mode 100644 index 62b4fd91..00000000 --- a/src/Server/Transport.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -namespace clice { - -/// NOTE: Receiving and sending messages are only done in the main thread. -/// so it's safe to use the static variable as a buffer. - -namespace { - -class MessageBuffer { - std::vector buffer; - std::size_t max = 0; - -public: - void write(std::string_view message) { buffer.insert(buffer.end(), message.begin(), message.end()); } - - std::string_view read() { - std::string_view view = std::string_view(buffer.data(), buffer.size()); - auto start = view.find("Content-Length: ") + 16; - auto end = view.find("\r\n\r\n"); - - if(start != std::string_view::npos && end != std::string_view::npos) { - std::size_t length = std::stoul(std::string(view.substr(start, end - start))); - if(view.size() >= length + end + 4) { - this->max = length + end + 4; - return view.substr(end + 4, length); - } - } - - return {}; - } - - void clear() { - if(max != 0) { - buffer.erase(buffer.begin(), buffer.begin() + max); - max = 0; - } - } -}; - -void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - static llvm::SmallVector buffer; - buffer.resize(suggested_size); - buf->base = buffer.data(); - buf->len = buffer.size(); -} - -// callback for reading data. -static void (*unique_callback)(std::string_view) = nullptr; - -void read_callback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - static MessageBuffer buffer; - if(nread > 0) { - buffer.write(std::string_view(buf->base, nread)); - if(auto message = buffer.read(); !message.empty()) { - unique_callback(message); - buffer.clear(); - } - } else if(nread < 0) { - // if(nread != UV_EOF) { - // spdlog::error("Read error: {}", uv_err_name(nread)); - // } - uv_close((uv_handle_t*)stream, NULL); - } -} - -} // namespace - -Pipe::Pipe(uv_loop_t* loop, void (*callback)(std::string_view)) { - uv_pipe_init(loop, &stdin_pipe, 0); - uv_pipe_init(loop, &stdout_pipe, 0); - - uv_pipe_open(&stdin_pipe, 0); - uv_pipe_open(&stdout_pipe, 1); - - unique_callback = callback; - uv_read_start(reinterpret_cast(&stdin_pipe), alloc_buffer, read_callback); -} - -void Pipe::write(std::string_view message) { - static uv_buf_t buf; - static uv_write_t req; - buf = uv_buf_init(const_cast(message.data()), message.size()); - - auto state = uv_write(&req, reinterpret_cast(&stdout_pipe), &buf, 1, NULL); - if(state < 0) { - spdlog::error("Error writing to stdout: {}", uv_strerror(state)); - } -} - -static uv_tcp_t unique_client; - -Socket::Socket(uv_loop_t* loop, void (*callback)(std::string_view), const char* host, unsigned int port) { - uv_tcp_init(loop, &server); - sockaddr_in addr; - uv_ip4_addr(host, port, &addr); - uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); - unique_callback = callback; - uv_listen(reinterpret_cast(&server), 1, [](uv_stream_t* server, int status) { - if(status < 0) { - spdlog::error("Listen error: {}", uv_strerror(status)); - return; - } - - spdlog::info("Server listening on port {}", 50505); - uv_tcp_init(uv_default_loop(), (uv_tcp_t*)&unique_client); - if(uv_accept(server, (uv_stream_t*)&unique_client) == 0) { - uv_read_start((uv_stream_t*)&unique_client, alloc_buffer, read_callback); - } else { - uv_close((uv_handle_t*)&unique_client, NULL); - } - }); -} - -void Socket::write(std::string_view message) { - static uv_buf_t buf; - static uv_write_t req; - buf = uv_buf_init(const_cast(message.data()), message.size()); - - auto state = uv_write(&req, (uv_stream_t*)&unique_client, &buf, 1, NULL); - if(state < 0) { - spdlog::error("Error writing to socket: {}", uv_strerror(state)); - } -} - -} // namespace clice diff --git a/tests/Feature/SemanticTokens.cpp b/tests/Feature/SemanticTokens.cpp new file mode 100644 index 00000000..991a536b --- /dev/null +++ b/tests/Feature/SemanticTokens.cpp @@ -0,0 +1,45 @@ +#include +#include +#include + +namespace { + +using namespace clang; + +template +auto f() { + using type = typename T::type; + return T::value; +} + +std::vector compileArgs = { + "clang++", + "-std=c++20", + "main.cpp", + "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", +}; + +TEST(test, test) { + + const char* code = R"( +template +struct X { + using type = T; + static constexpr bool value = true; +}; + +template +auto f() { + using type = typename X::type::type; + return T::value; +} +)"; + + auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs); + auto fileID = AST->getFileID("main.cpp"); + AST->context.getTranslationUnitDecl()->dump(); + auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp"); +} + +} // namespace + diff --git a/tests/test.cpp b/tests/test.cpp index f6f199ce..d522f391 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,10 +1,17 @@ #include #include +#include namespace { using namespace clang; +template +auto f() { + using type = typename T::type; + return T::value; +} + TEST(test, test) { std::vector compileArgs = { "clang++", @@ -14,19 +21,23 @@ TEST(test, test) { }; const char* code = R"( -int x [[maybe_unused]] = 0; +template +struct X { + using type = T; + static constexpr bool value = true; +}; + +template +auto f() { + using type = typename X::type::type; + return T::value; +} )"; auto AST = clice::ParsedAST::build("main.cpp", code, compileArgs); auto fileID = AST->getFileID("main.cpp"); - auto tokens = AST->spelledTokens(fileID); - - // ClassVisitor visitor(&AST->context); - // visitor.TraverseDecl(AST->context.getTranslationUnitDecl()); - - for(auto& token: tokens) { - llvm::outs() << clang::tok::getTokenName(token.kind()) << ": " << token.text(AST->sourceManager) << "\n"; - } + AST->context.getTranslationUnitDecl()->dump(); + auto semanticTokens = clice::feature::semanticTokens(*AST, "main.cpp"); } } // namespace