diff --git a/lsp-server/.clang-format b/.clang-format similarity index 100% rename from lsp-server/.clang-format rename to .clang-format diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..f430938 --- /dev/null +++ b/.clangd @@ -0,0 +1,9 @@ +CompileFlags: + Add: [-std=c++23] + +If: + PathMatch: [.*\.inl$, .*\.tpp$] +CompileFlags: + Add: + - -xc++-header + - -std=c++23 diff --git a/lsp-server/CMakeLists.txt b/lsp-server/CMakeLists.txt index 4a0b932..5a53dc0 100644 --- a/lsp-server/CMakeLists.txt +++ b/lsp-server/CMakeLists.txt @@ -1,12 +1,76 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 4.0) project(tsl-server) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(nlohmann_json REQUIRED) -find_package(spdlog REQUIRED) -find_package(fmt REQUIRED) +message(STATUS "CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +# 设置默认构建类型 +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE + "Release" + CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +if(MSVC) + add_compile_options(/W4 /permissive- /Zc:__cplusplus /utf-8) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +else() + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# MinGW/MSYS2 静态链接 +if(MINGW) + add_link_options(-static -static-libgcc -static-libstdc++) +endif() + +# Linux 静态链接 +if(UNIX AND NOT APPLE) + add_link_options(-static-libgcc -static-libstdc++) + find_package(Threads REQUIRED) +endif() + +if(DEFINED VCPKG_DIR) + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake + CACHE PATH "") + endif() + message(STATUS ">>> Vcpkg: ${CMAKE_TOOLCHAIN_FILE}") + + if(NOT DEFINED VCPKG_TARGET_TRIPLET) + set(VCPKG_TARGET_TRIPLET + "x64-windows-static" + CACHE STRING "") + endif() + + list(APPEND CMAKE_INCLUDE_PATH + ${VCPKG_DIR}/installed/x64-windows-static/include) + list(APPEND CMAKE_PREFIX_PATH ${VCPKG_DIR}/installed/x64-windows-static) +endif() + +set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib" ".so") + +find_package(glaze CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) +find_package(fmt CONFIG REQUIRED) + +if(NOT TARGET spdlog::spdlog_header_only) + message(WARNING "spdlog header-only target not found, using shared library") +endif() +if(NOT TARGET fmt::fmt-header-only) + message(WARNING "fmt header-only target not found, using shared library") +endif() + +if(UNIX AND NOT APPLE) + find_package(Threads REQUIRED) +endif() + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) set(SOURCES src/main.cpp @@ -14,18 +78,25 @@ set(SOURCES src/lsp/dispacther.cpp src/lsp/server.cpp src/provider/base/provider_registry.cpp + src/provider/base/provider_interface.cpp src/provider/initialize/initialize_provider.cpp src/provider/initialized/initialized_provider.cpp src/provider/text_document/did_open_provider.cpp src/provider/text_document/did_change_provider.cpp src/provider/text_document/completion_provider.cpp - src/provider/trace/set_trace_provider.cpp -) + src/provider/trace/set_trace_provider.cpp) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} - nlohmann_json::nlohmann_json - spdlog::spdlog - fmt::fmt -) +target_include_directories(${PROJECT_NAME} PRIVATE src) +target_compile_definitions(${PROJECT_NAME} PRIVATE SPDLOG_HEADER_ONLY + FMT_HEADER_ONLY) + +target_link_libraries( + ${PROJECT_NAME} PRIVATE glaze::glaze spdlog::spdlog_header_only + fmt::fmt-header-only) + +# Linux 需要链接 pthread +if(UNIX AND NOT APPLE) + target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) +endif() diff --git a/lsp-server/NOTICE.md b/lsp-server/NOTICE.md new file mode 100644 index 0000000..0daa3d9 --- /dev/null +++ b/lsp-server/NOTICE.md @@ -0,0 +1,7 @@ +# Third-party Notices + +This project uses the following libraries under the MIT license: + +- [stephenberry/glaze](https://github.com/stephenberry/glaze) +- [gabime/spdlog](https://github.com/gabime/spdlog) +- [fmtlib/fmt](https://github.com/fmtlib/fmt) diff --git a/lsp-server/README.md b/lsp-server/README.md new file mode 100644 index 0000000..584e8f2 --- /dev/null +++ b/lsp-server/README.md @@ -0,0 +1,3 @@ +# TSL Server + +This project depends on third-party libraries licensed under MIT (see [NOTICE.md](./NOTICE.md)). \ No newline at end of file diff --git a/lsp-server/src/language/tsl_keywords.cpp b/lsp-server/src/language/tsl_keywords.cpp index 034f796..f4302fe 100644 --- a/lsp-server/src/language/tsl_keywords.cpp +++ b/lsp-server/src/language/tsl_keywords.cpp @@ -26,27 +26,27 @@ namespace tsl return result; } - std::vector TslKeywords::GetCompletionItems(const std::string& prefix) + std::vector TslKeywords::GetCompletionItems(const std::string& prefix) { InitKeywords(); - std::vector result; + std::vector result; std::string lower_prefix = ToLowerCase(prefix); for (const auto& keyword : keywords_) { if (prefix.empty() || StartsWith(ToLowerCase(keyword.keyword), lower_prefix)) { - lsp::CompletionItem item; + lsp::protocol::CompletionItem item; item.label = keyword.keyword; item.kind = keyword.completion_kind; item.detail = keyword.description; - item.insert_text = keyword.keyword; + item.insertText = keyword.keyword; result.push_back(item); } } // 按字母顺序排序 std::sort(result.begin(), result.end(), - [](const lsp::CompletionItem& a, const lsp::CompletionItem& b) { + [](const lsp::protocol::CompletionItem& a, const lsp::protocol::CompletionItem& b) { return a.label < b.label; }); return result; @@ -74,190 +74,190 @@ namespace tsl return; keywords_ = { // Program Structure - { "program", KeywordCategory::kProgramStructure, "Program declaration", lsp::CompletionItemKind::kKeyword }, - { "function", KeywordCategory::kProgramStructure, "Function declaration", lsp::CompletionItemKind::kFunction }, - { "procedure", KeywordCategory::kProgramStructure, "Procedure declaration", lsp::CompletionItemKind::kFunction }, - { "unit", KeywordCategory::kProgramStructure, "Unit declaration", lsp::CompletionItemKind::kModule }, - { "uses", KeywordCategory::kProgramStructure, "Uses clause", lsp::CompletionItemKind::kKeyword }, - { "implementation", KeywordCategory::kProgramStructure, "Implementation section", lsp::CompletionItemKind::kKeyword }, - { "interface", KeywordCategory::kProgramStructure, "Interface section", lsp::CompletionItemKind::kInterface }, - { "initialization", KeywordCategory::kProgramStructure, "Initialization section", lsp::CompletionItemKind::kKeyword }, - { "finalization", KeywordCategory::kProgramStructure, "Finalization section", lsp::CompletionItemKind::kKeyword }, + { "program", KeywordCategory::kProgramStructure, "Program declaration", lsp::protocol::CompletionItemKind::kKeyword }, + { "function", KeywordCategory::kProgramStructure, "Function declaration", lsp::protocol::CompletionItemKind::kFunction }, + { "procedure", KeywordCategory::kProgramStructure, "Procedure declaration", lsp::protocol::CompletionItemKind::kFunction }, + { "unit", KeywordCategory::kProgramStructure, "Unit declaration", lsp::protocol::CompletionItemKind::kModule }, + { "uses", KeywordCategory::kProgramStructure, "Uses clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "implementation", KeywordCategory::kProgramStructure, "Implementation section", lsp::protocol::CompletionItemKind::kKeyword }, + { "interface", KeywordCategory::kProgramStructure, "Interface section", lsp::protocol::CompletionItemKind::kInterface }, + { "initialization", KeywordCategory::kProgramStructure, "Initialization section", lsp::protocol::CompletionItemKind::kKeyword }, + { "finalization", KeywordCategory::kProgramStructure, "Finalization section", lsp::protocol::CompletionItemKind::kKeyword }, // Data Types - { "string", KeywordCategory::kDataTypes, "String data type", lsp::CompletionItemKind::kClass }, - { "integer", KeywordCategory::kDataTypes, "Integer data type", lsp::CompletionItemKind::kClass }, - { "boolean", KeywordCategory::kDataTypes, "Boolean data type", lsp::CompletionItemKind::kClass }, - { "int64", KeywordCategory::kDataTypes, "64-bit integer data type", lsp::CompletionItemKind::kClass }, - { "real", KeywordCategory::kDataTypes, "Real number data type", lsp::CompletionItemKind::kClass }, - { "array", KeywordCategory::kDataTypes, "Array data type", lsp::CompletionItemKind::kClass }, + { "string", KeywordCategory::kDataTypes, "String data type", lsp::protocol::CompletionItemKind::kClass }, + { "integer", KeywordCategory::kDataTypes, "Integer data type", lsp::protocol::CompletionItemKind::kClass }, + { "boolean", KeywordCategory::kDataTypes, "Boolean data type", lsp::protocol::CompletionItemKind::kClass }, + { "int64", KeywordCategory::kDataTypes, "64-bit integer data type", lsp::protocol::CompletionItemKind::kClass }, + { "real", KeywordCategory::kDataTypes, "Real number data type", lsp::protocol::CompletionItemKind::kClass }, + { "array", KeywordCategory::kDataTypes, "Array data type", lsp::protocol::CompletionItemKind::kClass }, // Class Types - { "type", KeywordCategory::kClassTypes, "Type declaration", lsp::CompletionItemKind::kKeyword }, - { "class", KeywordCategory::kClassTypes, "Class declaration", lsp::CompletionItemKind::kClass }, - { "fakeclass", KeywordCategory::kClassTypes, "Fake class declaration", lsp::CompletionItemKind::kClass }, - { "new", KeywordCategory::kClassTypes, "Object instantiation", lsp::CompletionItemKind::kKeyword }, + { "type", KeywordCategory::kClassTypes, "Type declaration", lsp::protocol::CompletionItemKind::kKeyword }, + { "class", KeywordCategory::kClassTypes, "Class declaration", lsp::protocol::CompletionItemKind::kClass }, + { "fakeclass", KeywordCategory::kClassTypes, "Fake class declaration", lsp::protocol::CompletionItemKind::kClass }, + { "new", KeywordCategory::kClassTypes, "Object instantiation", lsp::protocol::CompletionItemKind::kKeyword }, // Class Modifiers - { "override", KeywordCategory::kClassModifiers, "Override method", lsp::CompletionItemKind::kKeyword }, - { "overload", KeywordCategory::kClassModifiers, "Overload method", lsp::CompletionItemKind::kKeyword }, - { "virtual", KeywordCategory::kClassModifiers, "Virtual method", lsp::CompletionItemKind::kKeyword }, - { "property", KeywordCategory::kClassModifiers, "Property declaration", lsp::CompletionItemKind::kProperty }, - { "self", KeywordCategory::kClassModifiers, "Self reference", lsp::CompletionItemKind::kVariable }, - { "inherited", KeywordCategory::kClassModifiers, "Inherited method call", lsp::CompletionItemKind::kKeyword }, + { "override", KeywordCategory::kClassModifiers, "Override method", lsp::protocol::CompletionItemKind::kKeyword }, + { "overload", KeywordCategory::kClassModifiers, "Overload method", lsp::protocol::CompletionItemKind::kKeyword }, + { "virtual", KeywordCategory::kClassModifiers, "Virtual method", lsp::protocol::CompletionItemKind::kKeyword }, + { "property", KeywordCategory::kClassModifiers, "Property declaration", lsp::protocol::CompletionItemKind::kProperty }, + { "self", KeywordCategory::kClassModifiers, "Self reference", lsp::protocol::CompletionItemKind::kVariable }, + { "inherited", KeywordCategory::kClassModifiers, "Inherited method call", lsp::protocol::CompletionItemKind::kKeyword }, // Access Modifiers - { "public", KeywordCategory::kAccessModifiers, "Public access modifier", lsp::CompletionItemKind::kKeyword }, - { "protected", KeywordCategory::kAccessModifiers, "Protected access modifier", lsp::CompletionItemKind::kKeyword }, - { "private", KeywordCategory::kAccessModifiers, "Private access modifier", lsp::CompletionItemKind::kKeyword }, - { "published", KeywordCategory::kAccessModifiers, "Published access modifier", lsp::CompletionItemKind::kKeyword }, + { "public", KeywordCategory::kAccessModifiers, "Public access modifier", lsp::protocol::CompletionItemKind::kKeyword }, + { "protected", KeywordCategory::kAccessModifiers, "Protected access modifier", lsp::protocol::CompletionItemKind::kKeyword }, + { "private", KeywordCategory::kAccessModifiers, "Private access modifier", lsp::protocol::CompletionItemKind::kKeyword }, + { "published", KeywordCategory::kAccessModifiers, "Published access modifier", lsp::protocol::CompletionItemKind::kKeyword }, // Property Accessors - { "read", KeywordCategory::kPropertyAccessors, "Property read accessor", lsp::CompletionItemKind::kKeyword }, - { "write", KeywordCategory::kPropertyAccessors, "Property write accessor", lsp::CompletionItemKind::kKeyword }, + { "read", KeywordCategory::kPropertyAccessors, "Property read accessor", lsp::protocol::CompletionItemKind::kKeyword }, + { "write", KeywordCategory::kPropertyAccessors, "Property write accessor", lsp::protocol::CompletionItemKind::kKeyword }, // Constructors - { "create", KeywordCategory::kConstructors, "Constructor method", lsp::CompletionItemKind::kConstructor }, - { "destroy", KeywordCategory::kConstructors, "Destructor method", lsp::CompletionItemKind::kMethod }, - { "operator", KeywordCategory::kConstructors, "Operator overload", lsp::CompletionItemKind::kOperator }, + { "create", KeywordCategory::kConstructors, "Constructor method", lsp::protocol::CompletionItemKind::kConstructor }, + { "destroy", KeywordCategory::kConstructors, "Destructor method", lsp::protocol::CompletionItemKind::kMethod }, + { "operator", KeywordCategory::kConstructors, "Operator overload", lsp::protocol::CompletionItemKind::kOperator }, // Variable Modifiers - { "external", KeywordCategory::kVariableModifiers, "External declaration", lsp::CompletionItemKind::kKeyword }, - { "const", KeywordCategory::kVariableModifiers, "Constant declaration", lsp::CompletionItemKind::kConstant }, - { "out", KeywordCategory::kVariableModifiers, "Output parameter", lsp::CompletionItemKind::kKeyword }, - { "var", KeywordCategory::kVariableModifiers, "Variable declaration", lsp::CompletionItemKind::kVariable }, - { "global", KeywordCategory::kVariableModifiers, "Global variable", lsp::CompletionItemKind::kVariable }, - { "static", KeywordCategory::kVariableModifiers, "Static variable", lsp::CompletionItemKind::kVariable }, + { "external", KeywordCategory::kVariableModifiers, "External declaration", lsp::protocol::CompletionItemKind::kKeyword }, + { "const", KeywordCategory::kVariableModifiers, "Constant declaration", lsp::protocol::CompletionItemKind::kConstant }, + { "out", KeywordCategory::kVariableModifiers, "Output parameter", lsp::protocol::CompletionItemKind::kKeyword }, + { "var", KeywordCategory::kVariableModifiers, "Variable declaration", lsp::protocol::CompletionItemKind::kVariable }, + { "global", KeywordCategory::kVariableModifiers, "Global variable", lsp::protocol::CompletionItemKind::kVariable }, + { "static", KeywordCategory::kVariableModifiers, "Static variable", lsp::protocol::CompletionItemKind::kVariable }, // Conditionals - { "if", KeywordCategory::kConditionals, "If statement", lsp::CompletionItemKind::kKeyword }, - { "else", KeywordCategory::kConditionals, "Else clause", lsp::CompletionItemKind::kKeyword }, - { "then", KeywordCategory::kConditionals, "Then clause", lsp::CompletionItemKind::kKeyword }, - { "case", KeywordCategory::kConditionals, "Case statement", lsp::CompletionItemKind::kKeyword }, - { "of", KeywordCategory::kConditionals, "Of clause", lsp::CompletionItemKind::kKeyword }, + { "if", KeywordCategory::kConditionals, "If statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "else", KeywordCategory::kConditionals, "Else clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "then", KeywordCategory::kConditionals, "Then clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "case", KeywordCategory::kConditionals, "Case statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "of", KeywordCategory::kConditionals, "Of clause", lsp::protocol::CompletionItemKind::kKeyword }, // Loops - { "for", KeywordCategory::kLoops, "For loop", lsp::CompletionItemKind::kKeyword }, - { "while", KeywordCategory::kLoops, "While loop", lsp::CompletionItemKind::kKeyword }, - { "do", KeywordCategory::kLoops, "Do clause", lsp::CompletionItemKind::kKeyword }, - { "downto", KeywordCategory::kLoops, "Downto clause", lsp::CompletionItemKind::kKeyword }, - { "step", KeywordCategory::kLoops, "Step clause", lsp::CompletionItemKind::kKeyword }, - { "until", KeywordCategory::kLoops, "Until clause", lsp::CompletionItemKind::kKeyword }, - { "repeat", KeywordCategory::kLoops, "Repeat loop", lsp::CompletionItemKind::kKeyword }, - { "to", KeywordCategory::kLoops, "To clause", lsp::CompletionItemKind::kKeyword }, + { "for", KeywordCategory::kLoops, "For loop", lsp::protocol::CompletionItemKind::kKeyword }, + { "while", KeywordCategory::kLoops, "While loop", lsp::protocol::CompletionItemKind::kKeyword }, + { "do", KeywordCategory::kLoops, "Do clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "downto", KeywordCategory::kLoops, "Downto clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "step", KeywordCategory::kLoops, "Step clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "until", KeywordCategory::kLoops, "Until clause", lsp::protocol::CompletionItemKind::kKeyword }, + { "repeat", KeywordCategory::kLoops, "Repeat loop", lsp::protocol::CompletionItemKind::kKeyword }, + { "to", KeywordCategory::kLoops, "To clause", lsp::protocol::CompletionItemKind::kKeyword }, // Branch Control - { "break", KeywordCategory::kBranchControl, "Break statement", lsp::CompletionItemKind::kKeyword }, - { "continue", KeywordCategory::kBranchControl, "Continue statement", lsp::CompletionItemKind::kKeyword }, - { "goto", KeywordCategory::kBranchControl, "Goto statement", lsp::CompletionItemKind::kKeyword }, - { "label", KeywordCategory::kBranchControl, "Label declaration", lsp::CompletionItemKind::kKeyword }, - { "exit", KeywordCategory::kBranchControl, "Exit statement", lsp::CompletionItemKind::kKeyword }, + { "break", KeywordCategory::kBranchControl, "Break statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "continue", KeywordCategory::kBranchControl, "Continue statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "goto", KeywordCategory::kBranchControl, "Goto statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "label", KeywordCategory::kBranchControl, "Label declaration", lsp::protocol::CompletionItemKind::kKeyword }, + { "exit", KeywordCategory::kBranchControl, "Exit statement", lsp::protocol::CompletionItemKind::kKeyword }, // Return Control - { "return", KeywordCategory::kReturnControl, "Return statement", lsp::CompletionItemKind::kKeyword }, - { "debugreturn", KeywordCategory::kReturnControl, "Debug return statement", lsp::CompletionItemKind::kKeyword }, - { "debugrunenv", KeywordCategory::kReturnControl, "Debug run environment", lsp::CompletionItemKind::kKeyword }, - { "debugrunenvdo", KeywordCategory::kReturnControl, "Debug run environment do", lsp::CompletionItemKind::kKeyword }, + { "return", KeywordCategory::kReturnControl, "Return statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "debugreturn", KeywordCategory::kReturnControl, "Debug return statement", lsp::protocol::CompletionItemKind::kKeyword }, + { "debugrunenv", KeywordCategory::kReturnControl, "Debug run environment", lsp::protocol::CompletionItemKind::kKeyword }, + { "debugrunenvdo", KeywordCategory::kReturnControl, "Debug run environment do", lsp::protocol::CompletionItemKind::kKeyword }, // Block Control - { "begin", KeywordCategory::kBlockControl, "Begin block", lsp::CompletionItemKind::kKeyword }, - { "end", KeywordCategory::kBlockControl, "End block", lsp::CompletionItemKind::kKeyword }, - { "with", KeywordCategory::kBlockControl, "With statement", lsp::CompletionItemKind::kKeyword }, + { "begin", KeywordCategory::kBlockControl, "Begin block", lsp::protocol::CompletionItemKind::kKeyword }, + { "end", KeywordCategory::kBlockControl, "End block", lsp::protocol::CompletionItemKind::kKeyword }, + { "with", KeywordCategory::kBlockControl, "With statement", lsp::protocol::CompletionItemKind::kKeyword }, // References - { "weakref", KeywordCategory::kReferences, "Weak reference", lsp::CompletionItemKind::kKeyword }, - { "autoref", KeywordCategory::kReferences, "Auto reference", lsp::CompletionItemKind::kKeyword }, + { "weakref", KeywordCategory::kReferences, "Weak reference", lsp::protocol::CompletionItemKind::kKeyword }, + { "autoref", KeywordCategory::kReferences, "Auto reference", lsp::protocol::CompletionItemKind::kKeyword }, // Namespace - { "namespace", KeywordCategory::kNamespace, "Namespace declaration", lsp::CompletionItemKind::kModule }, + { "namespace", KeywordCategory::kNamespace, "Namespace declaration", lsp::protocol::CompletionItemKind::kModule }, // Exceptions - { "except", KeywordCategory::kExceptions, "Exception handling", lsp::CompletionItemKind::kKeyword }, - { "raise", KeywordCategory::kExceptions, "Raise exception", lsp::CompletionItemKind::kKeyword }, - { "try", KeywordCategory::kExceptions, "Try block", lsp::CompletionItemKind::kKeyword }, - { "finally", KeywordCategory::kExceptions, "Finally block", lsp::CompletionItemKind::kKeyword }, - { "exceptobject", KeywordCategory::kExceptions, "Exception object", lsp::CompletionItemKind::kKeyword }, + { "except", KeywordCategory::kExceptions, "Exception handling", lsp::protocol::CompletionItemKind::kKeyword }, + { "raise", KeywordCategory::kExceptions, "Raise exception", lsp::protocol::CompletionItemKind::kKeyword }, + { "try", KeywordCategory::kExceptions, "Try block", lsp::protocol::CompletionItemKind::kKeyword }, + { "finally", KeywordCategory::kExceptions, "Finally block", lsp::protocol::CompletionItemKind::kKeyword }, + { "exceptobject", KeywordCategory::kExceptions, "Exception object", lsp::protocol::CompletionItemKind::kKeyword }, // Logical Operators - { "and", KeywordCategory::kLogicalOperators, "Logical AND", lsp::CompletionItemKind::kOperator }, - { "in", KeywordCategory::kLogicalOperators, "In operator", lsp::CompletionItemKind::kOperator }, - { "is", KeywordCategory::kLogicalOperators, "Is operator", lsp::CompletionItemKind::kOperator }, - { "not", KeywordCategory::kLogicalOperators, "Logical NOT", lsp::CompletionItemKind::kOperator }, - { "or", KeywordCategory::kLogicalOperators, "Logical OR", lsp::CompletionItemKind::kOperator }, + { "and", KeywordCategory::kLogicalOperators, "Logical AND", lsp::protocol::CompletionItemKind::kOperator }, + { "in", KeywordCategory::kLogicalOperators, "In operator", lsp::protocol::CompletionItemKind::kOperator }, + { "is", KeywordCategory::kLogicalOperators, "Is operator", lsp::protocol::CompletionItemKind::kOperator }, + { "not", KeywordCategory::kLogicalOperators, "Logical NOT", lsp::protocol::CompletionItemKind::kOperator }, + { "or", KeywordCategory::kLogicalOperators, "Logical OR", lsp::protocol::CompletionItemKind::kOperator }, // Arithmetic Operators - { "div", KeywordCategory::kArithmeticOperators, "Integer division", lsp::CompletionItemKind::kOperator }, - { "mod", KeywordCategory::kArithmeticOperators, "Modulo operation", lsp::CompletionItemKind::kOperator }, + { "div", KeywordCategory::kArithmeticOperators, "Integer division", lsp::protocol::CompletionItemKind::kOperator }, + { "mod", KeywordCategory::kArithmeticOperators, "Modulo operation", lsp::protocol::CompletionItemKind::kOperator }, // Bitwise Operators - { "ror", KeywordCategory::kBitwiseOperators, "Rotate right", lsp::CompletionItemKind::kOperator }, - { "rol", KeywordCategory::kBitwiseOperators, "Rotate left", lsp::CompletionItemKind::kOperator }, - { "shr", KeywordCategory::kBitwiseOperators, "Shift right", lsp::CompletionItemKind::kOperator }, - { "shl", KeywordCategory::kBitwiseOperators, "Shift left", lsp::CompletionItemKind::kOperator }, + { "ror", KeywordCategory::kBitwiseOperators, "Rotate right", lsp::protocol::CompletionItemKind::kOperator }, + { "rol", KeywordCategory::kBitwiseOperators, "Rotate left", lsp::protocol::CompletionItemKind::kOperator }, + { "shr", KeywordCategory::kBitwiseOperators, "Shift right", lsp::protocol::CompletionItemKind::kOperator }, + { "shl", KeywordCategory::kBitwiseOperators, "Shift left", lsp::protocol::CompletionItemKind::kOperator }, // Set Operators - { "union", KeywordCategory::kSetOperators, "Set union", lsp::CompletionItemKind::kOperator }, - { "minus", KeywordCategory::kSetOperators, "Set difference", lsp::CompletionItemKind::kOperator }, - { "union2", KeywordCategory::kSetOperators, "Set union (alternative)", lsp::CompletionItemKind::kOperator }, + { "union", KeywordCategory::kSetOperators, "Set union", lsp::protocol::CompletionItemKind::kOperator }, + { "minus", KeywordCategory::kSetOperators, "Set difference", lsp::protocol::CompletionItemKind::kOperator }, + { "union2", KeywordCategory::kSetOperators, "Set union (alternative)", lsp::protocol::CompletionItemKind::kOperator }, // SQL Control - { "select", KeywordCategory::kSqlControl, "SQL SELECT", lsp::CompletionItemKind::kKeyword }, - { "vselect", KeywordCategory::kSqlControl, "Virtual SELECT", lsp::CompletionItemKind::kKeyword }, - { "sselect", KeywordCategory::kSqlControl, "Special SELECT", lsp::CompletionItemKind::kKeyword }, - { "update", KeywordCategory::kSqlControl, "SQL UPDATE", lsp::CompletionItemKind::kKeyword }, - { "delete", KeywordCategory::kSqlControl, "SQL DELETE", lsp::CompletionItemKind::kKeyword }, - { "mselect", KeywordCategory::kSqlControl, "Multiple SELECT", lsp::CompletionItemKind::kKeyword }, + { "select", KeywordCategory::kSqlControl, "SQL SELECT", lsp::protocol::CompletionItemKind::kKeyword }, + { "vselect", KeywordCategory::kSqlControl, "Virtual SELECT", lsp::protocol::CompletionItemKind::kKeyword }, + { "sselect", KeywordCategory::kSqlControl, "Special SELECT", lsp::protocol::CompletionItemKind::kKeyword }, + { "update", KeywordCategory::kSqlControl, "SQL UPDATE", lsp::protocol::CompletionItemKind::kKeyword }, + { "delete", KeywordCategory::kSqlControl, "SQL DELETE", lsp::protocol::CompletionItemKind::kKeyword }, + { "mselect", KeywordCategory::kSqlControl, "Multiple SELECT", lsp::protocol::CompletionItemKind::kKeyword }, // SQL Keywords - { "sqlin", KeywordCategory::kSqlKeywords, "SQL IN", lsp::CompletionItemKind::kKeyword }, - { "from", KeywordCategory::kSqlKeywords, "SQL FROM", lsp::CompletionItemKind::kKeyword }, - { "where", KeywordCategory::kSqlKeywords, "SQL WHERE", lsp::CompletionItemKind::kKeyword }, - { "group", KeywordCategory::kSqlKeywords, "SQL GROUP", lsp::CompletionItemKind::kKeyword }, - { "by", KeywordCategory::kSqlKeywords, "SQL BY", lsp::CompletionItemKind::kKeyword }, - { "order", KeywordCategory::kSqlKeywords, "SQL ORDER", lsp::CompletionItemKind::kKeyword }, - { "distinct", KeywordCategory::kSqlKeywords, "SQL DISTINCT", lsp::CompletionItemKind::kKeyword }, - { "join", KeywordCategory::kSqlKeywords, "SQL JOIN", lsp::CompletionItemKind::kKeyword }, + { "sqlin", KeywordCategory::kSqlKeywords, "SQL IN", lsp::protocol::CompletionItemKind::kKeyword }, + { "from", KeywordCategory::kSqlKeywords, "SQL FROM", lsp::protocol::CompletionItemKind::kKeyword }, + { "where", KeywordCategory::kSqlKeywords, "SQL WHERE", lsp::protocol::CompletionItemKind::kKeyword }, + { "group", KeywordCategory::kSqlKeywords, "SQL GROUP", lsp::protocol::CompletionItemKind::kKeyword }, + { "by", KeywordCategory::kSqlKeywords, "SQL BY", lsp::protocol::CompletionItemKind::kKeyword }, + { "order", KeywordCategory::kSqlKeywords, "SQL ORDER", lsp::protocol::CompletionItemKind::kKeyword }, + { "distinct", KeywordCategory::kSqlKeywords, "SQL DISTINCT", lsp::protocol::CompletionItemKind::kKeyword }, + { "join", KeywordCategory::kSqlKeywords, "SQL JOIN", lsp::protocol::CompletionItemKind::kKeyword }, // SQL Operators (note: some overlap with logical operators) - { "on", KeywordCategory::kSqlOperators, "SQL ON", lsp::CompletionItemKind::kOperator }, - { "like", KeywordCategory::kSqlOperators, "SQL LIKE", lsp::CompletionItemKind::kOperator }, + { "on", KeywordCategory::kSqlOperators, "SQL ON", lsp::protocol::CompletionItemKind::kOperator }, + { "like", KeywordCategory::kSqlOperators, "SQL LIKE", lsp::protocol::CompletionItemKind::kOperator }, // Calling Conventions - { "cdecl", KeywordCategory::kCallingConventions, "C calling convention", lsp::CompletionItemKind::kKeyword }, - { "pascal", KeywordCategory::kCallingConventions, "Pascal calling convention", lsp::CompletionItemKind::kKeyword }, - { "stdcall", KeywordCategory::kCallingConventions, "Standard calling convention", lsp::CompletionItemKind::kKeyword }, - { "safecall", KeywordCategory::kCallingConventions, "Safe calling convention", lsp::CompletionItemKind::kKeyword }, - { "fastcall", KeywordCategory::kCallingConventions, "Fast calling convention", lsp::CompletionItemKind::kKeyword }, - { "register", KeywordCategory::kCallingConventions, "Register calling convention", lsp::CompletionItemKind::kKeyword }, + { "cdecl", KeywordCategory::kCallingConventions, "C calling convention", lsp::protocol::CompletionItemKind::kKeyword }, + { "pascal", KeywordCategory::kCallingConventions, "Pascal calling convention", lsp::protocol::CompletionItemKind::kKeyword }, + { "stdcall", KeywordCategory::kCallingConventions, "Standard calling convention", lsp::protocol::CompletionItemKind::kKeyword }, + { "safecall", KeywordCategory::kCallingConventions, "Safe calling convention", lsp::protocol::CompletionItemKind::kKeyword }, + { "fastcall", KeywordCategory::kCallingConventions, "Fast calling convention", lsp::protocol::CompletionItemKind::kKeyword }, + { "register", KeywordCategory::kCallingConventions, "Register calling convention", lsp::protocol::CompletionItemKind::kKeyword }, // System Keywords - { "setuid", KeywordCategory::kSystemKeywords, "Set user ID", lsp::CompletionItemKind::kKeyword }, - { "sudo", KeywordCategory::kSystemKeywords, "Super user do", lsp::CompletionItemKind::kKeyword }, + { "setuid", KeywordCategory::kSystemKeywords, "Set user ID", lsp::protocol::CompletionItemKind::kKeyword }, + { "sudo", KeywordCategory::kSystemKeywords, "Super user do", lsp::protocol::CompletionItemKind::kKeyword }, // Builtin Variables - { "paramcount", KeywordCategory::kBuiltinVariables, "Parameter count", lsp::CompletionItemKind::kVariable }, - { "realparamcount", KeywordCategory::kBuiltinVariables, "Real parameter count", lsp::CompletionItemKind::kVariable }, - { "params", KeywordCategory::kBuiltinVariables, "Parameters array", lsp::CompletionItemKind::kVariable }, - { "system", KeywordCategory::kBuiltinVariables, "System variable", lsp::CompletionItemKind::kVariable }, - { "tslassigning", KeywordCategory::kBuiltinVariables, "TSL assigning", lsp::CompletionItemKind::kVariable }, - { "likeeps", KeywordCategory::kBuiltinVariables, "Like EPS", lsp::CompletionItemKind::kVariable }, - { "likeepsrate", KeywordCategory::kBuiltinVariables, "Like EPS rate", lsp::CompletionItemKind::kVariable }, + { "paramcount", KeywordCategory::kBuiltinVariables, "Parameter count", lsp::protocol::CompletionItemKind::kVariable }, + { "realparamcount", KeywordCategory::kBuiltinVariables, "Real parameter count", lsp::protocol::CompletionItemKind::kVariable }, + { "params", KeywordCategory::kBuiltinVariables, "Parameters array", lsp::protocol::CompletionItemKind::kVariable }, + { "system", KeywordCategory::kBuiltinVariables, "System variable", lsp::protocol::CompletionItemKind::kVariable }, + { "tslassigning", KeywordCategory::kBuiltinVariables, "TSL assigning", lsp::protocol::CompletionItemKind::kVariable }, + { "likeeps", KeywordCategory::kBuiltinVariables, "Like EPS", lsp::protocol::CompletionItemKind::kVariable }, + { "likeepsrate", KeywordCategory::kBuiltinVariables, "Like EPS rate", lsp::protocol::CompletionItemKind::kVariable }, // Builtin Functions - { "echo", KeywordCategory::kBuiltinFunctions, "Echo function", lsp::CompletionItemKind::kFunction }, - { "mtic", KeywordCategory::kBuiltinFunctions, "MTIC function", lsp::CompletionItemKind::kFunction }, - { "mtoc", KeywordCategory::kBuiltinFunctions, "MTOC function", lsp::CompletionItemKind::kFunction }, + { "echo", KeywordCategory::kBuiltinFunctions, "Echo function", lsp::protocol::CompletionItemKind::kFunction }, + { "mtic", KeywordCategory::kBuiltinFunctions, "MTIC function", lsp::protocol::CompletionItemKind::kFunction }, + { "mtoc", KeywordCategory::kBuiltinFunctions, "MTOC function", lsp::protocol::CompletionItemKind::kFunction }, // Boolean Constants - { "false", KeywordCategory::kBooleanConstants, "Boolean false", lsp::CompletionItemKind::kConstant }, - { "true", KeywordCategory::kBooleanConstants, "Boolean true", lsp::CompletionItemKind::kConstant }, + { "false", KeywordCategory::kBooleanConstants, "Boolean false", lsp::protocol::CompletionItemKind::kConstant }, + { "true", KeywordCategory::kBooleanConstants, "Boolean true", lsp::protocol::CompletionItemKind::kConstant }, // Math Constants - { "inf", KeywordCategory::kMathConstants, "Infinity", lsp::CompletionItemKind::kConstant }, - { "nan", KeywordCategory::kMathConstants, "Not a Number", lsp::CompletionItemKind::kConstant }, + { "inf", KeywordCategory::kMathConstants, "Infinity", lsp::protocol::CompletionItemKind::kConstant }, + { "nan", KeywordCategory::kMathConstants, "Not a Number", lsp::protocol::CompletionItemKind::kConstant }, // Null Constants - { "nil", KeywordCategory::kNullConstants, "Null value", lsp::CompletionItemKind::kConstant } + { "nil", KeywordCategory::kNullConstants, "Null value", lsp::protocol::CompletionItemKind::kConstant } }; keyword_set_.clear(); for (const auto& keyword : keywords_) diff --git a/lsp-server/src/language/tsl_keywords.hpp b/lsp-server/src/language/tsl_keywords.hpp index 3d6a135..e0d6bda 100644 --- a/lsp-server/src/language/tsl_keywords.hpp +++ b/lsp-server/src/language/tsl_keywords.hpp @@ -2,7 +2,7 @@ #include #include #include -#include "../lsp/lsp_types.hpp" +#include "../protocol/protocol.hpp" namespace tsl { @@ -46,7 +46,7 @@ namespace tsl std::string keyword; KeywordCategory category; std::string description; - lsp::CompletionItemKind completion_kind; + lsp::protocol::CompletionItemKind completion_kind; }; class TslKeywords @@ -54,7 +54,7 @@ namespace tsl public: static const std::vector& GetAllKeywords(); static std::vector GetKeywordsByCategory(KeywordCategory category); - static std::vector GetCompletionItems(const std::string& prefix = ""); + static std::vector GetCompletionItems(const std::string& prefix = ""); static bool IsKeyword(const std::string& word); static KeywordCategory GetKeywordCategory(const std::string& word); diff --git a/lsp-server/src/lsp/dispacther.cpp b/lsp-server/src/lsp/dispacther.cpp index 08f9ffe..dab008c 100644 --- a/lsp-server/src/lsp/dispacther.cpp +++ b/lsp-server/src/lsp/dispacther.cpp @@ -1,22 +1,32 @@ +#include +#include #include "./dispacther.hpp" +#include "../protocol/transform/facade.hpp" -namespace lsp { - - +namespace lsp +{ void RequestDispatcher::RegisterProvider(const std::string& method, RequestProvider handler) { + std::unique_lock lock(providers_mutex_); providers_[method] = std::move(handler); + spdlog::debug("Registered provider for method: {}", method); } - nlohmann::json RequestDispatcher::Dispatch(const LspRequest& request) + std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request) { + std::shared_lock lock(providers_mutex_); auto it = providers_.find(request.method); - if (it != providers_.end()) { + if (it != providers_.end()) + { + RequestProvider provider = it->second; + lock.unlock(); try { - return it->second(request); - } catch (const std::exception& e) + return provider(request); + } + catch (const std::exception& e) { + spdlog::error("Provider error for method {}: {}", request.method, e.what()); return HandleException(request, e.what()); } } @@ -25,39 +35,52 @@ namespace lsp { bool RequestDispatcher::SupportsMethod(const std::string& method) const { + std::shared_lock lock(providers_mutex_); return providers_.find(method) != providers_.end(); } std::vector RequestDispatcher::GetSupportedMethods() const { + std::shared_lock lock(providers_mutex_); std::vector methods; - for (const auto& pair : providers_) { - methods.push_back(pair.first); + methods.reserve(providers_.size()); + for (const auto& [method, _] : providers_) + { + methods.push_back(method); } return methods; } - nlohmann::json RequestDispatcher::HandleUnknownMethod(const LspRequest& request) + std::string RequestDispatcher::HandleUnknownMethod(const protocol::RequestMessage& request) { - nlohmann::json resp; - resp["jsonrpc"] = "2.0"; - resp["id"] = request.id; - resp["error"] = { - {"code", -32601}, - {"message", "Method not found: " + request.method} - }; - return resp; + return BuildErrorResponse(request, protocol::ErrorCode::kMethodNotFound, "Method not found: " + request.method); } - nlohmann::json RequestDispatcher::HandleException(const LspRequest& request, const std::string& error_message) + std::string RequestDispatcher::HandleException(const protocol::RequestMessage& request, const std::string& error_message) { - nlohmann::json resp; - resp["jsonrpc"] = "2.0"; - resp["id"] = request.id; - resp["error"] = { - {"code", -32603}, // Internal error - {"message", "Internal error: " + error_message} - }; - return resp; + return BuildErrorResponse(request, protocol::ErrorCode::kInternalError, "Internal error: " + error_message); + } + + std::string RequestDispatcher::BuildErrorResponse(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message) + { + protocol::ResponseMessage response; + response.jsonrpc = "2.0"; + response.id = request.id; + + protocol::ResponseError error; + error.code = code; + error.message = message; + response.error = error; + + std::string json; + auto ec = glz::write_json(response, json); + if (ec) + { + spdlog::error("Failed to serialize error response: {}", glz::format_error(ec, json)); + // 返回一个硬编码的错误响应作为后备 + return R"({"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":"Failed to serialize error response"}})"; + } + + return json; } } diff --git a/lsp-server/src/lsp/dispacther.hpp b/lsp-server/src/lsp/dispacther.hpp index c9bff29..85de247 100644 --- a/lsp-server/src/lsp/dispacther.hpp +++ b/lsp-server/src/lsp/dispacther.hpp @@ -1,27 +1,32 @@ #pragma once -#include "./lsp_types.hpp" +#include +#include +#include +#include +#include "../protocol/protocol.hpp" namespace lsp { - - // 请求处理函数类型 - using RequestProvider = std::function; + // 请求处理函数类型 - 返回序列化后的 JSON 字符串 + using RequestProvider = std::function; class RequestDispatcher { public: RequestDispatcher() = default; - void RegisterProvider(const std::string& method, RequestProvider provider); // 可选:重命名 - nlohmann::json Dispatch(const LspRequest& request); + + void RegisterProvider(const std::string& method, RequestProvider provider); + std::string Dispatch(const protocol::RequestMessage& request); bool SupportsMethod(const std::string& method) const; std::vector GetSupportedMethods() const; private: - nlohmann::json HandleUnknownMethod(const LspRequest& request); - nlohmann::json HandleException(const LspRequest& request, const std::string& error_message); + std::string HandleUnknownMethod(const protocol::RequestMessage& request); + std::string HandleException(const protocol::RequestMessage& request, const std::string& error_message); + std::string BuildErrorResponse(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message); private: + mutable std::shared_mutex providers_mutex_; std::unordered_map providers_; }; - } diff --git a/lsp-server/src/lsp/lsp_types.hpp b/lsp-server/src/lsp/lsp_types.hpp deleted file mode 100644 index 3f548a9..0000000 --- a/lsp-server/src/lsp/lsp_types.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#include -#include - -namespace lsp -{ - // LSP请求结构 - struct LspRequest - { - std::string jsonrpc; - std::string method; - nlohmann::json params; - nlohmann::json id; - - LspRequest(const nlohmann::json& json) : - jsonrpc(json.value("jsonrpc", "2.0")), - method(json.value("method", "")), - params(json.value("params", nlohmann::json::object())), - id(json.value("id", nlohmann::json::array())) {} - }; - - // 补全项类型枚举 - enum class CompletionItemKind - { - kText = 1, - kMethod = 2, - kFunction = 3, - kConstructor = 4, - kField = 5, - kVariable = 6, - kClass = 7, - kInterface = 8, - kModule = 9, - kProperty = 10, - kUnit = 11, - kValue = 12, - kEnum = 13, - kKeyword = 14, - kSnippet = 15, - kColor = 16, - kFile = 17, - kReference = 18, - kFolder = 19, - kEnumMember = 20, - kConstant = 21, - kStruct = 22, - kEvent = 23, - kOperator = 24, - kTypeParameter = 25 - }; - - // 补全项目 - struct CompletionItem - { - std::string label; - CompletionItemKind kind; - std::optional detail; - std::optional documentation; - std::optional insert_text; - }; - -} diff --git a/lsp-server/src/lsp/request_scheduler.cpp b/lsp-server/src/lsp/request_scheduler.cpp new file mode 100644 index 0000000..07a54c6 --- /dev/null +++ b/lsp-server/src/lsp/request_scheduler.cpp @@ -0,0 +1,56 @@ +#include +#include +#include "./request_scheduler.hpp" + +namespace lsp +{ + + RequestScheduler::RequestScheduler(size_t concurrency) : + executor_(concurrency) + { + spdlog::info("RequestScheduler initialized with {} threads", concurrency); + } + RequestScheduler::~RequestScheduler() = default; + + void RequestScheduler::Sumbit(const std::string& request_id, TaskFunc task) + { + std::lock_guard lock(task_mutex_); + auto future = executor_.silent_async([this, request_id, task = std::move(task)] { + try + { + auto result = task(); + if (result) + SendResponse(*result); + } + catch (const std::exception& e) + { + spdlog::error("Task {} failed: {}", request_id, e.what()); + } + std::lock_guard lock(cancel_mutex_); + running_tasks_.erase(request_id); + }); + running_tasks_[request_id] = std::move(future); + } + + void RequestScheduler::Cancel(const std::string& request_id) + { + std::lock_guard lock(cancel_mutex_); + // Taskflow doesn't support preemptive cancel yet; you can set a flag here for cooperative cancel + spdlog::warn("Cancel not implemented for request_id = {} (requires cooperative cancel)", request_id); + } + + void RequestScheduler::SetOutputFallback(std::function callback) + { + std::lock_guard lock(output_mutex_); + send_response_ = std::move(callback); + } + + void RequestScheduler::SendResponse(const std::string& response) + { + std::lock_guard lock(output_mutex_); + if (send_response_) + send_response_(response); + else + spdlog::error("No response callback set! Unable to send: {}", response); + } +} diff --git a/lsp-server/src/lsp/request_scheduler.hpp b/lsp-server/src/lsp/request_scheduler.hpp new file mode 100644 index 0000000..d3a88bc --- /dev/null +++ b/lsp-server/src/lsp/request_scheduler.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace lsp +{ + class RequestScheduler + { + public: + using TaskFunc = std::function()>; + explicit RequestScheduler(size_t concurrency); + ~RequestScheduler(); + + void Sumbit(const std::string& request_id, TaskFunc task); + void Cancel(const std::string& request_id); + void SetOutputFallback(std::function callback); + + private: + void SendResponse(const std::string& response); + + private: + tf::Executor executor_; + std::mutex task_mutex_; + std::mutex cancel_mutex_; + std::mutex output_mutex_; + + std::unordered_map> running_tasks_; + std::function send_response_; + }; +} diff --git a/lsp-server/src/lsp/server.cpp b/lsp-server/src/lsp/server.cpp index 78fe765..7cc3abd 100644 --- a/lsp-server/src/lsp/server.cpp +++ b/lsp-server/src/lsp/server.cpp @@ -6,6 +6,7 @@ #include #endif #include "../provider/base/provider_registry.hpp" +#include "../protocol/transform/facade.hpp" #include "./server.hpp" namespace lsp @@ -13,25 +14,41 @@ namespace lsp LspServer::LspServer() { providers::RegisterAllProviders(dispatcher_); - spdlog::debug("LSP server initialized with providers."); + spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetSupportedMethods().size()); } + LspServer::~LspServer() = default; + void LspServer::Run() { spdlog::info("LSP server starting main loop..."); + + // 设置二进制模式 +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stdin), _O_BINARY); +#endif + while (true) { try { std::optional message = ReadMessage(); - if (!message) continue; - nlohmann::json response = HandleRequest(*message); + if (!message) + { + spdlog::debug("No message received, continuing..."); + continue; + } + + std::string response = HandleMessage(*message); if (!response.empty()) + { SendResponse(response); + } } - catch(const std::exception& e) + catch (const std::exception& e) { - spdlog::error("Error processing message: {}", e.what()); + spdlog::error("Error in main loop: {}", e.what()); } } spdlog::info("LSP server main loop ended"); @@ -45,17 +62,20 @@ namespace lsp // 读取 LSP Header while (std::getline(std::cin, line)) { - spdlog::trace("Received header line: {}", line); - // 去掉尾部 \r if (!line.empty() && line.back() == '\r') + { line.pop_back(); + } + if (line.empty()) - break; - // 查找 Content-Length + { + break; // 空行表示 header 结束 + } + if (line.find("Content-Length:") == 0) { - std::string length_str = line.substr(15); // 跳过 "Content-Length:" + std::string length_str = line.substr(15); // 跳过 "Content-Length:" size_t start = length_str.find_first_not_of(" "); if (start != std::string::npos) { @@ -76,7 +96,7 @@ namespace lsp if (content_length == 0) { - spdlog::warn("No Content-Length found in header"); + spdlog::debug("No Content-Length found in header"); return std::nullopt; } @@ -84,61 +104,142 @@ namespace lsp std::string body(content_length, '\0'); std::cin.read(&body[0], content_length); - spdlog::trace("Message body: {}", body); - if (std::cin.gcount() != static_cast(content_length)) { - spdlog::error("Read incomplete message body, expected: {}, got: {}", content_length, std::cin.gcount()); + spdlog::error("Read incomplete message body, expected: {}, got: {}", + content_length, + std::cin.gcount()); return std::nullopt; } + + spdlog::trace("Received message: {}", body); return body; } - nlohmann::json LspServer::HandleRequest(const std::string& raw_request) + std::string LspServer::HandleMessage(const std::string& raw_message) { try { - nlohmann::json json = nlohmann::json::parse(raw_request); - LspRequest request(json); - spdlog::trace("Processing method: {}", request.method); + // 首先尝试解析为通用的 JSON 来判断消息类型 + glz::json_t doc; + auto error = glz::read_json(doc, raw_message); + if (error) + { + spdlog::error("Failed to parse message as JSON: {}", + glz::format_error(error, raw_message)); + return ""; + } - nlohmann::json response = dispatcher_.Dispatch(request); - return response; + auto& obj = doc.get(); + + // 判断消息类型 + bool has_id = obj.contains("id"); + bool has_method = obj.contains("method"); + bool has_result = obj.contains("result"); + bool has_error = obj.contains("error"); + + if (has_method && has_id) + { + // Request + protocol::RequestMessage request; + error = glz::read_json(request, raw_message); + if (error) + { + spdlog::error("Failed to parse request: {}", + glz::format_error(error, raw_message)); + return ""; + } + return HandleRequest(request); + } + else if (has_method && !has_id) + { + // Notification + protocol::NotificationMessage notification; + error = glz::read_json(notification, raw_message); + if (error) + { + spdlog::error("Failed to parse notification: {}", + glz::format_error(error, raw_message)); + return ""; + } + return HandleNotification(notification); + } + else if (has_id && (has_result || has_error)) + { + // Response + protocol::ResponseMessage response; + error = glz::read_json(response, raw_message); + if (error) + { + spdlog::error("Failed to parse response: {}", + glz::format_error(error, raw_message)); + return ""; + } + HandleResponse(response); + return ""; // 响应不需要回复 + } + else + { + spdlog::error("Unknown message type"); + return ""; + } } catch (const std::exception& e) { - spdlog::error("Failed to handle request: {}", e.what()); - return nlohmann::json(); + spdlog::error("Failed to handle message: {}", e.what()); + return ""; } } - void LspServer::SendResponse(const nlohmann::json& response) + std::string LspServer::HandleRequest(const protocol::RequestMessage& request) { - std::string response_str = response.dump(); - size_t byte_length = response_str.length(); + spdlog::debug("Processing request method: {}", request.method); - // 调试:显示实际发送的原始内容 - spdlog::debug("Response length: {}", byte_length); - spdlog::debug("Raw response content: [{}]", response_str); - - // 在程序启动时设置stdout为二进制模式(只需设置一次) - static bool binary_mode_set = false; - if (!binary_mode_set) + // 特殊处理 initialize 请求 + if (request.method == "initialize") { -#ifdef _WIN32 - _setmode(_fileno(stdout), _O_BINARY); -#endif - binary_mode_set = true; + is_initialized_ = true; } + return dispatcher_.Dispatch(request); + } + + std::string LspServer::HandleNotification(const protocol::NotificationMessage& notification) + { + spdlog::debug("Processing notification - method: {}", notification.method); + + // 特殊处理某些通知 + if (notification.method == "initialized") + { + spdlog::info("Client initialized"); + } + else if (notification.method == "exit") + { + spdlog::info("Exit notification received"); + std::exit(0); + } + + // 通知不需要响应 + return ""; + } + + void LspServer::HandleResponse(const protocol::ResponseMessage& response) + { + } + + void LspServer::SendResponse(const std::string& response) + { + size_t byte_length = response.length(); + // 构建完整消息 std::string header = "Content-Length: " + std::to_string(byte_length) + "\r\n\r\n"; - // 使用 write 系统调用,绕过 C++ 流的缓冲和转换 + // 发送 header 和 body std::cout.write(header.c_str(), header.length()); - std::cout.write(response_str.c_str(), response_str.length()); + std::cout.write(response.c_str(), response.length()); std::cout.flush(); - spdlog::trace("Response sent successfully"); + spdlog::trace("Response sent - length: {}", byte_length); + spdlog::trace("Response sent - body: {}", response); } } diff --git a/lsp-server/src/lsp/server.hpp b/lsp-server/src/lsp/server.hpp index 9d2941c..8fc8b79 100644 --- a/lsp-server/src/lsp/server.hpp +++ b/lsp-server/src/lsp/server.hpp @@ -5,23 +5,30 @@ namespace lsp { - class LspServer { public: LspServer(); - ~LspServer() = default; + ~LspServer(); void Run(); private: // 读取LSP消息 std::optional ReadMessage(); - // 处理LSP请求 - nlohmann::json HandleRequest(const std::string& raw_request); + + // 处理LSP请求 - 返回序列化的响应或空字符串(对于通知) + std::string HandleMessage(const std::string& raw_message); + // 发送LSP响应 - void SendResponse(const nlohmann::json& response); + void SendResponse(const std::string& response); + + // 处理不同类型的消息 + std::string HandleRequest(const protocol::RequestMessage& request); + std::string HandleNotification(const protocol::NotificationMessage& notification); + void HandleResponse(const protocol::ResponseMessage& response); private: RequestDispatcher dispatcher_; + bool is_initialized_ = false; }; } diff --git a/lsp-server/src/protocol/detail/basic_types.hpp b/lsp-server/src/protocol/detail/basic_types.hpp new file mode 100644 index 0000000..74e4372 --- /dev/null +++ b/lsp-server/src/protocol/detail/basic_types.hpp @@ -0,0 +1,244 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace lsp::protocol +{ + + using integer = std::int32_t; + using uinteger = std::uint32_t; + using decimal = double; + using boolean = bool; + using string = std::string; + + // LSPAny 这样设计是为了glaz序列化 + struct LSPAny; + using LSPObject = std::map; + using LSPArray = std::vector; + + struct LSPAny + { + using LSPAnyVariant = std::variant< + LSPObject, + LSPArray, + string, + decimal, // 放在前面,优先匹配数字 + boolean, + std::nullptr_t>; + + LSPAnyVariant value; + + // 默认构造函数 + LSPAny(); + // 拷贝和移动构造函数 + LSPAny(const LSPAny& other); + LSPAny(LSPAny&& other) noexcept; + // 针对每种支持的类型的构造函数 + LSPAny(const std::map& val); + LSPAny(std::map&& val); + LSPAny(const std::vector& val); + LSPAny(std::vector&& val); + LSPAny(const string& val); + LSPAny(string&& val); + LSPAny(const char* val); + + // 所有数字类型都转换为 decimal + LSPAny(int val); + LSPAny(long val); + LSPAny(long long val); + LSPAny(unsigned int val); + LSPAny(unsigned long val); + LSPAny(unsigned long long val); + LSPAny(float val); + LSPAny(double val); + LSPAny(long double val); + LSPAny(boolean val); + LSPAny(std::nullptr_t); + + // 赋值操作符 + LSPAny& operator=(const LSPAny& other); + LSPAny& operator=(LSPAny&& other) noexcept; + LSPAny& operator=(const std::map& val); + LSPAny& operator=(std::map&& val); + LSPAny& operator=(const std::vector& val); + LSPAny& operator=(std::vector&& val); + LSPAny& operator=(const string& val); + LSPAny& operator=(string&& val); + LSPAny& operator=(const char* val); + LSPAny& operator=(int val); + LSPAny& operator=(long val); + LSPAny& operator=(long long val); + LSPAny& operator=(unsigned int val); + LSPAny& operator=(unsigned long val); + LSPAny& operator=(unsigned long long val); + LSPAny& operator=(float val); + LSPAny& operator=(double val); + LSPAny& operator=(long double val); + LSPAny& operator=(boolean val); + LSPAny& operator=(std::nullptr_t); + + // 类型检查辅助函数 + template + bool is() const; + + template + T& get(); + + template + const T& get() const; + + // 访问操作符 + template + auto visit(Visitor&& visitor) const; + + template + auto visit(Visitor&& visitor); + }; + + using ProgressToken = std::variant; + + template + struct ProgressParams + { + ProgressToken token; + T value; + }; + + using DocumentUri = string; + using URI = string; + + struct Position + { + uinteger line; + uinteger character; + }; + + using PositionEncodingKind = std::string_view; + namespace PositionEncodingKindLiterals + { + inline constexpr PositionEncodingKind UTF8 = "utf-8"; + inline constexpr PositionEncodingKind UTF16 = "utf-16"; + inline constexpr PositionEncodingKind UTF32 = "utf-32"; + } + + struct Range + { + Position start; + Position end; + }; + + struct DocumentFilter + { + string language; + string scheme; + std::optional pattern; + }; + + using DocumentSelector = std::vector; + + struct TextEdit + { + Range range; + string newText; + }; + + struct ChangeAnnotation + { + string label; + boolean needsConfirmation; + string description; + }; + + using ChangeAnnotationIdentifier = string; + + struct AnnotatedTextEdit : TextEdit + { + ChangeAnnotationIdentifier annotationId; + }; + + struct Location + { + DocumentUri uri; + Range range; + }; + + struct LocationLink + { + Range originSelectionRange; + DocumentUri targetUri; + Range targetRange; + Range targetSelectionRange; + }; + + struct Command + { + string title; + string command; + std::optional arguments; + }; + + using MarkupKind = std::string_view; + namespace MarkupKindLiterals + { + inline constexpr MarkupKind PlainText = "plaintext"; + inline constexpr MarkupKind Markdown = "markdown"; + }; + + struct MarkupContent + { + MarkupKind kind; + string value; + }; + + struct RegularExpressionsClientCapabilities + { + string engine; + std::optional version; + }; + + struct MarkdownClientCapabilities + { + string parser; + std::optional version; + std::optional> allowedTags; + }; + + // LSP模板方法 + template + bool LSPAny::is() const + { + return std::holds_alternative(value); + } + + template + T& LSPAny::get() + { + return std::get(value); + } + + template + const T& LSPAny::get() const + { + return std::get(value); + } + + // 访问操作符 + template + auto LSPAny::visit(Visitor&& visitor) const + { + return std::visit(std::forward(visitor), value); + } + + template + auto LSPAny::visit(Visitor&& visitor) + { + return std::visit(std::forward(visitor), value); + } + +} + +#include "./lsp_any.inl" diff --git a/lsp-server/src/protocol/detail/capabilities.hpp b/lsp-server/src/protocol/detail/capabilities.hpp new file mode 100644 index 0000000..3d10fad --- /dev/null +++ b/lsp-server/src/protocol/detail/capabilities.hpp @@ -0,0 +1,212 @@ +#pragma once +#include "./basic_types.hpp" +#include "./progress.hpp" +#include "./diagnostics.hpp" +#include "./workspace.hpp" +#include "./document_sync.hpp" +#include "./completion.hpp" +#include "./document_features.hpp" +#include "./symbols.hpp" +#include "./navigation.hpp" +#include "./code_actions.hpp" +#include "./formatting.hpp" +#include "./rename.hpp" +#include "./semantic_tokens.hpp" +#include "./inline_features.hpp" +#include "./signature_help.hpp" +#include "./notebook.hpp" +#include "./file_operations.hpp" +#include "./configuration.hpp" + +namespace lsp::protocol +{ + struct TextDocumentClientCapabilities + { + std::optional synchronization; + std::optional completion; + std::optional hover; + std::optional singatureHelp; + std::optional declatration; + std::optional definition; + std::optional typeDefinition; + std::optional implementation; + std::optional reference; + std::optional documentHighlight; + std::optional documentSymbol; + std::optional codeAction; + std::optional codeLens; + std::optional documentLink; + std::optional colorProvider; + std::optional formatting; + std::optional rangeFormatting; + std::optional onTypeFormatting; + std::optional rename; + std::optional publishDiagnostics; + std::optional foldingRange; + std::optional selectionRange; + std::optional linkedEditingRange; + std::optional callHierarchy; + std::optional semanticTokens; + std::optional moniker; + std::optional typeHierarchy; + std::optional inlineValue; + std::optional inlayHint; + std::optional diagnostic; + }; + + struct ClientCapabilities + { + struct Workspace + { + struct FileOperations + { + std::optional dynamicRegistration; + std::optional didCreate; + std::optional willCreate; + std::optional didRename; + std::optional willRename; + std::optional didDelete; + std::optional willDelete; + }; + + std::optional applyEdit; + std::optional workspaceEdit; + std::optional didChangeConfiguration; + std::optional didChangeWatchedFiles; + std::optional symbol; + std::optional executeCommand; + std::optional workspaceFolders; + std::optional configuration; + std::optional semanticTokens; + std::optional codeLens; + std::optional fileOperations; + std::optional inlineValue; + std::optional inlayHint; + std::optional diagnostic; + }; + struct Window + { + std::optional workDoneProgress; + std::optional showMessage; + std::optional showDocument; + }; + struct General + { + struct StaleRequestSupport + { + boolean cancel; + std::vector retryOnContentModified; + }; + + std::optional staleRequestSupport; + std::optional regularExpression; + std::optional markdown; + std::optional> positionEncodings; + }; + + std::optional workspace; + std::optional textDocument; + std::optional notebookDocument; + std::optional window; + std::optional general; + std::optional experimental; + }; + + struct ServerCapabilities + { + struct Workspace + { + struct FileOperations + { + std::optional dynamicRegistration; + std::optional didCreate; + std::optional willCreate; + std::optional didRename; + std::optional willRename; + std::optional didDelete; + std::optional willDelete; + }; + + std::optional workspaceFolders; + std::optional fileOperations; + }; + + std::optional positionEncoding; + std::optional> textDocumentSync; + std::optional> notebookDocumentSync; + std::optional completionProvider; + std::optional> hoverProvider; + std::optional signatureHelpProvider; + std::optional> declarationProvider; + std::optional> definitionProvider; + std::optional> typeDefinitionProvider; + std::optional> implementationProvider; + std::optional> referencesProvider; + std::optional> documentHighlightProvider; + std::optional> documentSymbolProvider; + std::optional> codeActionProvider; + std::optional codeLensProvider; + std::optional documentLinkProvider; + std::optional> colorProvider; + std::optional> documentFormattingProvider; + std::optional> documentRangeFormattingProvider; + std::optional documentOnTypeFormattingProvider; + std::optional> renameProvider; + std::optional> foldingRangeProvider; + std::optional executeCommandProvider; + std::optional> selectionRangeProvider; + std::optional> linkedEditingRangeProvider; + std::optional> callHierarchyProvider; + std::optional> semanticTokensProvider; + std::optional> monikerProvider; + std::optional> typeHierarchyProvider; + std::optional> inlineValueProvider; + std::optional> inlayHintProvider; + std::optional> diagnosticProvider; + std::optional> workspaceSymbolProvider; + std::optional workspace; + std::optional experimental; + }; + + struct InitializeResult + { + struct ServerInfo + { + string name; + std::optional version; + }; + + ServerCapabilities capabilities; + ServerInfo serverInfo; + }; + + enum class InitializeErrorCodes + { + kUnknownProtocolVersion = 1 + }; + + struct InitializeError + { + boolean retry; + }; + + struct InitializeParams : WorkDoneProgressParams + { + struct ClientInfo + { + string name; + std::optional version; + }; + + std::optional processId; + std::optional clientInfo; + std::optional locale; + std::optional rootPath; + std::optional rootUri; + std::optional initializationOptions; + std::optional capabilities; + TraceValue trace; + std::optional> workspaceFolders; + }; + +} diff --git a/lsp-server/src/protocol/detail/code_actions.hpp b/lsp-server/src/protocol/detail/code_actions.hpp new file mode 100644 index 0000000..a50465a --- /dev/null +++ b/lsp-server/src/protocol/detail/code_actions.hpp @@ -0,0 +1,141 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" +#include "./workspace.hpp" + +namespace lsp::protocol +{ + using CodeActionKind = std::string_view; + namespace CodeActionKindLiterals + { + inline constexpr CodeActionKind Empty = ""; + inline constexpr CodeActionKind QuickFix = "quickfix"; + inline constexpr CodeActionKind Refactor = "refactor"; + inline constexpr CodeActionKind RefactorExtract = "refactor.extract"; + inline constexpr CodeActionKind RefactorInline = "refactor.inline"; + inline constexpr CodeActionKind RefactorRewrite = "refactor.rewrite"; + inline constexpr CodeActionKind Source = "source"; + inline constexpr CodeActionKind SourceOrganizeImports = "source.organizeImports"; + inline constexpr CodeActionKind SourceFixAll = "source.fixAll"; + } + + struct CodeActionClientCapabilities + { + struct CodeActionLiteralSupport + { + struct CodeActionKinds + { + std::vector valueSet; + }; + }; + + struct ResolveSupport + { + std::vector properties; + }; + + std::optional dynamicRegistration; + std::optional codeActionLiteralSupport; + std::optional isPreferredSupport; + std::optional disabledSupport; + std::optional resolveSupport; + std::optional honorsChangeAnnotations; + }; + + struct CodeActionOptions : WorkDoneProgressOptions + { + std::optional> codeActionKinds; + std::optional resolveProvider; + }; + + struct CodeActionRegistrationOptions : TextDocumentRegistrationOptions, CodeActionOptions + { + }; + + enum class CodeActionTriggerKind + { + kInvoked = 1, + kAutomatic = 2 + }; + + struct CodeActionContext + { + std::vector diagnostics; + std::optional> only; + std::optional triggerKind; + }; + + struct CodeActionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + Range range; + CodeActionContext context; + }; + + struct CodeAction + { + struct Disabled + { + string reason; + }; + + string title; + std::optional kind; + std::optional> diagnostics; + std::optional isPreferred; + std::optional disabled; + std::optional edit; + std::optional command; + std::optional data; + }; + + // Color + struct DocumentColorClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DocumentColorOptions : WorkDoneProgressOptions + { + }; + + struct DocumentColorRegistrationOptions : TextDocumentRegistrationOptions, DocumentColorOptions, StaticRegistrationOptions + { + }; + + struct DocumentColorParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct Color + { + decimal red; + decimal green; + decimal blue; + decimal alpha; + }; + + struct ColorInformation + { + Range range; + Color color; + }; + + struct ColorPresentationParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + Color color; + Range range; + }; + + struct ColorPresentation + { + string label; + std::optional textEdit; + std::optional> additionalTextEdits; + }; + + +} diff --git a/lsp-server/src/protocol/detail/completion.hpp b/lsp-server/src/protocol/detail/completion.hpp new file mode 100644 index 0000000..918364c --- /dev/null +++ b/lsp-server/src/protocol/detail/completion.hpp @@ -0,0 +1,185 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + enum class CompletionItemTag + { + kDeprecated = 1 + }; + + enum class InsertTextMode + { + kAsIs = 1, + kAdjustIndentation = 2 + }; + + enum class CompletionItemKind + { + kText = 1, + kMethod = 2, + kFunction = 3, + kConstructor = 4, + kField = 5, + kVariable = 6, + kClass = 7, + kInterface = 8, + kModule = 9, + kProperty = 10, + kUnit = 11, + kValue = 12, + kEnum = 13, + kKeyword = 14, + kSnippet = 15, + kColor = 16, + kFile = 17, + kReference = 18, + kFolder = 19, + kEnumMember = 20, + kConstant = 21, + kStruct = 22, + kEvent = 23, + kOperator = 24, + kTypeParameter = 25 + }; + + enum class InsertTextFormat + { + kPlainText = 1, + kSnippet = 2 + }; + + enum CompletionTriggerKind + { + kInvoked = 1, + kTriggerCharacter = 2, + kTriggerForIncompleteCompletions = 3 + }; + + struct CompletionContext + { + CompletionTriggerKind triggerKind; + std::optional triggerCharacter; + }; + + struct InsertReplaceEdit + { + string newText; + Range insert; + Range replace; + }; + + struct CompletionItemLabelDetails + { + std::optional detail; + std::optional description; + }; + + struct CompletionItem + { + string label; + std::optional labelDetails; + std::optional kind; + std::optional detail; + std::optional> documentation; + std::optional preselect; + std::optional sortText; + std::optional filterText; + std::optional insertText; + std::optional insertTextFormat; + std::optional insertTextMode; + std::optional> textEdit; + std::optional textEditText; + std::optional> additionalTextEdits; + std::optional> commitCharacters; + std::optional command; + std::optional data; + }; + + struct CompletionList + { + struct ItemDefaults + { + struct EditRange + { + Range insert; + Range replace; + }; + std::optional> commitCharacters; + std::optional> editRange; + std::optional insertTextFormat; + std::optional insertTextMode; + std::optional data; + }; + boolean isIncomplete; + ItemDefaults itemDefaults; + std::vector items; + }; + + struct CompletionClientCapabilities + { + struct CompletionItem + { + struct TagSupport + { + std::optional> valueSet; + }; + struct ResolveSupport + { + std::vector properties; + }; + struct InsertTextModeSupport + { + std::optional> valueSet; + }; + + boolean snippetSupport; + std::optional commitCharactersSupport; + std::optional> documentationFormat; + std::optional deprecatedSupport; + std::optional preselectSupport; + std::optional tagSupport; + boolean insertReplaceSupport; + ResolveSupport resolveSupport; + std::optional insertTextModeSupport; + boolean labelDetailsSupport; + }; + struct CompletionItemKinds + { + std::optional> valueSet; + }; + struct CompletionList + { + std::vector itemDefault; + }; + + boolean dynamicRegistration; + std::optional completionItem; + std::optional completionItemKind; + std::optional contextSupport; + std::optional insertTextMode; + std::optional completionList; + }; + + struct CompletionOptions : WorkDoneProgressOptions + { + struct CompletionItem + { + std::optional labelDetailsSupport; + }; + std::optional> triggerCharacters; + std::optional> allCommitCharacters; + std::optional resolveProvider; + std::optional completionItem; + }; + struct CompletionRegistrationOptions : TextDocumentRegistrationOptions, CompletionOptions + { + }; + struct CompletionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + std::optional context; + }; + +} diff --git a/lsp-server/src/protocol/detail/configuration.hpp b/lsp-server/src/protocol/detail/configuration.hpp new file mode 100644 index 0000000..df6dbb6 --- /dev/null +++ b/lsp-server/src/protocol/detail/configuration.hpp @@ -0,0 +1,111 @@ +#pragma once +#include "./basic_types.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + // Configuration + struct ConfigurationItem + { + std::optional scopeUri; + std::optional section; + }; + + struct ConfigurationParams + { + std::vector items; + }; + + struct DidChangeConfigurationClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DidChangeConfigurationParams + { + LSPAny settings; + }; + + // Command Execution + struct ExecuteCommandClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct ExecuteCommandOptions : WorkDoneProgressOptions + { + std::vector commands; + }; + + struct ExecuteCommandRegistrationOptions : ExecuteCommandOptions + { + }; + + struct ExecuteCommandParams : WorkDoneProgressParams + { + string command; + std::optional> arguments; + }; + + // Message + enum class MessageType + { + kError = 1, + kWarning = 2, + kInfo = 3, + kLog = 4, + kDebug = 5 + }; + + struct ShowMessageParams + { + MessageType type; + string message; + }; + + struct ShowMessageRequestClientCapabilities + { + struct MessageActionItem + { + std::optional additionalPropertiesSupport; + }; + std::optional messageActionItem; + }; + + struct MessageActionItem + { + std::string title; + }; + + struct ShowMessageRequestParams + { + MessageType type; + string message; + std::optional> actions; + }; + + struct ShowDocumentClientCapabilities + { + boolean support; + }; + + struct ShowDocumentParams + { + URI uri; + std::optional external; + std::optional takeFocus; + std::optional selection; + }; + + struct ShowDocumentResult + { + boolean success; + }; + + struct LogMessageParams + { + MessageType type; + string message; + }; + +} diff --git a/lsp-server/src/protocol/detail/diagnostics.hpp b/lsp-server/src/protocol/detail/diagnostics.hpp new file mode 100644 index 0000000..2a81982 --- /dev/null +++ b/lsp-server/src/protocol/detail/diagnostics.hpp @@ -0,0 +1,169 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + enum class DiagnosticSeverity + { + kError = 1, + kWarning = 2, + kInformation = 3, + kHint = 4 + }; + + enum class DiagnosticTag + { + kUnnecessary = 1, + kDeprecated = 2 + }; + + struct DiagnosticRelatedInformation + { + Location location; + string message; + }; + + struct CodeDescription + { + string href; + }; + + struct Diagnostic + { + Range range; + std::optional severity; + std::optional code; + std::optional codeDescription; + std::optional source; + string message; + std::optional> tags; + std::optional> relatedInformation; + std::optional data; + }; + + struct PublishDiagnosticsClientCapabilities + { + struct TagSupport + { + std::vector valueSet; + }; + std::optional relatedInformation; + std::optional tagSupport; + std::optional versionSupport; + std::optional codeDescriptionSupport; + std::optional dataSupport; + }; + + struct PublishDiagnosticsParams + { + DocumentUri uri; + std::optional version; + std::vector diagnostics; + }; + + struct DiagnosticClientCapabilities + { + std::optional dynamicRegistration; + std::optional relatedDocumentSupport; + }; + struct DiagnosticOptions : WorkDoneProgressOptions + { + std::optional identifier; + boolean interFileDependencies; + boolean workspaceDiagnostics; + }; + struct DiagnosticRegistrationOptions : TextDocumentRegistrationOptions, DiagnosticOptions, StaticRegistrationOptions + { + }; + struct DiagnosticParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + std::optional identifier; + std::optional previousResultId; + }; + + using DocumentDiagnosticReportKind = std::string_view; + namespace DocumentDiagnosticReportKindLiterals + { + inline constexpr DocumentDiagnosticReportKind Full = "full"; + inline constexpr DocumentDiagnosticReportKind Unchanged = "unchanged"; + } + + struct FullDocumentDiagnosticReport + { + DocumentDiagnosticReportKind kind = DocumentDiagnosticReportKindLiterals::Full; + std::optional resultId; + std::vector items; + }; + + struct UnchangedDocumentDiagnosticReport + { + DocumentDiagnosticReportKind kind = DocumentDiagnosticReportKindLiterals::Unchanged; + std::optional resultId; + }; + + struct RelatedFullDocumentDiagnosticReport : FullDocumentDiagnosticReport + { + std::optional>> relatedDocuments; + }; + + struct RelatedUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport + { + std::optional>> relatedDocuments; + }; + + struct DocumentDiagnosticReportPartialResult + { + std::optional>> relatedDocuments; + }; + + using DocumentDiagnosticReport = std::variant; + + struct DiagnosticServerCancellationData + { + std::optional retriggerRequest; + }; + + struct PreviousResultId + { + DocumentUri uri; + string value; + }; + + struct WorkspaceDiagnosticParams : WorkDoneProgressParams, PartialResultParams + { + std::optional identifier; + std::vector previousResultIds; + }; + + struct WorkspaceFullDocumentDiagnosticReport : FullDocumentDiagnosticReport + { + DocumentUri uri; + std::optional version; + }; + + struct WorkspaceUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport + { + DocumentUri uri; + std::optional version; + }; + + using WorkspaceDocumentDiagnosticReport = std::variant; + struct WorkspaceDiagnosticReport + { + std::vector items; + }; + + struct WorkspaceDiagnosticReportPartialResult + { + std::vector items; + }; + + struct DiagnosticWorkspaceClientCapabilities + { + std::optional refreshSupport; + }; + +} diff --git a/lsp-server/src/protocol/detail/document_features.hpp b/lsp-server/src/protocol/detail/document_features.hpp new file mode 100644 index 0000000..c3e2eb4 --- /dev/null +++ b/lsp-server/src/protocol/detail/document_features.hpp @@ -0,0 +1,209 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + // Document Highlight + struct DocumentHighlightClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DocumentHighlightOptions : WorkDoneProgressOptions + { + }; + + struct DocumentHighlightRegistrationOptions : TextDocumentRegistrationOptions, DocumentHighlightOptions + { + }; + + struct DocumentHighlightParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + }; + + enum class DocumentHighlightKind + { + Text = 1, + Read = 2, + Write = 3 + }; + + struct DocumentHighlight + { + Range range; + DocumentHighlightKind kind; + }; + + // Document Link + struct DocumentLinkClientCapabilities + { + std::optional dynamicRegistration; + std::optional tooltipSupport; + }; + + struct DocumentLinkOptions : WorkDoneProgressOptions + { + boolean resolveProvider; + }; + + struct DocumentLinkRegistrationOptions : TextDocumentRegistrationOptions, DocumentLinkOptions + { + }; + + struct DocumentLinkParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct DocumentLink + { + Range range; + std::optional target; + std::optional tooltip; + std::optional data; + }; + + // Hover + struct HoverClientCapabilities + { + std::optional dynamicRegistration; + std::optional> contentFormat; + }; + + struct HoverOptions : WorkDoneProgressOptions + { + }; + + struct HoverRegistrationOptions : TextDocumentRegistrationOptions, HoverOptions + { + }; + + struct HoverParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + struct Hover + { + struct MarkedStringObject + { + std::string language; + std::string value; + }; + + using MarkedString = std::variant; + + std::variant, MarkupContent> contents; + std::optional range; + }; + + // CodeLens + struct CodeLensClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct CodeLensOptions : WorkDoneProgressOptions + { + std::optional resolveProvider; + }; + + struct CodeLensRegistrationOptions : TextDocumentRegistrationOptions, CodeLensOptions + { + }; + + struct CodeLensParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct CodeLens + { + Range range; + std::optional command; + std::optional data; + }; + + struct CodeLensWorkspaceClientCapabilities + { + std::optional refreshSupport; + }; + + // Fold Range + using FoldingRangeKind = std::string_view; + namespace FoldingRangeKindLiterals + { + inline constexpr FoldingRangeKind Comment = "comment"; + inline constexpr FoldingRangeKind Imports = "imports"; + inline constexpr FoldingRangeKind Region = "region"; + } + + struct FoldingRangeClientCapabilities + { + struct FoldingRangeKinds + { + std::vector valueSet; + }; + struct FoldingRange + { + std::optional collapsedText; + }; + + std::optional dynamicRegistration; + std::optional rangeLimit; + std::optional lineFoldingOnly; + std::optional foldingRangeKind; + std::optional foldingRange; + }; + + struct FoldingRangeOptions : WorkDoneProgressOptions + { + }; + + struct FoldingRangeRegistrationOptions : TextDocumentRegistrationOptions, FoldingRangeOptions, StaticRegistrationOptions + { + }; + + struct FoldingRangeParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct FoldingRange + { + uinteger startLine; + std::optional startCharacter; + uinteger endLine; + std::optional endCharacter; + std::optional kind; + std::optional collapsedText; + }; + + // Sekection Range + struct SelectionRangeClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct SelectionRangeOptions : WorkDoneProgressOptions + { + }; + + struct SelectionRangeRegistrationOptions : SelectionRangeOptions, TextDocumentRegistrationOptions, StaticRegistrationOptions + { + }; + + struct SelectionRangeParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + std::vector positions; + }; + + struct SelectionRange + { + Range range; + std::optional selectionRange; + }; + +} diff --git a/lsp-server/src/protocol/detail/document_sync.hpp b/lsp-server/src/protocol/detail/document_sync.hpp new file mode 100644 index 0000000..020671a --- /dev/null +++ b/lsp-server/src/protocol/detail/document_sync.hpp @@ -0,0 +1,120 @@ +#pragma once +#include "./basic_types.hpp" +#include "./registration.hpp" + +namespace lsp::protocol +{ + struct TextDocumentSyncClientCapabilities + { + boolean dynamicRegistration; + std::optional willSave; + std::optional willSaveWaitUntil; + std::optional didSave; + }; + + enum class TextDocumentSyncKind + { + kNone = 0, + kFull = 1, + kIncremental = 2 + }; + + struct TextDocumentSyncOptions + { + std::optional openClose; + std::optional change; + }; + + struct TextDocumentItem + { + DocumentUri uri; + string languageId; + integer version; + string text; + }; + + struct TextDocumentIdentifier + { + DocumentUri uri; + }; + + struct VersionedTextDocumentIdentifier : TextDocumentIdentifier + { + integer version; + }; + + struct OptionalVersionedTextDocumentIdentifier : TextDocumentIdentifier + { + std::optional version; + }; + + struct TextDocumentPositionParams + { + TextDocumentIdentifier textDocument; + Position position; + }; + + struct DidOpenTextDocumentParams + { + TextDocumentItem textDocument; + }; + + struct TextDocumentChangeRegistrationOptions : TextDocumentRegistrationOptions + { + TextDocumentSyncKind syncKind; + }; + + struct TextDocumentContentChangeEvent + { + Range range; + uinteger rangeLength; + string text; + }; + + struct DidChangeTextDocumentParams + { + VersionedTextDocumentIdentifier textDocument; + std::vector contentChanges; + }; + + enum class TextDocumentSaveReason + { + Manual = 1, + AfterDelay = 2, + FocusOut = 3 + }; + + struct WillSaveTextDocumentParams + { + TextDocumentIdentifier textDocument; + TextDocumentSaveReason reason; + }; + + struct SaveOptions + { + std::optional includeText; + }; + + struct TextDocumentSaveRegistrationOptions : TextDocumentRegistrationOptions + { + std::optional includeText; + }; + + struct DidSaveTextDocumentParams + { + TextDocumentIdentifier textDocument; + std::optional text; + }; + + struct DidCloseTextDocumentParams + { + TextDocumentIdentifier textDocument; + }; + + struct TextDocumentEdit + { + // OptionalVersionedTextDocumentIdentifier textDocument; + std::variant, std::vector> edits; + }; + +} diff --git a/lsp-server/src/protocol/detail/file_operations.hpp b/lsp-server/src/protocol/detail/file_operations.hpp new file mode 100644 index 0000000..9e278ee --- /dev/null +++ b/lsp-server/src/protocol/detail/file_operations.hpp @@ -0,0 +1,122 @@ + +#pragma once +#include "./basic_types.hpp" +#include "./workspace.hpp" + +namespace lsp::protocol +{ + using FileOperationPatternKind = std::string_view; + namespace FileOperationPatternKindLiterals + { + inline constexpr FileOperationPatternKind File = "file"; + inline constexpr FileOperationPatternKind Folder = "folder"; + } + + struct FileOperationPatternOptions + { + std::optional ignoreCase; + }; + + struct FileOperationPattern + { + string glob; + std::optional matches; + std::optional options; + }; + + struct FileOperationFilter + { + std::optional scheme; + FileOperationPattern pattern; + }; + + struct FileCreate + { + string uri; + }; + + struct CreateFilesParams + { + std::vector files; + }; + + struct FileOperationRegistrationOptions + { + std::vector filters; + }; + + struct FileRename + { + string oldUri; + string newUri; + }; + + struct RenameFilesParams + { + std::vector files; + }; + + struct FileDelete + { + string uri; + }; + + struct DeleteFilesParams + { + std::vector files; + }; + + // File Watching + struct DidChangeWatchedFilesClientCapabilities + { + std::optional dynamicRegistration; + std::optional relativePatternSupport; + }; + + using Pattern = string; + + struct RelativePattern + { + std::variant baseUri; + Pattern pattern; + }; + + using GlobPattern = std::variant; + + enum class WatchKind + { + kCreate = 1, + kChange = 2, + kDelete = 4 + }; + + struct FileSystemWatcher + { + GlobPattern globPattern; + std::optional kind; + }; + + struct DidChangeWatchedFilesRegistrationOptions + { + std::vector watchers; + }; + + enum class FileChangeType + { + kCreated = 1, + kChanged = 2, + kDeleted = 3 + }; + + struct FileEvent + { + DocumentUri uri; + FileChangeType type; + }; + + struct DidChangeWatchedFilesParams + { + std::vector changes; + }; + +} diff --git a/lsp-server/src/protocol/detail/formatting.hpp b/lsp-server/src/protocol/detail/formatting.hpp new file mode 100644 index 0000000..4f8b98d --- /dev/null +++ b/lsp-server/src/protocol/detail/formatting.hpp @@ -0,0 +1,82 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + // Document Formatting + struct DocumentFormattingClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DocumentFormattingOptions : WorkDoneProgressOptions + { + }; + + struct DocumentFormattingRegistrationOptions : TextDocumentRegistrationOptions, DocumentFormattingOptions + { + }; + + struct FormattingOptions + { + uinteger tabSize; + boolean insertSpaces; + std::optional trimTrailingWhitespace; + std::optional insertFinalNewline; + std::optional trimFinalNewlines; + std::map> key; + }; + + struct DocumentFormattingParams : WorkDoneProgressParams + { + TextDocumentIdentifier textDocument; + FormattingOptions options; + }; + + // Document Range Formatting + struct DocumentRangeFormattingClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DocumentRangeFormattingOptions : WorkDoneProgressOptions + { + }; + + struct DocumentRangeFormattingRegistrationOptions : TextDocumentRegistrationOptions, DocumentRangeFormattingOptions + { + }; + + struct DocumentRangeFormattingParams : WorkDoneProgressParams + { + TextDocumentIdentifier textDocument; + Range range; + FormattingOptions options; + }; + + // Document On Type Formatting + struct DocumentOnTypeFormattingClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct DocumentOnTypeFormattingOptions : WorkDoneProgressOptions + { + string firstTriggerCharacter; + std::optional> moreTriggerCharacter; + }; + + struct DocumentOnTypeFormattingRegistrationOptions : TextDocumentRegistrationOptions, DocumentOnTypeFormattingOptions + { + }; + + struct DocumentOnTypeFormattingParams + { + TextDocumentIdentifier textDocument; + Position position; + string ch; + FormattingOptions options; + }; +} diff --git a/lsp-server/src/protocol/detail/inline_features.hpp b/lsp-server/src/protocol/detail/inline_features.hpp new file mode 100644 index 0000000..ff3353b --- /dev/null +++ b/lsp-server/src/protocol/detail/inline_features.hpp @@ -0,0 +1,162 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + // Inlay Hints + struct InlayHintClientCapabilities + { + struct ResolveSupport + { + std::vector properties; + }; + std::optional dynamicRegistration; + std::optional resolveSupport; + }; + + struct InlayHintOptions : WorkDoneProgressOptions + { + std::optional resolveProvider; + }; + + struct InlayHintRegistrationOptions : TextDocumentRegistrationOptions, InlayHintOptions, StaticRegistrationOptions + { + }; + + struct InlayHintParams : WorkDoneProgressParams + { + TextDocumentIdentifier textDocument; + Range range; + }; + + struct InlayHintLabelPart + { + string value; + std::optional> tooltip; + std::optional location; + std::optional command; + }; + + enum class InlayHintKind + { + kType = 1, + kParameter = 2 + }; + + struct InlayHint + { + Position position; + std::variant> label; + std::optional kind; + std::optional> textEdits; + ; + std::optional> tooltip; + std::optional paddingLeft; + std::optional paddingRight; + std::optional data; + }; + + struct InlayHintWorkspaceClientCapabilities + { + std::optional refreshSupport; + }; + + // Inline Values + struct InlineValueClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct InlineValueOptions : WorkDoneProgressOptions + { + }; + + struct InlineValueRegistrationOptions : TextDocumentRegistrationOptions, InlineValueOptions, StaticRegistrationOptions + { + }; + + struct InlineValueContent + { + integer frameId; + Range stoppedLocation; + }; + + struct InlineValueText + { + Range range; + string text; + }; + + struct InlineValueParams : WorkDoneProgressParams + { + TextDocumentIdentifier textDocument; + Range range; + InlineValueContent context; + }; + + struct InlineValueVariableLookup + { + Range range; + std::optional variableName; + std::optional caseSensitiveLookup; + }; + + struct InlineValueEvaluatableExpression + { + Range range; + std::optional expression; + }; + using InlineValue = std::variant; + + struct InlineValueWorkspaceClientCapabilities + { + std::optional refreshSupport; + }; + + // Moniker + struct MonikerClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct MonikerOptions : WorkDoneProgressOptions + { + }; + + struct MonikerRegistrationOptions : TextDocumentRegistrationOptions, MonikerOptions + { + }; + + struct MonikerParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + }; + + using UniquenessLevel = std::string_view; + namespace UniquenessLevelLiterals + { + inline constexpr UniquenessLevel Document = "document"; + inline constexpr UniquenessLevel Project = "project"; + inline constexpr UniquenessLevel Group = "group"; + inline constexpr UniquenessLevel Scheme = "scheme"; + inline constexpr UniquenessLevel Global = "global"; + } + + using MonikerKind = std::string_view; + namespace MonikerKindLiterals + { + inline constexpr MonikerKind Import = "import"; + inline constexpr MonikerKind Export = "export"; + inline constexpr MonikerKind Local = "local"; + } + + struct Moniker + { + string scheme; + string identifier; + UniquenessLevel unique; + std::optional kind; + }; + +} diff --git a/lsp-server/src/protocol/detail/lsp_any.inl b/lsp-server/src/protocol/detail/lsp_any.inl new file mode 100644 index 0000000..6c899a1 --- /dev/null +++ b/lsp-server/src/protocol/detail/lsp_any.inl @@ -0,0 +1,193 @@ +#pragma once +#include "./basic_types.hpp" + +namespace lsp::protocol +{ + + inline LSPAny::LSPAny() : + value(nullptr) {} + + inline LSPAny::LSPAny(const LSPAny& other) : + value(other.value) {} + + inline LSPAny::LSPAny(LSPAny&& other) noexcept : + value(std::move(other.value)) {} + + inline LSPAny::LSPAny(const std::map& val) : + value(val) {} + + inline LSPAny::LSPAny(std::map&& val) : + value(std::move(val)) {} + + inline LSPAny::LSPAny(const std::vector& val) : + value(val) {} + + inline LSPAny::LSPAny(std::vector&& val) : + value(std::move(val)) {} + + inline LSPAny::LSPAny(const string& val) : + value(val) {} + + inline LSPAny::LSPAny(string&& val) : + value(std::move(val)) {} + + inline LSPAny::LSPAny(const char* val) : + value(string(val)) {} + + inline LSPAny::LSPAny(int val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(long val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(long long val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(unsigned int val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(unsigned long val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(unsigned long long val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(float val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(double val) : + value(val) {} + + inline LSPAny::LSPAny(long double val) : + value(static_cast(val)) {} + + inline LSPAny::LSPAny(boolean val) : + value(val) {} + + inline LSPAny::LSPAny(std::nullptr_t) : + value(nullptr) {} + + // 赋值操作符 + inline LSPAny& LSPAny::operator=(const LSPAny& other) + { + value = other.value; + return *this; + } + + inline LSPAny& LSPAny::operator=(LSPAny&& other) noexcept + { + value = std::move(other.value); + return *this; + } + + // 针对每种支持的类型的赋值操作符 + inline LSPAny& LSPAny::operator=(const std::map& val) + { + value = val; + return *this; + } + + inline LSPAny& LSPAny::operator=(std::map&& val) + { + value = std::move(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(const std::vector& val) + { + value = val; + return *this; + } + + inline LSPAny& LSPAny::operator=(std::vector&& val) + { + value = std::move(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(const string& val) + { + value = val; + return *this; + } + + inline LSPAny& LSPAny::operator=(string&& val) + { + value = std::move(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(const char* val) + { + value = string(val); + return *this; + } + + // 所有数字类型都转换为 decimal + inline LSPAny& LSPAny::operator=(int val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(long val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(long long val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(unsigned int val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(unsigned long val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(unsigned long long val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(float val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(double val) + { + value = val; + return *this; + } + + inline LSPAny& LSPAny::operator=(long double val) + { + value = static_cast(val); + return *this; + } + + inline LSPAny& LSPAny::operator=(boolean val) + { + value = val; + return *this; + } + + inline LSPAny& LSPAny::operator=(std::nullptr_t) + { + value = nullptr; + return *this; + } + +} diff --git a/lsp-server/src/protocol/detail/message.hpp b/lsp-server/src/protocol/detail/message.hpp new file mode 100644 index 0000000..6be8db1 --- /dev/null +++ b/lsp-server/src/protocol/detail/message.hpp @@ -0,0 +1,64 @@ +#pragma once +#include "./basic_types.hpp" + +namespace lsp::protocol +{ + enum class ErrorCode : int + { + kParseError = -32700, + kInvalidRequest = -32600, + kMethodNotFound = -32601, + kInvalidParams = -32602, + kInternalError = -32603, + + kJsonrpcReservedErrorRangeStart = -32099, + kServerNotInitialized = -32002, + kUnknownErrorCode = -32001, + kJsonrpcReservedErrorRangeEnd = -32000, + kLspReservedErrorRangeStart = -32899, + kRequestFailed = -32803, + kServerCancelled = -32802, + kContentModified = -32801, + kRequestCancelled = -32800, + kLspReservedErrorRangeEnd = -32800 + }; + + struct Message + { + string jsonrpc = "2.0"; + }; + + struct RequestMessage: Message + { + std::variant id; + string method; + std::optional> params; + }; + + struct ResponseError: Message + { + ErrorCode code; + string message; + std::optional data; + }; + + struct ResponseMessage: Message + { + std::variant id; + std::optional result; + std::optional error; + }; + + struct NotificationMessage: Message + { + string method; + std::optional params; + }; + + struct CancelParams + { + std::variant id; + }; + +} + diff --git a/lsp-server/src/protocol/detail/navigation.hpp b/lsp-server/src/protocol/detail/navigation.hpp new file mode 100644 index 0000000..31fd6f4 --- /dev/null +++ b/lsp-server/src/protocol/detail/navigation.hpp @@ -0,0 +1,199 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" +#include "./symbols.hpp" + +namespace lsp::protocol +{ + // Declaration + struct DeclarationClientCapabilities + { + std::optional dynamicRegistration; + std::optional linkSupport; + }; + + struct DeclarationOptions : WorkDoneProgressOptions + { + }; + + struct DeclarationRegistrationOptions : DeclarationOptions, TextDocumentRegistrationOptions, StaticRegistrationOptions + { + }; + + struct DeclarationParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + }; + + // Definition + struct DefinitionClientCapabilities + { + std::optional dynamicRegistration; + std::optional linkSupport; + }; + + struct DefinitionOptions : WorkDoneProgressOptions + { + }; + + struct DefinitionRegistrationOptions : TextDocumentRegistrationOptions, DefinitionOptions + { + }; + + struct DefinitionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + }; + + // TypeDefinition + struct TypeDefinitionClientCapabilities + { + std::optional dynamicRegistration; + std::optional linkSupport; + }; + + struct TypeDefinitionOptions : WorkDoneProgressOptions + { + }; + + struct TypeDefinitionRegistrationOptions : TextDocumentRegistrationOptions, TypeDefinitionOptions, StaticRegistrationOptions + { + }; + + struct TypeDefinitionParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + // Implementation + struct ImplementationClientCapabilities + { + std::optional dynamicRegistration; + std::optional linkSupport; + }; + + struct ImplementationOptions : WorkDoneProgressOptions + { + }; + + struct ImplementationRegistrationOptions : TextDocumentRegistrationOptions, ImplementationOptions, StaticRegistrationOptions + { + }; + + struct ImplementationParams : WorkDoneProgressParams, PartialResultParams + { + }; + + // Reference + struct ReferenceContext + { + boolean includeDeclaration; + }; + struct ReferenceClientCapabilities + { + std::optional dynamicRegistration; + }; + struct ReferenceOptions : WorkDoneProgressOptions + { + }; + struct ReferenceRegistrationOptions : TextDocumentRegistrationOptions, ReferenceOptions + { + }; + struct ReferenceParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + ReferenceContext context; + }; + + // CallHierarchy + struct CallHierarchyClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct CallHierarchyOptions : WorkDoneProgressOptions + { + }; + + struct CallHierarchyRegistrationOptions : TextDocumentRegistrationOptions, CallHierarchyOptions, StaticRegistrationOptions + { + }; + + struct CallHierarchyParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + struct CallHierarchyItem + { + string name; + SymbolKind kind; + std::vector tags; + DocumentUri uri; + Range range; + Range selectionRange; + std::optional data; + }; + + struct CallHierarchyIncomingCallsParams : WorkDoneProgressParams, PartialResultParams + { + CallHierarchyItem item; + }; + + struct CallHierarchyIncomingCall + { + CallHierarchyItem from; + std::vector fromRanges; + }; + + struct CallHierarchyOutgoingCallsParams : WorkDoneProgressParams, PartialResultParams + { + CallHierarchyItem item; + }; + + struct CallHierarchyOutgoingCall + { + CallHierarchyItem from; + std::vector fromRanges; + }; + + // TypeHierarchy + struct TypeHierarchyClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct TypeHierarchyOptions : WorkDoneProgressOptions + { + }; + + struct TypeHierarchyRegistrationOptions : TextDocumentRegistrationOptions, TypeHierarchyOptions, StaticRegistrationOptions + { + }; + + struct TypeHierarchyParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + struct TypeHierarchyPrepareParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + struct TypeHierarchyItem + { + string name; + SymbolKind kind; + std::vector tags; + DocumentUri uri; + Range range; + Range selectionRange; + std::optional data; + }; + + struct TypeHierarchySupertypesParams : WorkDoneProgressParams, PartialResultParams + { + TypeHierarchyItem item; + }; + + struct TypeHierarchySubtypesParams : WorkDoneProgressParams, PartialResultParams + { + TypeHierarchyItem item; + }; + +} diff --git a/lsp-server/src/protocol/detail/notebook.hpp b/lsp-server/src/protocol/detail/notebook.hpp new file mode 100644 index 0000000..6c73392 --- /dev/null +++ b/lsp-server/src/protocol/detail/notebook.hpp @@ -0,0 +1,148 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" + +namespace lsp::protocol +{ + enum class NotebookCellKind + { + Markup = 1, + Code = 2, + }; + + struct ExecutionSummary + { + uinteger executionOrder; + std::optional success; + }; + + struct NotebookCell + { + NotebookCellKind kind; + DocumentUri document; + std::optional metadata; + std::optional executionSummary; + }; + + struct NotebookDocument + { + URI uri; + string notebookType; + integer version; + LSPObject metadata; + std::vector cells; + }; + + struct NotebookDocumentFilter + { + string notebookType; + std::optional scheme; + std::optional pattern; + }; + + struct NotebookCellTextDocumentFilter + { + std::variant notebook; + std::optional language; + }; + + struct NotebookSelector + { + struct Cell + { + string language; + }; + + std::variant notebook; + std::optional> cells; + }; + + struct NotebookDocumentSyncOptions + { + std::vector notebookSelector; + std::optional save; + }; + + struct NotebookDocumentSyncRegistrationOptions : NotebookDocumentSyncOptions, StaticRegistrationOptions + { + }; + + struct NotebookDocumentSyncClientCapabilities + { + std::optional dynamicRegistration; + std::optional executionSummarySupport; + }; + + struct NotebookDocumentClientCapabilities + { + NotebookDocumentSyncClientCapabilities synchronization; + }; + + struct DidOpenNotebookDocumentParams + { + NotebookDocument notebookDocument; + std::vector cellTextDocuments; + }; + + struct VersionedNotebookDocumentIdentifier + { + integer version; + URI uri; + }; + + struct NotebookCellArrayChange + { + uinteger start; + uinteger deleteCount; + std::optional> cells; + }; + + struct NotebookDocumentChangeEvent + { + struct Cell + { + struct Structure + { + NotebookCellArrayChange array; + std::optional> didOpen; + std::optional> didClose; + }; + + struct TextContent + { + VersionedTextDocumentIdentifier document; + std::vector changes; + }; + + std::optional structure; + std::optional> data; + std::optional textContent; + }; + + std::optional metadata; + std::optional> cells; + }; + + struct DidChangeNotebookDocumentParams + { + VersionedNotebookDocumentIdentifier notebookDocument; + NotebookDocumentChangeEvent change; + }; + + struct NotebookDocumentIdentifier + { + URI uri; + }; + + struct DidSaveNotebookDocumentParams + { + NotebookDocumentIdentifier notebookDocument; + }; + + struct DidCloseNotebookDocumentParams + { + NotebookDocumentIdentifier notebookDocument; + std::vector cellTextDocuments; + }; + +} diff --git a/lsp-server/src/protocol/detail/progress.hpp b/lsp-server/src/protocol/detail/progress.hpp new file mode 100644 index 0000000..854a374 --- /dev/null +++ b/lsp-server/src/protocol/detail/progress.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "./basic_types.hpp" + +namespace lsp::protocol +{ + struct WorkDoneProgressBegin + { + string kind = "begin"; + string title; + boolean cancellable; + std::optional message; + uinteger percentage; + }; + + struct WorkDoneProgressReport + { + string kind = "report"; + boolean cancellable; + std::optional message; + uinteger percentage; + }; + + struct WorkDoneProgressEnd + { + string kind = "end"; + string message; + }; + + struct WorkDoneProgressParams + { + ProgressToken workDoneToken; + }; + + struct WorkDoneProgressOptions + { + boolean workDoneProgress; + }; + + struct PartialResultParams + { + ProgressToken partialResultToken; + }; + + struct WorkDoneProgressCreateParams + { + ProgressToken token; + }; + + struct WorkDoneProgressCancelParams + { + ProgressToken token; + }; + + using TraceValue = std::string_view; + namespace TraceValueLiterals + { + inline constexpr TraceValue Off = "off"; + inline constexpr TraceValue Messages = "messages"; + inline constexpr TraceValue Verbose = "verbose"; + } + + struct SetTraceParams + { + TraceValue value; + }; + + struct LogTraceParams + { + string message; + std::optional verbose; + }; + +} diff --git a/lsp-server/src/protocol/detail/registration.hpp b/lsp-server/src/protocol/detail/registration.hpp new file mode 100644 index 0000000..6e69f74 --- /dev/null +++ b/lsp-server/src/protocol/detail/registration.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "./basic_types.hpp" + +namespace lsp::protocol +{ + struct Registration + { + string id; + string method; + std::optional registerOptions; + }; + + struct RegistrationParams + { + std::vector registrations; + }; + + struct StaticRegistrationOptions + { + std::optional id; + }; + + struct TextDocumentRegistrationOptions + { + std::optional documentSelector; + }; + + struct Unregistration + { + string id; + string method; + }; + + struct UnregistrationParams + { + std::vector unregistration; + }; + +} // namespace lsp::protocol diff --git a/lsp-server/src/protocol/detail/rename.hpp b/lsp-server/src/protocol/detail/rename.hpp new file mode 100644 index 0000000..c461057 --- /dev/null +++ b/lsp-server/src/protocol/detail/rename.hpp @@ -0,0 +1,64 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + enum class PrepareSupportDefaultBehavior + { + kIdentifier = 1 + }; + + // Rename + struct RenameClientCapabilities + { + std::optional dynamicRegistration; + std::optional prepareSupport; + std::optional prepareSupportDefaultBehavior; + std::optional honorsChangeAnnotations; + }; + + struct RenameOptions : WorkDoneProgressOptions + { + std::optional prepareProvider; + }; + + struct RenameRegistrationOptions : TextDocumentRegistrationOptions, RenameOptions + { + }; + + struct RenameParams : TextDocumentPositionParams, WorkDoneProgressParams + { + string newName; + }; + + struct PrepareRenameParams : TextDocumentPositionParams, WorkDoneProgressParams + { + }; + + // Linked Editing Range + struct LinkedEditingRangeClientCapabilities + { + std::optional dynamicRegistration; + }; + + struct LinkedEditingRangeOptions : WorkDoneProgressOptions + { + }; + + struct LinkedEditingRangeRegistrationOptions : TextDocumentRegistrationOptions, LinkedEditingRangeOptions, StaticRegistrationOptions + { + }; + + struct LinkedEditingRangeParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams + { + }; + + struct LinkedEditingRanges + { + std::vector ranges; + std::optional wordPattern; + }; + +} diff --git a/lsp-server/src/protocol/detail/semantic_tokens.hpp b/lsp-server/src/protocol/detail/semantic_tokens.hpp new file mode 100644 index 0000000..e9769a3 --- /dev/null +++ b/lsp-server/src/protocol/detail/semantic_tokens.hpp @@ -0,0 +1,151 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + using SemanticTokenType = std::string_view; + namespace SemanticTokenTypesLiterals + { + inline constexpr SemanticTokenType Namespace = "namespace"; + inline constexpr SemanticTokenType Type = "type"; + inline constexpr SemanticTokenType Class = "class"; + inline constexpr SemanticTokenType Enum = "enum"; + inline constexpr SemanticTokenType Interface = "interface"; + inline constexpr SemanticTokenType Struct = "struct"; + inline constexpr SemanticTokenType TypeParameter = "typeParameter"; + inline constexpr SemanticTokenType Parameter = "parameter"; + inline constexpr SemanticTokenType Variable = "variable"; + inline constexpr SemanticTokenType Property = "property"; + inline constexpr SemanticTokenType EnumMember = "enumMember"; + inline constexpr SemanticTokenType Event = "event"; + inline constexpr SemanticTokenType Function = "function"; + inline constexpr SemanticTokenType Method = "method"; + inline constexpr SemanticTokenType Macro = "macro"; + inline constexpr SemanticTokenType Keyword = "keyword"; + inline constexpr SemanticTokenType Modifier = "modifier"; + inline constexpr SemanticTokenType Comment = "comment"; + inline constexpr SemanticTokenType String = "string"; + inline constexpr SemanticTokenType Number = "number"; + inline constexpr SemanticTokenType Regexp = "regexp"; + inline constexpr SemanticTokenType Operator = "operator"; + inline constexpr SemanticTokenType Decorator = "decorator"; + } + + using SemanticTokenModifiers = std::string_view; + namespace SemanticTokenModifiersLiterals + { + inline constexpr SemanticTokenModifiers Declaration = "declaration"; + inline constexpr SemanticTokenModifiers Definition = "definition"; + inline constexpr SemanticTokenModifiers Readonly = "readonly"; + inline constexpr SemanticTokenModifiers Static = "static"; + inline constexpr SemanticTokenModifiers Deprecated = "deprecated"; + inline constexpr SemanticTokenModifiers Abstract = "abstract"; + inline constexpr SemanticTokenModifiers Async = "async"; + inline constexpr SemanticTokenModifiers Modification = "modification"; + inline constexpr SemanticTokenModifiers Documentation = "documentation"; + inline constexpr SemanticTokenModifiers DefaultLibrary = "defaultLibrary"; + } + + using TokenFormat = std::string_view; + namespace TokenFormatLiterals + { + inline constexpr TokenFormat Relative = "relative"; + } + + struct SemanticTokensLegend + { + std::vector tokenType; + std::vector tokenModifiers; + }; + + struct SemanticTokensClientCapabilities + { + struct Requests + { + struct Full + { + std::optional delta; + }; + + std::optional range; + std::variant full; + }; + + std::optional dynamicRegistration; + std::vector tokenType; + std::vector tokenModifiers; + std::optional overlappingTokenSupport; + std::optional multilineTokenSupport; + std::optional serverCancelSupport; + std::optional augmentsSyntaxTokens; + }; + + struct SemanticTokensOptions : WorkDoneProgressOptions + { + struct Full + { + std::optional delta; + }; + + SemanticTokensLegend legend; + std::optional range; + std::variant full; + }; + + struct SemanticTokensRegistrationOptions : TextDocumentRegistrationOptions, SemanticTokensOptions, StaticRegistrationOptions + { + }; + + struct SemanticTokensParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct SemanticTokens + { + std::optional resultId; + std::vector data; + }; + + struct SemanticTokensPartialResult + { + std::vector data; + }; + + struct SemanticTokensDeltaParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + string previousResultId; + }; + + struct SemanticTokensEdit + { + uinteger start; + uinteger deleteCount; + std::optional data; + }; + + struct SemanticTokensDelta + { + std::optional resultId; + std::optional> edits; + }; + + struct SemanticTokensDeltaPartialResult + { + std::optional> edits; + }; + + struct SemanticTokensRangeParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + Range range; + }; + + struct SemanticTokensWorkspaceClientCapabilities + { + std::optional refreshSupport; + }; +} diff --git a/lsp-server/src/protocol/detail/signature_help.hpp b/lsp-server/src/protocol/detail/signature_help.hpp new file mode 100644 index 0000000..41bff20 --- /dev/null +++ b/lsp-server/src/protocol/detail/signature_help.hpp @@ -0,0 +1,78 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" + +namespace lsp::protocol +{ + struct SignatureHelpClientCapabilities + { + struct SignatureInformation + { + struct ParammeterInformation + { + std::optional labelOffsetSupport; + }; + + std::optional> documentationFormat; + std::optional parameterInformation; + std::optional activeParameterSupport; + }; + + std::optional dynamicRegistration; + std::optional signatureInformation; + std::optional contextSupport; + }; + + struct SignatureHelpOptions : WorkDoneProgressOptions + { + std::optional> triggerCharacters; + std::optional> retriggerCharacters; + }; + + struct SignatureHelpRegistrationOptions : TextDocumentRegistrationOptions, SignatureHelpOptions + { + }; + + enum class SignatureHelpTriggerKind + { + kInvoked = 1, + kTriggerCharacter = 2, + kContentChange = 3 + }; + + struct ParammeterInformation + { + string label; + std::variant documentation; + }; + + struct SignatureInformation + { + string label; + std::optional> documentation; + std::optional> parameters; + std::optional activeParameter; + }; + + struct SignatureHelp + { + std::vector signatures; + std::optional activeSignature; + std::optional activeParameter; + }; + + struct SignatureHelpContext + { + SignatureHelpTriggerKind triggerKind; + string triggerCharacter; + boolean isRetrigger; + std::optional activeSignatureHelp; + }; + + struct SignatureHelpParams : TextDocumentPositionParams, WorkDoneProgressParams + { + std::optional contextSupport; + }; + +} diff --git a/lsp-server/src/protocol/detail/symbols.hpp b/lsp-server/src/protocol/detail/symbols.hpp new file mode 100644 index 0000000..ac8cb5c --- /dev/null +++ b/lsp-server/src/protocol/detail/symbols.hpp @@ -0,0 +1,147 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./progress.hpp" +#include "./completion.hpp" + +namespace lsp::protocol +{ + enum class SymbolKind + { + kFile = 1, + kModule = 2, + kNamespace = 3, + kPackage = 4, + kClass = 5, + kMethod = 6, + kProperty = 7, + kField = 8, + kConstructor = 9, + kEnum = 10, + kInterface = 11, + kFunction = 12, + kVariable = 13, + kConstant = 14, + kString = 15, + kNumber = 16, + kBoolean = 17, + kArray = 18, + kObject = 19, + kKey = 20, + kNull = 21, + kEnumMember = 22, + kStruct = 23, + kEvent = 24, + kOperator = 25, + kTypeParameter = 26 + }; + + enum class SymbolTag + { + Deprecated = 1 + }; + + struct DocumentSymbol + { + string name; + std::optional detail; + SymbolKind kind; + std::optional> tags; + std::optional deprecated; + Range range; + Range selectionRange; + std::optional> children; + }; + + struct SymbolInformation + { + string name; + SymbolKind kind; + std::optional> tags; + std::optional deprecated; + Location location; + std::optional containerName; + }; + + struct DocumentSymbolClientCapabilities + { + struct SymbolKinds + { + std::vector valueSet; + }; + struct TagSupport + { + std::optional> valueSet; + }; + + std::optional dynamicRegistration; + std::optional symbolKind; + std::optional hierarchicalDocumentSymbolSupport; + std::optional tagSupport; + std::optional labelSupport; + }; + + struct DocumentSymbolOptions : WorkDoneProgressOptions + { + std::optional label; + }; + + struct DocumentSymbolRegistrationOptions : TextDocumentRegistrationOptions, DocumentSymbolOptions + { + }; + + struct DocumentSymbolParams : WorkDoneProgressParams, PartialResultParams + { + TextDocumentIdentifier textDocument; + }; + + struct WorkspaceSymbolClientCapabilities + { + struct SymbolKinds + { + std::vector valueSet; + }; + struct TagSupport + { + std::optional> valueSet; + }; + struct ResolveSupport + { + std::vector properties; + }; + + std::optional dynamicRegistration; + std::optional symbolKind; + std::optional tagSupport; + std::optional resolveSupport; + }; + + struct WorkspaceSymbolOptions : WorkDoneProgressOptions + { + std::optional resolveProvider; + }; + + struct WorkspaceSymbolRegistrationOptions : WorkspaceSymbolOptions + { + }; + + struct WorkspaceSymbolParams : WorkDoneProgressParams, PartialResultParams + { + string query; + }; + + struct WorkspaceSymbol + { + struct LocationUriOnly + { + DocumentUri uri; + }; + string name; + SymbolKind kind; + std::optional> tags; + std::optional containerName; + std::variant location; + std::optional data; + }; + +} diff --git a/lsp-server/src/protocol/detail/workspace.hpp b/lsp-server/src/protocol/detail/workspace.hpp new file mode 100644 index 0000000..48ac057 --- /dev/null +++ b/lsp-server/src/protocol/detail/workspace.hpp @@ -0,0 +1,121 @@ +#pragma once +#include "./basic_types.hpp" +#include "./document_sync.hpp" +#include "./diagnostics.hpp" + +namespace lsp::protocol +{ + struct CreateFileOptions + { + boolean overwrite; + boolean ignoreIfExists; + }; + + struct CreateFile + { + string kind = "create"; + DocumentUri uri; + std::optional options; + std::optional annotationId; + }; + + struct RenameFileOptions + { + boolean overwrite; + boolean ignoreIfExists; + }; + + struct RenameFile + { + string kind = "rename"; + DocumentUri oldUri; + DocumentUri newUri; + std::optional options; + std::optional annotationId; + }; + + struct DeleteFileOptions + { + boolean recursive; + boolean ignoreIfNotExists; + }; + + struct DeleteFile + { + string kind = "delete"; + DocumentUri uri; + std::optional options; + std::optional annotationId; + }; + + struct WorkspaceEdit + { + std::map> changes; + std::variant, std::vector, std::vector, std::vector> documentChanges; + std::optional> changeAnnotations; + }; + + using ResourceOperationKind = std::string_view; + namespace ResourceOperationKindLiterals + { + inline constexpr ResourceOperationKind Create = "create"; + inline constexpr ResourceOperationKind Rename = "rename"; + inline constexpr ResourceOperationKind Delete = "delete"; + } + + using FailureHandlingKind = std::string_view; + namespace FailureHandlingKindLiterals + { + inline constexpr FailureHandlingKind Abort = "abort"; + inline constexpr FailureHandlingKind Transactional = "transactional"; + inline constexpr FailureHandlingKind TextOnlyTransactional = "textOnlyTransactional"; + inline constexpr FailureHandlingKind Undo = "undo"; + inline constexpr FailureHandlingKind Commit = "commit"; + } + + struct WorkspaceEditClientCapabilities + { + struct ChangeAnnotationSupport + { + std::optional groupsOnLabel; + }; + + boolean documentChanges; + std::optional> resourceOperations; + std::optional failureHandling; + std::optional normalizesLineEndings; + ChangeAnnotationSupport changeAnnotationSupport; + }; + + struct WorkspaceFolder + { + URI uri; + string name; + }; + + struct WorkspaceFolderServerCapabilities + { + std::optional supported; + std::optional> changeNotifications; + }; + + struct WorkspaceFoldersChangeEvent + { + std::vector added; + std::vector removed; + }; + + struct ApplyWorkspaceEditParams + { + std::optional label; + WorkspaceEdit edit; + }; + + struct ApplyWorkspaceEditResult + { + boolean applied; + std::optional failureReason; + std::optional> failedChanges; + }; + +} diff --git a/lsp-server/src/protocol/protocol.hpp b/lsp-server/src/protocol/protocol.hpp new file mode 100644 index 0000000..b2d32ca --- /dev/null +++ b/lsp-server/src/protocol/protocol.hpp @@ -0,0 +1,1274 @@ +#pragma once + +// 包含所有 LSP 协议定义 +#include +#include "./detail/basic_types.hpp" +#include "./detail/capabilities.hpp" +#include "./detail/code_actions.hpp" +#include "./detail/completion.hpp" +#include "./detail/configuration.hpp" +#include "./detail/diagnostics.hpp" +#include "./detail/document_features.hpp" +#include "./detail/document_sync.hpp" +#include "./detail/file_operations.hpp" +#include "./detail/formatting.hpp" +#include "./detail/inline_features.hpp" +#include "./detail/message.hpp" +#include "./detail/navigation.hpp" +#include "./detail/notebook.hpp" +#include "./detail/progress.hpp" +#include "./detail/registration.hpp" +#include "./detail/rename.hpp" +#include "./detail/semantic_tokens.hpp" +#include "./detail/signature_help.hpp" +#include "./detail/symbols.hpp" +#include "./detail/workspace.hpp" + +namespace glz +{ + using namespace lsp; + // 为 LSPAny 提供 glaze 支持 + template<> + struct meta + { + static constexpr std::string_view name = "LSPAny"; + using T = protocol::LSPAny; + static constexpr auto value = &T::value; + }; + + template<> + struct meta + { + using T = protocol::RequestMessage; + static constexpr auto value = glz::object( + &T::jsonrpc, + &T::id, + &T::method, + &T::params + ); + }; + + template<> + struct meta + { + using T = protocol::ResponseError; + static constexpr auto value = glz::object( + &T::jsonrpc, + &T::code, + &T::message, + &T::data + ); + }; + + template<> + struct meta + { + using T = protocol::ResponseMessage; + static constexpr auto value = glz::object( + &T::jsonrpc, + &T::id, + &T::result, + &T::error + ); + }; + + template<> + struct meta + { + using T = protocol::NotificationMessage; + static constexpr auto value = glz::object( + &T::jsonrpc, + &T::method, + &T::params + ); + }; + + template<> + struct meta + { + using T = protocol::AnnotatedTextEdit; + static constexpr auto value = glz::object( + &T::range, + &T::newText, + &T::annotationId + ); + }; + + template<> + struct meta + { + using T = protocol::InitializeParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::processId, + &T::clientInfo, + &T::locale, + &T::rootPath, + &T::rootUri, + &T::initializationOptions, + &T::capabilities, + &T::trace, + &T::workspaceFolders + ); + }; + + template<> + struct meta + { + using T = protocol::CodeActionOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::codeActionKinds, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::CodeActionRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::codeActionKinds, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::CodeActionParams; + static constexpr auto value = glz::object( + &T::textDocument, + &T::position, + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::range, + &T::context + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentColorOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentColorRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id); + }; + + template<> + struct meta + { + using T = protocol::DocumentColorParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument); + }; + + template<> + struct meta + { + using T = protocol::ColorPresentationParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::color, + &T::range); + }; + + template<> + struct meta + { + using T = protocol::CompletionOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::triggerCharacters, + &T::allCommitCharacters, + &T::resolveProvider, + &T::completionItem + ); + }; + + template<> + struct meta + { + using T = protocol::CompletionRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::triggerCharacters, + &T::allCommitCharacters, + &T::resolveProvider, + &T::completionItem + ); + }; + + template<> + struct meta + { + using T = protocol::CompletionParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position, + &T::context + ); + }; + + template<> + struct meta + { + using T = protocol::ExecuteCommandOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::commands + ); + }; + + template<> + struct meta + { + using T = protocol::ExecuteCommandRegistrationOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::commands + ); + }; + + template<> + struct meta + { + using T = protocol::ExecuteCommandParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::arguments + ); + }; + + template<> + struct meta + { + using T = protocol::DiagnosticOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::interFileDependencies, + &T::workspaceDiagnostics + ); + }; + + template<> + struct meta + { + using T = protocol::DiagnosticRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id, + &T::workDoneProgress, + &T::interFileDependencies, + &T::workspaceDiagnostics + ); + }; + + template<> + struct meta + { + using T = protocol::DiagnosticParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::identifier, + &T::previousResultId + ); + }; + + template<> + struct meta + { + using T = protocol::RelatedFullDocumentDiagnosticReport; + static constexpr auto value = glz::object( + &T::kind, + &T::resultId, + &T::items, + &T::relatedDocuments + ); + }; + + template<> + struct meta + { + using T = protocol::RelatedUnchangedDocumentDiagnosticReport; + static constexpr auto value = glz::object( + &T::kind, + &T::resultId, + &T::relatedDocuments + ); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceDiagnosticParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::identifier, + &T::previousResultIds + ); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceFullDocumentDiagnosticReport; + static constexpr auto value = glz::object( + &T::kind, + &T::resultId, + &T::items, + &T::uri, + &T::version + ); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceUnchangedDocumentDiagnosticReport; + static constexpr auto value = glz::object( + &T::kind, + &T::resultId, + &T::uri, + &T::version + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentHighlightOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentHighlightRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentHighlightParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentLinkOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentLinkRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentLinkParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument + ); + }; + + template<> + struct meta + { + using T = protocol::HoverOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::HoverRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::HoverParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::CodeLensOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::CodeLensRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::CodeLensParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument + ); + }; + + template<> + struct meta + { + using T = protocol::FoldingRangeOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::FoldingRangeRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::FoldingRangeParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument + ); + }; + + template<> + struct meta + { + using T = protocol::SelectionRangeOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::SelectionRangeRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::SelectionRangeParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::positions + ); + }; + + template<> + struct meta + { + using T = protocol::VersionedTextDocumentIdentifier; + static constexpr auto value = glz::object( + &T::uri, + &T::version + ); + }; + + template<> + struct meta + { + using T = protocol::OptionalVersionedTextDocumentIdentifier; + static constexpr auto value = glz::object( + &T::uri, + &T::version + ); + }; + + template<> + struct meta + { + using T = protocol::TextDocumentChangeRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::syncKind + ); + }; + + template<> + struct meta + { + using T = protocol::TextDocumentSaveRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::includeText + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentFormattingOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentFormattingRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentFormattingParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::options + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentRangeFormattingOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentRangeFormattingRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentRangeFormattingParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::range, + &T::options + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentOnTypeFormattingOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::firstTriggerCharacter, + &T::moreTriggerCharacter + ); + }; + + template<> + struct meta + { + using T = protocol::DocumentOnTypeFormattingRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::firstTriggerCharacter, + &T::moreTriggerCharacter + ); + }; + + template<> + struct meta + { + using T = protocol::InlayHintOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::resolveProvider + ); + }; + + template<> + struct meta + { + using T = protocol::InlayHintRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::resolveProvider, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::InlayHintParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::range + ); + }; + + template<> + struct meta + { + using T = protocol::InlineValueOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::InlineValueRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::InlineValueParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::range, + &T::context + ); + }; + + template<> + struct meta + { + using T = protocol::MonikerOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::MonikerRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::MonikerParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::DeclarationOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DeclarationRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::DeclarationParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::DefinitionOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DefinitionRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::DefinitionParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::TypeDefinitionOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::TypeDefinitionRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::TypeDefinitionParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::ImplementationOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::ImplementationRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::ImplementationParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken + ); + }; + + template<> + struct meta + { + using T = protocol::ReferenceOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::ReferenceRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::ReferenceParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position, + &T::context + ); + }; + + template<> + struct meta + { + using T = protocol::CallHierarchyOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::CallHierarchyRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::CallHierarchyParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::CallHierarchyIncomingCallsParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::item + ); + }; + + template<> + struct meta + { + using T = protocol::CallHierarchyIncomingCallsParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::item + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchyOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchyRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchyParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchyPrepareParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchySubtypesParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::item + ); + }; + + template<> + struct meta + { + using T = protocol::TypeHierarchySupertypesParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::item + ); + }; + + template<> + struct meta + { + using T = protocol::NotebookDocumentSyncRegistrationOptions; + static constexpr auto value = glz::object( + &T::notebookSelector, + &T::save, + &T::id + ); + }; + + template<> + struct meta + { + using T = protocol::RenameOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::prepareProvider + ); + }; + + template<> + struct meta + { + using T = protocol::RenameRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::prepareProvider + ); + }; + + template<> + struct meta + { + using T = protocol::RenameParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position, + &T::newName + ); + }; + + template<> + struct meta + { + using T = protocol::PrepareRenameParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position + ); + }; + + template<> + struct meta + { + using T = protocol::LinkedEditingRangeOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress + ); + }; + + template<> + struct meta + { + using T = protocol::LinkedEditingRangeRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::id); + }; + + template<> + struct meta + { + using T = protocol::LinkedEditingRangeParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::position); + }; + + template<> + struct meta + { + using T = protocol::SemanticTokensOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::legend, + &T::range, + &T::full); + }; + + template<> + struct meta + { + using T = protocol::SemanticTokensRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::legend, + &T::range, + &T::full, + &T::id); + }; + + template<> + struct meta + { + using T = protocol::SemanticTokensParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument); + }; + + template<> + struct meta + { + using T = protocol::SemanticTokensDeltaParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::previousResultId); + }; + + template<> + struct meta + { + using T = protocol::SemanticTokensRangeParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument, + &T::range); + }; + + template<> + struct meta + { + using T = protocol::SignatureHelpOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::triggerCharacters, + &T::retriggerCharacters); + }; + + template<> + struct meta + { + using T = protocol::SignatureHelpRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::triggerCharacters, + &T::retriggerCharacters); + }; + + template<> + struct meta + { + using T = protocol::SignatureHelpParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::textDocument, + &T::position, + &T::contextSupport); + }; + + template<> + struct meta + { + using T = protocol::DocumentSymbolOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::label); + }; + + template<> + struct meta + { + using T = protocol::DocumentSymbolRegistrationOptions; + static constexpr auto value = glz::object( + &T::documentSelector, + &T::workDoneProgress, + &T::label); + }; + + template<> + struct meta + { + using T = protocol::DocumentSymbolParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::textDocument); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceSymbolOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::resolveProvider); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceSymbolRegistrationOptions; + static constexpr auto value = glz::object( + &T::workDoneProgress, + &T::resolveProvider); + }; + + template<> + struct meta + { + using T = protocol::WorkspaceSymbolParams; + static constexpr auto value = glz::object( + &T::workDoneToken, + &T::partialResultToken, + &T::query); + }; + +} + diff --git a/lsp-server/src/protocol/transform/common.hpp b/lsp-server/src/protocol/transform/common.hpp new file mode 100644 index 0000000..3106b60 --- /dev/null +++ b/lsp-server/src/protocol/transform/common.hpp @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include "../detail/basic_types.hpp" + +namespace lsp::transform +{ + // 通用错误类 + class ConversionError : public std::runtime_error + { + public: + explicit ConversionError(const std::string& message) + : std::runtime_error("LSP Conversion Error: " + message) {} + }; + + template + struct is_lsp_basic_type: std::false_type {}; + + template<> struct is_lsp_basic_type : std::true_type {}; + template<> struct is_lsp_basic_type : std::true_type {}; + template<> struct is_lsp_basic_type : std::true_type {}; + template<> struct is_lsp_basic_type : std::true_type {}; + + template + struct is_lsp_container_type : std::false_type {}; + + template<> struct is_lsp_container_type : std::true_type {}; + template<> struct is_lsp_container_type : std::true_type {}; + template<> struct is_lsp_container_type : std::true_type {}; + + // struct类型判断 + template + struct is_user_struct : std::integral_constant::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !is_lsp_basic_type::value && + !is_lsp_container_type::value> {}; +} diff --git a/lsp-server/src/protocol/transform/facade.hpp b/lsp-server/src/protocol/transform/facade.hpp new file mode 100644 index 0000000..b04851c --- /dev/null +++ b/lsp-server/src/protocol/transform/facade.hpp @@ -0,0 +1,93 @@ +#pragma once +#include "./transformer.hpp" + +namespace lsp::transform +{ + + // ===== 全局便利函数 ===== + // 基本类型 + template + protocol::LSPAny LSPAny(const T& obj); + + // // struct 类型 + // template + // typename std::enable_if::value, protocol::LSPAny>::type LSPAny(const T& value); + + // 容器类型 + template + protocol::LSPAny LSPAny(const std::vector& vec); + + template + protocol::LSPAny LSPAny(const std::map& map); + + template + protocol::LSPAny LSPAny(const std::optional& opt); + + template + T As(const protocol::LSPAny& any); + + template + std::optional As(const std::optional& opt); + + template + T As(const std::variant& var); + + template + T As(const protocol::LSPObject& obj); + + template + T As(const protocol::LSPArray& arr); + + template + T Number(const protocol::LSPAny& any); + + template + std::vector Vector(const protocol::LSPAny& any); + + template + std::optional Optional(const protocol::LSPAny& any); + + template + protocol::LSPObject LSPObject(const T& obj); + + template + protocol::LSPArray LSPArray(const T& vec); + + protocol::string String(const protocol::LSPAny& any); + protocol::boolean Bool(const protocol::LSPAny& any); + + // === 外观接口:类型检查 === + namespace check + { + inline bool IsObject(const protocol::LSPAny& any) { return any.is(); } + inline bool IsArray(const protocol::LSPAny& any) { return any.is(); } + inline bool IsString(const protocol::LSPAny& any) { return any.is(); } + inline bool IsNumber(const protocol::LSPAny& any) { return any.is(); } + inline bool IsBool(const protocol::LSPAny& any) { return any.is(); } + inline bool IsNull(const protocol::LSPAny& any) { return any.is(); } + } + + // === 外观接口:调试功能 === + + namespace debug + { + inline std::string GetTypeName(const protocol::LSPAny& any) + { + if (any.is()) + return "LSPObject"; + if (any.is()) + return "LSPArray"; + if (any.is()) + return "string"; + if (any.is()) + return "decimal"; + if (any.is()) + return "boolean"; + if (any.is()) + return "null"; + return "unknown"; + } + } +} + +#include "./facade.inl" diff --git a/lsp-server/src/protocol/transform/facade.inl b/lsp-server/src/protocol/transform/facade.inl new file mode 100644 index 0000000..bac705d --- /dev/null +++ b/lsp-server/src/protocol/transform/facade.inl @@ -0,0 +1,120 @@ +#pragma once +#include "./facade.hpp" + +namespace lsp::transform +{ + template + inline protocol::LSPAny LSPAny(const T& obj) + { + return LSPAnyConverter::ToLSPAny(obj); + } + + template + inline protocol::LSPAny LSPAny(const std::vector& vec) + { + return LSPAnyConverter::ToLSPAny(vec); + } + + template + inline protocol::LSPAny LSPAny(const std::map& map) + { + return LSPAnyConverter::ToLSPAny(map); + } + + template + inline protocol::LSPAny LSPAny(const std::optional& opt) + { + return LSPAnyConverter::ToLSPAny(opt); + } + + template + inline T As(const protocol::LSPAny& any) + { + return LSPAnyConverter::FromLSPAny(any); + } + + template + inline std::optional As(const std::optional& opt) + { + return LSPAnyConverter::As(opt); + } + + template + inline T As(const std::variant& var) + { + return LSPAnyConverter::As(var); + } + + template + inline T As(const protocol::LSPObject& obj) + { + return LSPAnyConverter::As(obj); + } + + template + inline T As(const protocol::LSPArray& arr) + { + return LSPAnyConverter::As(arr); + } + + template + inline T Number(const protocol::LSPAny& any) + { + return LSPAnyConverter::ToNumber(any); + } + + template + inline std::vector Vector(const protocol::LSPAny& any) + { + return LSPAnyConverter::ToVector(any); + } + + template + inline std::optional Optional(const protocol::LSPAny& any) + { + return LSPAnyConverter::ToOptional(any); + } + + template + inline protocol::LSPObject LSPObject(const T& obj) + { + // 如果已经是 LSPAny,直接获取其中的 LSPObject + if constexpr (std::is_same_v) + { + return LSPAnyConverter::ToLSPObject(obj); + } + else + { + // 否则先转换为 LSPAny,再获取 LSPObject + protocol::LSPAny any = LSPAnyConverter::ToLSPAny(obj); + return LSPAnyConverter::ToLSPObject(any); + } + } + + template + inline protocol::LSPArray LSPArray(const T& container) + { + // 如果已经是 LSPAny,直接获取其中的 LSPArray + if constexpr (std::is_same_v) + { + return LSPAnyConverter::ToLSPArray(container); + } + else + { + // 否则转换为 LSPAny + protocol::LSPAny any = LSPAnyConverter::ToLSPAny(container); + return LSPAnyConverter::ToLSPArray(any); + } + } + + inline protocol::string String(const protocol::LSPAny& any) + { + return LSPAnyConverter::ToString(any); + } + + inline protocol::boolean Bool(const protocol::LSPAny& any) + { + return LSPAnyConverter::ToBool(any); + } + +} diff --git a/lsp-server/src/protocol/transform/transformer.hpp b/lsp-server/src/protocol/transform/transformer.hpp new file mode 100644 index 0000000..0ffc514 --- /dev/null +++ b/lsp-server/src/protocol/transform/transformer.hpp @@ -0,0 +1,85 @@ +#pragma once +#include "../protocol.hpp" +#include "./common.hpp" + +namespace lsp::transform +{ + // LSPAny 序列化/反序列化工具类 + class LSPAnyConverter + { + public: + // === 基本类型的转换 === + static protocol::LSPAny ToLSPAny(protocol::boolean value); + static protocol::LSPAny ToLSPAny(int value); + static protocol::LSPAny ToLSPAny(long value); + static protocol::LSPAny ToLSPAny(long long value); + static protocol::LSPAny ToLSPAny(unsigned int value); + static protocol::LSPAny ToLSPAny(unsigned long value); + static protocol::LSPAny ToLSPAny(unsigned long long value); + static protocol::LSPAny ToLSPAny(float value); + static protocol::LSPAny ToLSPAny(double value); + static protocol::LSPAny ToLSPAny(long double value); + static protocol::LSPAny ToLSPAny(const protocol::string& str); + static protocol::LSPAny ToLSPAny(const char* str); + static protocol::LSPAny ToLSPAny(std::nullptr_t); + static protocol::LSPAny ToLSPAny(const protocol::LSPAny& any); + + // === LSPAny 到基本类型的转换 === + static protocol::boolean ToBool(const protocol::LSPAny& any); + static protocol::string ToString(const protocol::LSPAny& any); + + // 其他转换 + static protocol::LSPObject ToLSPObject(const protocol::LSPAny& any); + static protocol::LSPArray ToLSPArray(const protocol::LSPAny& any); + + // === 容器类型的转换 === + template + static protocol::LSPAny ToLSPAny(const std::vector& vec); + + template + static protocol::LSPAny ToLSPAny(const std::map& map); + + template + static protocol::LSPAny ToLSPAny(const std::optional& opt); + + // === Struct 到 LSPAny 的转换 === + template + static typename std::enable_if::value, protocol::LSPAny>::type ToLSPAny(const T& obj); + + template + static typename std::enable_if::value && !std::is_same::value, T>::type ToNumber(const protocol::LSPAny& any); + + // === LSPAny 到容器类型的转换 === + template + static std::vector ToVector(const protocol::LSPAny& any); + + template + static std::optional ToOptional(const protocol::LSPAny& any); + + template + static typename std::enable_if::value, T>::type FromLSPAny(const protocol::LSPAny& any); + + // 处理 std::optional + template + static std::optional As(const std::optional& opt); + + // 处理 std::variant + template + static T As(const std::variant& var); + + // 处理 LSPObject 和 LSPArray + template + static T As(const protocol::LSPObject& obj); + + template + static T As(const protocol::LSPArray& arr); + + + private: + template + static T FromLSPAnyImpl(const protocol::LSPAny& any); + }; + +} + +#include "./transformer.inl" diff --git a/lsp-server/src/protocol/transform/transformer.inl b/lsp-server/src/protocol/transform/transformer.inl new file mode 100644 index 0000000..64b9821 --- /dev/null +++ b/lsp-server/src/protocol/transform/transformer.inl @@ -0,0 +1,259 @@ +#pragma once +#include "./transformer.hpp" + +namespace lsp::transform +{ + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(protocol::boolean value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(int value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long long value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned int value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned long value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned long long value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(float value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(double value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long double value) + { + return protocol::LSPAny(value); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const protocol::string& str) + { + return protocol::LSPAny(str); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const char* str) + { + return protocol::LSPAny(str); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(std::nullptr_t) + { + return protocol::LSPAny(nullptr); + } + + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const protocol::LSPAny& any) + { + return any; + } + + inline protocol::boolean LSPAnyConverter::ToBool(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain a boolean"); + return any.get(); + } + + inline protocol::string LSPAnyConverter::ToString(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain a string"); + return any.get(); + } + + inline protocol::LSPObject LSPAnyConverter::ToLSPObject(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain LSPObject"); + return any.get(); + } + + inline protocol::LSPArray LSPAnyConverter::ToLSPArray(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain LSPArray"); + return any.get(); + } + + // 模板实现 + template + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::vector& vec) + { + protocol::LSPArray arr; + arr.reserve(vec.size()); + for (const auto& item : vec) + arr.push_back(ToLSPAny(item)); + return protocol::LSPAny(std::move(arr)); + } + + template + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::map& map) + { + protocol::LSPObject obj; + for (const auto& [key, value] : map) + obj[key] = ToLSPAny(value); + return protocol::LSPAny(std::move(obj)); + } + + template + inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::optional& opt) + { + if (opt.has_value()) + return ToLSPAny(*opt); + return protocol::LSPAny(nullptr); + } + + template + inline typename std::enable_if::value, protocol::LSPAny>::type + LSPAnyConverter::ToLSPAny(const T& obj) + { + try + { + // 使用glaze序列化为JSON字符串 + std::string json; + auto ec = glz::write_json(obj, json); + if (ec) + { + throw ConversionError("Failed to serialize struct to JSON: " + std::string(glz::format_error(ec, json))); + } + + // 直接解析为LSPAny + protocol::LSPAny result; + ec = glz::read_json(result, json); + if (ec) + { + throw ConversionError("Failed to parse JSON to LSPAny: " + std::string(glz::format_error(ec, json))); + } + + return result; + } + catch (const std::exception& e) + { + throw ConversionError("struct to LSPAny conversion failed: " + std::string(e.what())); + } + } + + // Fromprotocol::LSPAny 实现 + template + inline typename std::enable_if::value && !std::is_same::value, T>::type LSPAnyConverter::ToNumber(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain a number"); + return static_cast(any.get()); + } + + template + inline std::vector LSPAnyConverter::ToVector(const protocol::LSPAny& any) + { + if (!any.is()) + throw ConversionError("LSPAny does not contain an array"); + + const auto& arr = any.get(); + std::vector result; + result.reserve(arr.size()); + for (const auto& item : arr) + result.push_back(FromLSPAny(item)); + return result; + } + + template + inline std::optional LSPAnyConverter::ToOptional(const protocol::LSPAny& any) + { + if (any.is()) + return std::nullopt; + return FromLSPAny(any); + } + + template + inline typename std::enable_if::value, T>::type + LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any) + { + return FromLSPAnyImpl(any); + } + + // 处理 std::optional + template + inline std::optional LSPAnyConverter::As(const std::optional& opt) + { + if (opt.has_value()) + return As(*opt); + return std::nullopt; + } + + // 处理 std::variant + template + inline T LSPAnyConverter::As(const std::variant& var) + { + return std::visit([](const auto& val) -> T { + return As(val); + }, var); + } + + // 处理 LSPObject + template + inline T LSPAnyConverter::As(const protocol::LSPObject& obj) + { + protocol::LSPAny any(obj); + return FromLSPAny(any); + } + + // 处理 LSPArray + template + inline T LSPAnyConverter::As(const protocol::LSPArray& arr) + { + protocol::LSPAny any(arr); + return FromLSPAny(any); + } + + // FromLSPAnyImpl 实现 + template + inline T LSPAnyConverter::FromLSPAnyImpl(const protocol::LSPAny& any) + { + try + { + // 序列化LSPAny为JSON + std::string json; + auto ec = glz::write_json(any.value, json); + if (ec) + throw ConversionError("Failed to serialize LSPAny to JSON: " + std::string(glz::format_error(ec, json))); + + // 解析JSON到目标类型 + T result; + ec = glz::read_json(result, json); + if (ec) + throw ConversionError("Failed to parse JSON to target type: " + std::string(glz::format_error(ec, json))); + return result; + } + catch (const std::exception& e) + { + throw ConversionError("LSPAny to struct conversion failed: " + std::string(e.what())); + } + } + +} diff --git a/lsp-server/src/provider/base/provider_interface.cpp b/lsp-server/src/provider/base/provider_interface.cpp new file mode 100644 index 0000000..22b0e64 --- /dev/null +++ b/lsp-server/src/provider/base/provider_interface.cpp @@ -0,0 +1,23 @@ +#include +#include "./provider_interface.hpp" + +namespace lsp::providers +{ + + std::string ILspProvider::BuildErrorMessageResponse(protocol::ErrorCode code, const std::string& message) + { + protocol::ResponseError error; + error.code = code; + error.message = message; + std::string json; + auto ec = glz::write_json(error, json); + if (ec) + { + spdlog::error("{}: Error", GetProviderName()); + std::string errmsg = "Failed to serialize [" + GetProviderName() + "] error response: " + glz::format_error(ec); + throw std::runtime_error(errmsg); + } + return json; + } + +} diff --git a/lsp-server/src/provider/base/provider_interface.hpp b/lsp-server/src/provider/base/provider_interface.hpp index eb5ab27..3881cac 100644 --- a/lsp-server/src/provider/base/provider_interface.hpp +++ b/lsp-server/src/provider/base/provider_interface.hpp @@ -1,7 +1,6 @@ - #pragma once #include -#include "../../lsp/lsp_types.hpp" +#include "../../protocol/protocol.hpp" namespace lsp::providers { @@ -13,11 +12,14 @@ namespace lsp::providers virtual ~ILspProvider() = default; // 处理LSP请求 - virtual nlohmann::json ProvideResponse(const LspRequest& request) = 0; + virtual std::string ProvideResponse(const protocol::RequestMessage& request) = 0; // 获取支持的LSP方法名 virtual std::string GetMethod() const = 0; // 获取提供者名称(用于日志和调试) virtual std::string GetProviderName() const = 0; + + protected: + std::string BuildErrorMessageResponse(protocol::ErrorCode code, const std::string& message); }; } diff --git a/lsp-server/src/provider/base/provider_registry.hpp b/lsp-server/src/provider/base/provider_registry.hpp index 34bde18..2b5d7b0 100644 --- a/lsp-server/src/provider/base/provider_registry.hpp +++ b/lsp-server/src/provider/base/provider_registry.hpp @@ -19,7 +19,7 @@ namespace lsp::providers dispatcher.RegisterProvider( provider->GetMethod(), - [provider](const LspRequest& request) -> nlohmann::json { + [provider](const protocol::RequestMessage& request) -> std::string { return provider->ProvideResponse(request); }); } diff --git a/lsp-server/src/provider/initialize/initialize_provider.cpp b/lsp-server/src/provider/initialize/initialize_provider.cpp index 2757034..d08c4d9 100644 --- a/lsp-server/src/provider/initialize/initialize_provider.cpp +++ b/lsp-server/src/provider/initialize/initialize_provider.cpp @@ -1,55 +1,44 @@ #include #include "./initialize_provider.hpp" +#include "../../protocol/transform/facade.hpp" namespace lsp::providers::initialize { - - nlohmann::json InitializeProvider::ProvideResponse(const LspRequest& request) + std::string InitializeProvider::ProvideResponse(const protocol::RequestMessage& request) { spdlog::debug("InitializeProvider: Providing response for method {}", request.method); - nlohmann::json response; - response["jsonrpc"] = "2.0"; - response["id"] = request.id; - response["result"] = BuildInitializeResult(); - return response; + protocol::ResponseMessage response; + response.id = request.id; + response.result = transform::LSPAny(BuildInitializeResult()); + std::string json; + auto ec = glz::write_json(response, json); + return ec ? BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error") : json; } - inline std::string InitializeProvider::GetMethod() const + std::string InitializeProvider::GetMethod() const { return "initialize"; } - inline std::string InitializeProvider::GetProviderName() const + std::string InitializeProvider::GetProviderName() const { return "InitializeProvider"; } - nlohmann::json InitializeProvider::BuildInitializeResult() + protocol::InitializeResult InitializeProvider::BuildInitializeResult() { - nlohmann::json result; - result["capabilities"] = BuildServerCapabilities(); - result["serverInfo"] = BuildServerInfo(); + protocol::InitializeResult result; + result.serverInfo.name = "TSL Language Server"; + result.serverInfo.version = "1.0.0"; + protocol::TextDocumentSyncOptions opts; + opts.openClose = true; + opts.change = protocol::TextDocumentSyncKind::kIncremental; + protocol::CompletionOptions completion_provider; + completion_provider.resolveProvider = false; + + result.capabilities.textDocumentSync = opts; + result.capabilities.completionProvider = completion_provider; return result; } - nlohmann::json InitializeProvider::BuildServerCapabilities() - { - nlohmann::json capabilities; - capabilities["textDocumentSync"] = nlohmann::json(); - capabilities["textDocumentSync"]["change"] = 2; - capabilities["textDocumentSync"]["openClose"] = true; - capabilities["textDocumentSync"]["save"] = true; - capabilities["completionProvider"] = nlohmann::json(); - capabilities["completionProvider"]["resolveProvider"] = false; - return capabilities; - } - - nlohmann::json InitializeProvider::BuildServerInfo() - { - nlohmann::json serverInfo; - serverInfo["name"] = "TSL Language Server"; - serverInfo["version"] = "1.0.0"; - return serverInfo; - } - } diff --git a/lsp-server/src/provider/initialize/initialize_provider.hpp b/lsp-server/src/provider/initialize/initialize_provider.hpp index 79f2904..2f9d55c 100644 --- a/lsp-server/src/provider/initialize/initialize_provider.hpp +++ b/lsp-server/src/provider/initialize/initialize_provider.hpp @@ -1,20 +1,18 @@ #pragma once #include "../base/provider_interface.hpp" -#include namespace lsp::providers::initialize { + using namespace lsp; class InitializeProvider : public ILspProvider { public: InitializeProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; private: - nlohmann::json BuildServerCapabilities(); - nlohmann::json BuildServerInfo(); - nlohmann::json BuildInitializeResult(); + protocol::InitializeResult BuildInitializeResult(); }; } diff --git a/lsp-server/src/provider/initialized/initialized_provider.cpp b/lsp-server/src/provider/initialized/initialized_provider.cpp index 83e79b2..8974b4d 100644 --- a/lsp-server/src/provider/initialized/initialized_provider.cpp +++ b/lsp-server/src/provider/initialized/initialized_provider.cpp @@ -4,18 +4,21 @@ namespace lsp::providers::initialized { - nlohmann::json InitializedProvider::ProvideResponse(const LspRequest& request) + std::string InitializedProvider::ProvideResponse(const protocol::RequestMessage& request) { spdlog::debug("InitializeProvider: Providing response for method {}", request.method); - return nlohmann::json(); + std::string json; + glz::obj empty_obj{}; // glaze的对象类型 + auto ec = glz::write_json(empty_obj, json); + return ec ? BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error") : json; } - inline std::string InitializedProvider::GetMethod() const + std::string InitializedProvider::GetMethod() const { return "initialized"; } - inline std::string InitializedProvider::GetProviderName() const + std::string InitializedProvider::GetProviderName() const { return "InitializedProvider"; } diff --git a/lsp-server/src/provider/initialized/initialized_provider.hpp b/lsp-server/src/provider/initialized/initialized_provider.hpp index 6e8e5f5..b68a1b6 100644 --- a/lsp-server/src/provider/initialized/initialized_provider.hpp +++ b/lsp-server/src/provider/initialized/initialized_provider.hpp @@ -7,7 +7,7 @@ namespace lsp::providers::initialized { public: InitializedProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; }; diff --git a/lsp-server/src/provider/text_document/completion_provider.cpp b/lsp-server/src/provider/text_document/completion_provider.cpp index 957790a..92ea41d 100644 --- a/lsp-server/src/provider/text_document/completion_provider.cpp +++ b/lsp-server/src/provider/text_document/completion_provider.cpp @@ -1,211 +1,159 @@ #include #include "./completion_provider.hpp" +#include "../../protocol/transform/facade.hpp" namespace lsp::providers::text_document { - nlohmann::json CompletionProvider::ProvideResponse(const LspRequest& request) + std::string CompletionProvider::ProvideResponse(const protocol::RequestMessage& request) { spdlog::debug("CompletionProvider: Providing response for method {}", request.method); - try - { - nlohmann::json response = BuildCompletionResponse(request); - return response; - } - catch (const std::exception& e) - { - spdlog::error("{}: Error - ", GetProviderName(), e.what()); - nlohmann::json errorResponse = CreateErrorResponse(request.id, -32603, e.what()); - return errorResponse; + + try { + // 验证请求是否包含参数 + if (!request.params.has_value()) { + spdlog::warn("{}: Missing params in request", GetProviderName()); + return BuildErrorMessageResponse(protocol::ErrorCode::kInvalidParams, "Missing params"); + } + + // 从 variant 中提取参数 + protocol::CompletionParams completion_params = transform::As(request.params.value()); + protocol::CompletionList completion_list = BuildCompletionResponse(completion_params); + + // 构建响应消息 + protocol::ResponseMessage response; + response.id = request.id; + response.result = transform::LSPAny(completion_list); + + std::string json; + auto ec = glz::write_json(response, json); + if (ec) { + spdlog::error("{}: Failed to serialize response: {}", GetProviderName(), glz::format_error(ec, json)); + return BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Failed to serialize response"); + } + + return json; + + } catch (const transform::ConversionError& e) { + spdlog::error("{}: Failed to convert params: {}", GetProviderName(), e.what()); + return BuildErrorMessageResponse(protocol::ErrorCode::kInvalidParams, "Invalid completion params"); + } catch (const std::exception& e) { + spdlog::error("{}: Unexpected error: {}", GetProviderName(), e.what()); + return BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error"); } } - inline std::string CompletionProvider::GetMethod() const + std::string CompletionProvider::GetMethod() const { return "textDocument/completion"; } - inline std::string CompletionProvider::GetProviderName() const + std::string CompletionProvider::GetProviderName() const { return "CompletionProvider"; } - nlohmann::json CompletionProvider::BuildCompletionResponse(const LspRequest& request) + protocol::CompletionList CompletionProvider::BuildCompletionResponse(const protocol::CompletionParams& params) { - nlohmann::json response; - response["jsonrpc"] = "2.0"; - response["id"] = request.id; + spdlog::trace("{}: Processing completion request for URI='{}', Position=({}, {})", + GetProviderName(), + params.textDocument.uri, + params.position.line, + params.position.character); - // 验证必要参数 - if (!request.params.contains("textDocument") || !request.params.contains("position")) + // 获取补全前缀 + std::string prefix = ExtractPrefix(params.textDocument, params.position); + + // 如果提供了 context,可以使用其中的信息 + if (params.context.has_value()) { - spdlog::warn("{}: Missing required parameters in request", GetProviderName()); - // 返回空补全列表而非错误 - response["result"] = BuildCompletionResult({}); - return response; + spdlog::trace("{}: Trigger kind: {}", GetProviderName(), static_cast(params.context->triggerKind)); + if (params.context->triggerCharacter.has_value()) + spdlog::trace("{}: Trigger character: '{}'", GetProviderName(), params.context->triggerCharacter.value()); } - // 提取参数 - std::string uri = ExtractDocumentUri(request.params); - nlohmann::json position = ExtractPosition(request.params); - std::string prefix = ExtractPrefix(request.params); - - spdlog::trace("{}: Processing completion request for URI='{}', Position={}, prefix='{}'", GetProviderName(), uri, position.dump(), prefix); - // 收集所有补全项 - std::vector allItems; + std::vector allItems; // 添加关键字补全 auto keywordItems = ProvideKeywordCompletions(prefix); allItems.insert(allItems.end(), keywordItems.begin(), keywordItems.end()); // 添加上下文相关补全 - auto contextualItems = ProvideContextualCompletions(uri, position, prefix); + auto contextualItems = ProvideContextualCompletions(params.textDocument, params.position, prefix); allItems.insert(allItems.end(), contextualItems.begin(), contextualItems.end()); - // 构建响应 - response["result"] = BuildCompletionResult(allItems); + // 构建补全列表 + protocol::CompletionList result; + result.isIncomplete = false; // 表示这是完整的补全列表 + result.items = std::move(allItems); - spdlog::info("{}: Provided {}", GetProviderName(), allItems.size()); - - return response; - } - - nlohmann::json CompletionProvider::BuildCompletionResult(const std::vector& items) - { - nlohmann::json result; - result["isIncomplete"] = false; // 表示这是完整的补全列表 - result["items"] = nlohmann::json::array(); - - for (const auto& item : items) - result["items"].push_back(CompletionItemToJson(item)); + spdlog::info("{}: Provided {} completion items", GetProviderName(), result.items.size()); return result; } - std::string CompletionProvider::ExtractDocumentUri(const nlohmann::json& params) + std::string CompletionProvider::ExtractPrefix(const protocol::TextDocumentIdentifier& textDocument, const protocol::Position& position) { - if (params.contains("textDocument") && params["textDocument"].contains("uri")) - { - std::string uri = params["textDocument"]["uri"].get(); - spdlog::trace("ExtractDocumentUri: Found URI: ", uri); - return uri; - } - spdlog::warn("ExtractDocumentUri: No URI found in parameters"); + // TODO: 实现从文档内容和位置计算前缀 + // 这需要访问文档管理器来获取文档内容 + // 现在返回空字符串 + spdlog::trace("{}: ExtractPrefix not implemented, returning empty string", GetProviderName()); return ""; } - nlohmann::json CompletionProvider::ExtractPosition(const nlohmann::json& params) + std::vector CompletionProvider::ProvideKeywordCompletions(const std::string& prefix) { - if (params.contains("position")) - { - nlohmann::json pos = params["position"]; - spdlog::trace("ExtractPosition: Found position: ", pos.dump()); - return pos; - } - // 返回默认位置 - nlohmann::json defaultPos; - defaultPos["line"] = 0; - defaultPos["character"] = 0; - spdlog::warn("ExtractPosition: No position found in parameters, using default (0, 0)"); - return defaultPos; - } + std::vector items; - std::string CompletionProvider::ExtractPrefix(const nlohmann::json& params) - { - // 方法1: 直接从参数中获取prefix - if (params.contains("prefix")) - { - std::string prefix = params["prefix"].get(); - spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'"); - return prefix; - } - - // 方法2: 从context中获取prefix - if (params.contains("context") && params["context"].contains("prefix")) - { - std::string prefix = params["context"]["prefix"].get(); - spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'"); - return prefix; - } - - // TODO: 理想情况下,应该从文档内容和位置计算前缀 - // 这需要维护文档内容的状态 - spdlog::trace("ExtractPrefix: No prefix found, returning empty string"); - return ""; - } - - std::vector CompletionProvider::ProvideKeywordCompletions(const std::string& prefix) - { - std::vector items; - - // 从tsl_keywords_获取补全项 + // 从 tsl_keywords_ 获取补全项 auto tslItems = tsl_keywords_.GetCompletionItems(prefix); - for (const auto& tslItem : tslItems) - { - CompletionItem item; + for (const auto& tslItem : tslItems) { + protocol::CompletionItem item; item.label = tslItem.label; - item.kind = CompletionItemKind::kKeyword; // LSP CompletionItemKind.Keyword + item.kind = protocol::CompletionItemKind::kKeyword; item.detail = "TSL Keyword"; - item.documentation = "TSL language keyword"; - item.insert_text = tslItem.label; + + // 创建文档内容 + protocol::MarkupContent documentation; + documentation.kind = protocol::MarkupKindLiterals::PlainText; + documentation.value = "TSL language keyword"; + item.documentation = documentation; + + item.insertText = tslItem.label; items.push_back(item); } - spdlog::debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions"); + + spdlog::debug("{}: Found {} keyword completions", GetProviderName(), items.size()); return items; } - std::vector CompletionProvider::ProvideContextualCompletions( - const std::string& uri, - const nlohmann::json& position, - const std::string& prefix) + std::vector CompletionProvider::ProvideContextualCompletions(const protocol::TextDocumentIdentifier& textDocument, const protocol::Position& position, const std::string& prefix) { - spdlog::debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri); - std::vector items; + spdlog::debug("{}: Processing contextual completions for URI: {}", GetProviderName(), textDocument.uri); + + std::vector items; // TODO: 基于上下文提供补全 - // 这里可以添加: - // - 变量名补全 - // - 函数名补全 - // - 类型补全 - // - 属性补全等 - spdlog::debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions"); + // 示例:添加一个变量补全 + if (!prefix.empty() && prefix[0] == '$') + { + protocol::CompletionItem varItem; + varItem.label = "$myVariable"; + varItem.kind = protocol::CompletionItemKind::kVariable; + varItem.detail = "Local variable"; + varItem.insertText = "$myVariable"; + + protocol::MarkupContent doc; + doc.kind = protocol::MarkupKindLiterals::Markdown; + doc.value = "Example variable completion"; + varItem.documentation = doc; + + items.push_back(varItem); + } + + spdlog::debug("{}: Found {} contextual completions", GetProviderName(), items.size()); return items; } - - nlohmann::json CompletionProvider::CompletionItemToJson(const CompletionItem& item) - { - nlohmann::json json; - - json["label"] = item.label; - json["kind"] = item.kind; - - if (!item.detail) - json["detail"] = item.detail; - - if (!item.documentation) - json["documentation"] = item.documentation; - - if (!item.insert_text) - json["insertText"] = item.insert_text; - - return json; - } - - nlohmann::json CompletionProvider::CreateErrorResponse(const nlohmann::json& id, int code, const std::string& message) - { - nlohmann::json response; - response["jsonrpc"] = "2.0"; - response["id"] = id; - - nlohmann::json error; - error["code"] = code; - error["message"] = GetProviderName() + ": " + message; - - response["error"] = error; - spdlog::error("CreateErrorResponse: Created error response with code {} and message {}", code, message); - return response; - } - } diff --git a/lsp-server/src/provider/text_document/completion_provider.hpp b/lsp-server/src/provider/text_document/completion_provider.hpp index 5ce8ead..79f1dfb 100644 --- a/lsp-server/src/provider/text_document/completion_provider.hpp +++ b/lsp-server/src/provider/text_document/completion_provider.hpp @@ -2,7 +2,7 @@ #include #include #include "../base/provider_interface.hpp" -#include "../../lsp/lsp_types.hpp" +#include "../../protocol/protocol.hpp" #include "../../language/tsl_keywords.hpp" namespace lsp::providers::text_document @@ -12,36 +12,20 @@ namespace lsp::providers::text_document public: CompletionProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; private: // 构建完整的补全响应 - nlohmann::json BuildCompletionResponse(const LspRequest& request); + protocol::CompletionList BuildCompletionResponse(const protocol::CompletionParams& params); - // 构建补全结果 - nlohmann::json BuildCompletionResult(const std::vector& items); - - // 从请求中提取文档信息 - std::string ExtractDocumentUri(const nlohmann::json& params); - nlohmann::json ExtractPosition(const nlohmann::json& params); - - // 获取补全前缀 - std::string ExtractPrefix(const nlohmann::json& params); + // 获取补全前缀(从文档内容和位置计算) + std::string ExtractPrefix(const protocol::TextDocumentIdentifier& textDocument, const protocol::Position& position); // 提供不同类型的补全 - std::vector ProvideKeywordCompletions(const std::string& prefix); - std::vector ProvideContextualCompletions( - const std::string& uri, - const nlohmann::json& position, - const std::string& prefix); - - // 将CompletionItem转换为JSON - nlohmann::json CompletionItemToJson(const CompletionItem& item); - - // 创建错误响应 - nlohmann::json CreateErrorResponse(const nlohmann::json& id, int code, const std::string& message); + std::vector ProvideKeywordCompletions(const std::string& prefix); + std::vector ProvideContextualCompletions(const protocol::TextDocumentIdentifier& textDocument, const protocol::Position& position, const std::string& prefix); private: tsl::TslKeywords tsl_keywords_; diff --git a/lsp-server/src/provider/text_document/did_change_provider.cpp b/lsp-server/src/provider/text_document/did_change_provider.cpp index 21ca550..41bb0c0 100644 --- a/lsp-server/src/provider/text_document/did_change_provider.cpp +++ b/lsp-server/src/provider/text_document/did_change_provider.cpp @@ -5,59 +5,23 @@ namespace lsp::providers::text_document { - nlohmann::json DidChangeProvider::ProvideResponse(const LspRequest& request) + std::string DidChangeProvider::ProvideResponse(const protocol::RequestMessage& request) { spdlog::debug("DidChangeProvider: Providing response for method {}", request.method); - try - { - auto params = request.params; - if (params.contains("textDocument") && params.contains("contentChanges")) - { - auto textDoc = params["textDocument"]; - std::string uri = textDoc["uri"]; - auto changes = params["contentChanges"]; - - ApplyContentChanges(uri, changes); - } - } - catch (const std::exception& e) - { - // 处理错误,但不返回错误响应,因为这是通知 - } - - // 通知不需要响应 - return nlohmann::json(); + std::string json; + glz::obj empty_obj{}; // glaze的对象类型 + auto ec = glz::write_json(empty_obj, json); + return ec ? BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error") : json; } - inline std::string DidChangeProvider::GetMethod() const + std::string DidChangeProvider::GetMethod() const { return "textDocument/didChange"; } - inline std::string DidChangeProvider::GetProviderName() const + std::string DidChangeProvider::GetProviderName() const { return "DidChangeProvider"; } - void DidChangeProvider::ApplyContentChanges(const std::string& uri, const nlohmann::json& changes) - { - // 简化实现:假设是全文替换 - for (const auto& change : changes) - { - if (change.contains("text")) - { - // 如果没有range,表示全文替换 - if (!change.contains("range")) - { - DidOpenProvider::document_store[uri] = change["text"]; - } - else - { - // 这里可以实现增量更新,现在简化为全文替换 - DidOpenProvider::document_store[uri] = change["text"]; - } - } - } - } - } diff --git a/lsp-server/src/provider/text_document/did_change_provider.hpp b/lsp-server/src/provider/text_document/did_change_provider.hpp index 57f1d39..dd9bf25 100644 --- a/lsp-server/src/provider/text_document/did_change_provider.hpp +++ b/lsp-server/src/provider/text_document/did_change_provider.hpp @@ -7,11 +7,8 @@ namespace lsp::providers::text_document { public: DidChangeProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; - - private: - void ApplyContentChanges(const std::string& uri, const nlohmann::json& changes); }; } diff --git a/lsp-server/src/provider/text_document/did_open_provider.cpp b/lsp-server/src/provider/text_document/did_open_provider.cpp index c64ed8d..452feab 100644 --- a/lsp-server/src/provider/text_document/did_open_provider.cpp +++ b/lsp-server/src/provider/text_document/did_open_provider.cpp @@ -3,46 +3,23 @@ namespace lsp::providers::text_document { - std::unordered_map DidOpenProvider::document_store; - - nlohmann::json DidOpenProvider::ProvideResponse(const LspRequest& request) + std::string DidOpenProvider::ProvideResponse(const protocol::RequestMessage& request) { spdlog::debug("DidOpenProvider: Providing response for method {}", request.method); - try - { - auto params = request.params; - if (params.contains("textDocument")) - { - auto textDoc = params["textDocument"]; - std::string uri = textDoc["uri"]; - std::string text = textDoc["text"]; - - // 存储文档内容 - document_store[uri] = text; - } - } - catch (const std::exception& e) - { - // 处理错误,但不返回错误响应,因为这是通知 - } - // 通知不需要响应 - return nlohmann::json(); + std::string json; + glz::obj empty_obj{}; // glaze的对象类型 + auto ec = glz::write_json(empty_obj, json); + return ec ? BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error") : json; } - inline std::string DidOpenProvider::GetMethod() const + std::string DidOpenProvider::GetMethod() const { return "textDocument/didOpen"; } - inline std::string DidOpenProvider::GetProviderName() const + std::string DidOpenProvider::GetProviderName() const { return "DidOpenProvider"; } - std::string DidOpenProvider::GetDocumentContent(const std::string& uri) - { - auto it = document_store.find(uri); - return (it != document_store.end()) ? it->second : ""; - } - } diff --git a/lsp-server/src/provider/text_document/did_open_provider.hpp b/lsp-server/src/provider/text_document/did_open_provider.hpp index 9345c31..b366e3c 100644 --- a/lsp-server/src/provider/text_document/did_open_provider.hpp +++ b/lsp-server/src/provider/text_document/did_open_provider.hpp @@ -8,14 +8,8 @@ namespace lsp::providers::text_document { public: DidOpenProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; - - // 静态方法用于获取文档内容 - static std::string GetDocumentContent(const std::string& uri); - - public: - static std::unordered_map document_store; }; } diff --git a/lsp-server/src/provider/trace/set_trace_provider.cpp b/lsp-server/src/provider/trace/set_trace_provider.cpp index a8ac5d3..010a4ac 100644 --- a/lsp-server/src/provider/trace/set_trace_provider.cpp +++ b/lsp-server/src/provider/trace/set_trace_provider.cpp @@ -1,35 +1,24 @@ +#include #include "./set_trace_provider.hpp" namespace lsp::providers::trace { - nlohmann::json SetTraceProvider::ProvideResponse(const LspRequest& request) + std::string SetTraceProvider::ProvideResponse(const protocol::RequestMessage& request) { - try - { - auto params = request.params; - if (params.contains("value")) - { - std::string trace_value = params["value"]; - // 这里可以设置跟踪级别 - // 例如:设置全局跟踪变量 - } - } - catch (const std::exception& e) - { - // 处理错误 - } - - // 通知不需要响应 - return nlohmann::json(); + spdlog::debug("SetTraceProvider: Providing response for method {}", request.method); + std::string json; + glz::obj empty_obj{}; // glaze的对象类型 + auto ec = glz::write_json(empty_obj, json); + return ec ? BuildErrorMessageResponse(protocol::ErrorCode::kInternalError, "Internal error") : json; } - inline std::string SetTraceProvider::GetMethod() const + std::string SetTraceProvider::GetMethod() const { return "$/setTrace"; } - inline std::string SetTraceProvider::GetProviderName() const + std::string SetTraceProvider::GetProviderName() const { return "SetTraceProvider"; } diff --git a/lsp-server/src/provider/trace/set_trace_provider.hpp b/lsp-server/src/provider/trace/set_trace_provider.hpp index 43485c6..5c191e4 100644 --- a/lsp-server/src/provider/trace/set_trace_provider.hpp +++ b/lsp-server/src/provider/trace/set_trace_provider.hpp @@ -7,7 +7,7 @@ namespace lsp::providers::trace { public: SetTraceProvider() = default; - nlohmann::json ProvideResponse(const LspRequest& request) override; + std::string ProvideResponse(const protocol::RequestMessage& request) override; std::string GetMethod() const override; std::string GetProviderName() const override; }; diff --git a/test/test.tsl b/test/test.tsl index ecf55f0..3bf6b98 100644 --- a/test/test.tsl +++ b/test/test.tsl @@ -67,3 +67,8 @@ select distinct ["test"] from table where ['a'] = 1 end; a := array(1, 2); b := array((1, 2), (3, 4)); + +// 换行字符串 +sql += "update ts_xbcheck result set isvalid = "$status$remark$" where checkid in ('"$array2str +(checkid[(io1)*len:], "','")$"');\r\n" + diff --git a/test/test_glaz.cpp b/test/test_glaz.cpp new file mode 100644 index 0000000..5b9df55 --- /dev/null +++ b/test/test_glaz.cpp @@ -0,0 +1,311 @@ +#include +#include +#include "../lsp-server/src/protocol/protocol.hpp" +#include "../lsp-server/src/protocol/transform/facade.hpp" + +using namespace lsp::protocol; +using namespace lsp; + +template +void print_json(const string& name, const T& obj) +{ + if (auto result = glz::write_json(obj); result) + { + std::cout << name << " = " << *result << std::endl; + } else + { + std::cerr << "Error: " << result.error() << "\n"; + } +} + +void test_basic_conversion() +{ + std::cout << "\n=== Testing Basic Conversion ===" << std::endl; + + // 测试 struct 转换 + RequestMessage msg; + msg.method = "textDocument/completion"; + msg.id = 42; + + // Struct -> LSPAny + LSPAny any = transform::LSPAny(msg); + std::string json; + auto ce = glz::write_json(any, json); + if(ce) + std::cout << "Error" << std::endl; + std::cout << "Message as JSON: " << json << std::endl; + + // LSPAny -> Struct + RequestMessage restored = transform::As(any); + std::cout << "Restored method: " << restored.method << std::endl; + std::cout << "id = "; + std::visit([](const auto& value) + { + std::cout << value; + }, restored.id); + std::cout << std::endl; +} + +void test_vector_conversion() +{ + std::cout << "\n=== Testing Vector Conversion ===" << std::endl; + + std::vector positions = { + {10, 5}, + {20, 15}, + {30, 25} + }; + + // Vector -> LSPAny + LSPAny any = transform::LSPAny(positions); + + std::string json; + auto ce = glz::write_json(any, json); + std::cout << "Vector as JSON: " << json << std::endl; + + // LSPAny -> Vector + auto restored = transform::As>(any); + std::cout << "Restored " << restored.size() << " positions" << std::endl; +} + +void test_json_string() +{ + std::cout << "\n=== Test json_string ===" << std::endl; + + string json = R"({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":"file:///tmp","workDoneToken":"abc"}})"; + RequestMessage request; + auto ret = glz::read_json(request, json); + std::cout << "jsonrpc = " << request.jsonrpc << std::endl; + std::cout << "method = " << request.method << std::endl; + std::cout << "id = "; + std::visit([](const auto& value) + { + std::cout << value; + }, request.id); + std::cout << std::endl; + + auto result = glz::write_json(request.params); + std::cout << *result << std::endl; + InitializeParams params; + // std::string buffer = R"({"workDoneToken":123,"processId":"id","clientInfo":{"name":"VSCode","version":"1.0.0"},"rootPath":"456","initializationOptions":"options","trace":"messages"})"; + auto r2= glz::read_json(params, *result); + if (params.processId.has_value()) + std::cout << "processId = " << params.processId.value() << "\n"; + else + std::cout << "processId = null\n"; + std::cout << "rootUri = " << params.rootUri.value() << std::endl; + + std::cout << std::endl; +} + +void test_basic_types() +{ + std::cout << "\n=== Test basic_types.hpp ===" << std::endl; + Range range = + { + .start = {1, 3}, + .end = {2, 4} + }; + + DocumentFilter df = { + .language = "en", + .scheme = "scheme", + .pattern = "pattern", + }; + + AnnotatedTextEdit ate; + ate.range = {.start = {1, 3}, .end = {2, 4}}; + ate.newText = "text_edit"; + ate.annotationId = "id"; + + Location location = + { + .uri = "location_uri", + .range = {.start = {1, 3}, .end = {2, 4}} + }; + + Command command1 = + { + .title = "command_title", + .command = "command_command", + }; + Command command2 = + { + .title = "command_title", + .command = "command_command", + .arguments = "any_string" + }; + Command command3 = + { + .title = "command_title", + .command = "command_command", + .arguments = 123 + }; + std::vector array = { + 1.0, // decimal + string("two"), + 3.0, // decimal + true, + nullptr + }; + LSPAny array_val = array; + LSPObject object = { + {"name", string("test object")}, + {"count", 42.0}, // decimal + {"active", boolean(true)}, + {"data", array_val}, + {"metadata", nullptr} + }; + Command command4 = + { + .title = "command_title", + .command = "command_command", + .arguments = object + }; + + MarkdownClientCapabilities mkcp = + { + .parser = "parse", + .allowedTags = std::vector{"h1", "h2", "p", "code", "pre"} + }; + std::cout << "Test....basic_types" << std::endl; + print_json("Range", range); + print_json("DocumentFilter", df); + print_json("AnnotatedTextEdit", ate); + print_json("Location", location); + print_json("Command1", command1); + print_json("Command2", command2); + print_json("Command3", command3); + print_json("Command4", command4); + print_json("MarkdownClientCapabilities", mkcp); + std::cout << std::endl; +} + +void test_capabilities() +{ + std::cout << "\n=== Test capabilities.hpp ===" << std::endl; + InitializeParams ip; + ip.workDoneToken = 123; + ip.processId = std::nullopt; + ip.clientInfo = InitializeParams::ClientInfo{"VSCode", "1.0.0"}; + ip.rootPath = "456"; + ip.initializationOptions = "options"; + ip.trace = TraceValueLiterals::Messages; + + std::cout << "Test....capabilities" << std::endl; + print_json("InitializeParams", ip); + std::cout << std::endl; +} + +void test_RequestMessage_serialize() +{ + // // 1. 直接从 LSPObject 转换 + // lsp::protocol::LSPObject obj = [> ... <]; + // auto result = lsp::transform::As(obj); + + // // 2. 从 variant 转换 + // std::variant var = [> ... <]; + // auto result = lsp::transform::As(var); + + // // 3. 从 optional 转换 + // std::optional opt = [> ... <]; + // auto result = lsp::transform::As(opt); // 返回 optional + + // // 4. 从复杂的嵌套类型转换 + // std::optional> complex = [> ... <]; + // auto result = lsp::transform::As(complex); // 返回 optional + + // // 5. 实际使用场景 + // if (request.params.has_value()) { + // // 直接处理 optional> + // auto completionParams = lsp::transform::As(request.params); + // if (completionParams.has_value()) { + // std::cout << "URI: " << completionParams->textDocument.uri << std::endl; + // } + // } + std::string json = R"({ + "jsonrpc":"2.0", + "id":"4", + "method":"textDocument/completion", + "params":{ + "context":{"triggerKind":1}, + "partialResultToken":0, + "position":{"character":12,"line":22}, + "textDocument":{"uri":"file://path/to_file.ts"}, + "workDoneToken":0 + } + })"; + + RequestMessage request; + auto error = glz::read_json(request, json); + + if (error) { + std::cerr << "Failed to parse JSON: " << glz::format_error(error, json) << std::endl; + } + + std::cout << "jsonrpc: " << request.jsonrpc << std::endl; + std::cout << "method: " << request.method << std::endl; + std::visit([](const auto& id) + { + using T = std::decay_t; + if constexpr (std::is_same_v) { + std::cout << "ID (string): " << id << std::endl; + } else if constexpr (std::is_same_v) { + std::cout << "ID (integer): " << id << std::endl; + } + }, request.id); + + auto completionParams = transform::As(request.params.value()); + std::cout << "URI: " << completionParams.textDocument.uri << std::endl; + + // std::visit([](const auto& value) + // { + // std::cout << value; + // }, request.id); + + // std::visit([](const auto& params) { + + // }, request.params.value()); +} + +void test_message() +{ + std::cout << "\n=== Test message.hpp ===" << std::endl; + ResponseMessage response; + response.id = "123"; + InitializeResult result; + result.serverInfo.name = "TSL Language Server"; + result.serverInfo.version = "1.0.0"; + TextDocumentSyncOptions opts; + opts.openClose = true; + opts.change = TextDocumentSyncKind::kIncremental; + result.capabilities.textDocumentSync = opts; + + CompletionParams comparams; + comparams.textDocument.uri = "file://path/to_file.ts"; + comparams.position.character = 12; + comparams.position.line = 22; + comparams.context = CompletionContext{ + .triggerKind = CompletionTriggerKind::kInvoked, + .triggerCharacter = std::nullopt + }; + RequestMessage rm; + rm.id = "4"; + rm.method = "textDocument/completion"; + rm.params = transform::LSPObject(comparams); + + print_json("ResponseMessage", response); + print_json("rm", rm); +} + +int main() +{ + // test_basic_conversion(); + // test_vector_conversion(); + // test_json_string(); + // test_basic_types(); + // test_capabilities(); + test_message(); + test_RequestMessage_serialize(); + return 0; +} diff --git a/test/test_lsp.bash b/test/test_lsp.bash index 81003e7..e6c5867 100644 --- a/test/test_lsp.bash +++ b/test/test_lsp.bash @@ -1,3 +1,5 @@ +#!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" JSON='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":"file:///tmp","capabilities":{}}}' LENGTH=$(echo -n "$JSON" | wc -c) -printf "Content-Length: %d\r\n\r\n%s" $LENGTH "$JSON" | ../lsp-server/build/arch/tsl-server --log=verbose --log-stderr +printf "Content-Length: %d\r\n\r\n%s" $LENGTH "$JSON" | "$SCRIPT_DIR/../vscode/bin/tsl-server" --log=verbose --log-stderr diff --git a/test/test_lspany2.cpp b/test/test_lspany2.cpp new file mode 100644 index 0000000..5b6c6d9 --- /dev/null +++ b/test/test_lspany2.cpp @@ -0,0 +1,711 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using integer = std::int32_t; +using uinteger = std::uint32_t; +using decimal = double; +using boolean = bool; +using string = std::string; + +// 前向声明 +struct LSPAny; +using LSPObject = std::map; +using LSPArray = std::vector; + +using LSPAnyVariant = std::variant< + LSPObject, + LSPArray, + string, + decimal, // 放在前面,优先匹配数字 + boolean, + std::nullptr_t +>; + +struct LSPAny { + LSPAnyVariant value; + + // 默认构造函数 + LSPAny() : value(nullptr) {} + + // 拷贝和移动构造函数 + LSPAny(const LSPAny& other) : value(other.value) {} + LSPAny(LSPAny&& other) noexcept : value(std::move(other.value)) {} + + // 针对每种支持的类型的构造函数 + LSPAny(const std::map& val) : value(val) {} + LSPAny(std::map&& val) : value(std::move(val)) {} + + LSPAny(const std::vector& val) : value(val) {} + LSPAny(std::vector&& val) : value(std::move(val)) {} + + LSPAny(const string& val) : value(val) {} + LSPAny(string&& val) : value(std::move(val)) {} + LSPAny(const char* val) : value(string(val)) {} + + // 所有数字类型都转换为 decimal + LSPAny(int val) : value(static_cast(val)) {} + LSPAny(long val) : value(static_cast(val)) {} + LSPAny(long long val) : value(static_cast(val)) {} + LSPAny(unsigned int val) : value(static_cast(val)) {} + LSPAny(unsigned long val) : value(static_cast(val)) {} + LSPAny(unsigned long long val) : value(static_cast(val)) {} + LSPAny(float val) : value(static_cast(val)) {} + LSPAny(double val) : value(val) {} + LSPAny(long double val) : value(static_cast(val)) {} + + LSPAny(boolean val) : value(val) {} + LSPAny(std::nullptr_t) : value(nullptr) {} + + // 赋值操作符 + LSPAny& operator=(const LSPAny& other) { + value = other.value; + return *this; + } + + LSPAny& operator=(LSPAny&& other) noexcept { + value = std::move(other.value); + return *this; + } + + // 针对每种支持的类型的赋值操作符 + LSPAny& operator=(const std::map& val) { + value = val; + return *this; + } + + LSPAny& operator=(std::map&& val) { + value = std::move(val); + return *this; + } + + LSPAny& operator=(const std::vector& val) { + value = val; + return *this; + } + + LSPAny& operator=(std::vector&& val) { + value = std::move(val); + return *this; + } + + LSPAny& operator=(const string& val) { + value = val; + return *this; + } + + LSPAny& operator=(string&& val) { + value = std::move(val); + return *this; + } + + LSPAny& operator=(const char* val) { + value = string(val); + return *this; + } + + // 所有数字类型都转换为 decimal + LSPAny& operator=(int val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(long val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(long long val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(unsigned int val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(unsigned long val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(unsigned long long val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(float val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(double val) { + value = val; + return *this; + } + + LSPAny& operator=(long double val) { + value = static_cast(val); + return *this; + } + + LSPAny& operator=(boolean val) { + value = val; + return *this; + } + + LSPAny& operator=(std::nullptr_t) { + value = nullptr; + return *this; + } + + // 类型检查辅助函数 + template + bool is() const { return std::holds_alternative(value); } + + template + T& get() { return std::get(value); } + + template + const T& get() const { return std::get(value); } + + // 访问操作符 + template + auto visit(Visitor&& visitor) const { + return std::visit(std::forward(visitor), value); + } + + template + auto visit(Visitor&& visitor) { + return std::visit(std::forward(visitor), value); + } +}; + +// glaze 自动支持 std::variant,无需额外配置 +namespace glz { + template <> + struct meta { + using T = LSPAny; + static constexpr auto value = &T::value; + }; +} + +// ===== 简单清晰的转换模板 ===== + +// 转换工具类 +struct LSPConvert { + // === 基本类型的转换(优先级最高) === + + // boolean + static LSPAny ToLSPAny(boolean value) { + return LSPAny(value); + } + + // 整数类型 + static LSPAny ToLSPAny(int value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(long value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(long long value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(unsigned int value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(unsigned long value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(unsigned long long value) { + return LSPAny(value); + } + + // 浮点类型 + static LSPAny ToLSPAny(float value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(double value) { + return LSPAny(value); + } + + static LSPAny ToLSPAny(long double value) { + return LSPAny(value); + } + + // string + static LSPAny ToLSPAny(const string& str) { + return LSPAny(str); + } + + static LSPAny ToLSPAny(const char* str) { + return LSPAny(str); + } + + // nullptr + static LSPAny ToLSPAny(std::nullptr_t) { + return LSPAny(nullptr); + } + + // LSPAny 自身 + static LSPAny ToLSPAny(const LSPAny& any) { + return any; + } + + // === 容器类型的转换 === + + // vector + template + static LSPAny ToLSPAny(const std::vector& vec) { + LSPArray arr; + arr.reserve(vec.size()); + for (const auto& item : vec) { + arr.push_back(ToLSPAny(item)); + } + return LSPAny(std::move(arr)); + } + + // map + template + static LSPAny ToLSPAny(const std::map& map) { + LSPObject obj; + for (const auto& [key, value] : map) { + obj[key] = ToLSPAny(value); + } + return LSPAny(std::move(obj)); + } + + // optional + template + static LSPAny ToLSPAny(const std::optional& opt) { + if (opt.has_value()) { + return ToLSPAny(*opt); + } + return LSPAny(nullptr); + } + + // === Struct 到 LSPAny 的转换(最低优先级) === + template + static typename std::enable_if< + !std::is_arithmetic::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value, + LSPAny + >::type ToLSPAny(const T& obj) { + // 序列化为 JSON 字符串 + std::string json; + auto ec = glz::write_json(obj, json); + if (ec) { + throw std::runtime_error("Failed to serialize to JSON"); + } + + // 直接解析为 LSPAny + LSPAny result; + ec = glz::read_json(result, json); + if (ec) { + throw std::runtime_error("Failed to parse JSON to LSPAny"); + } + + return result; + } + + // === LSPAny 到基本类型的转换 === + + // boolean + static boolean BoolFromLSPAny(const LSPAny& any) { + if (!any.is()) { + throw std::runtime_error("LSPAny is not a boolean"); + } + return any.get(); + } + + // 数字类型 + template + static typename std::enable_if::value && !std::is_same::value, T>::type + NumberFromLSPAny(const LSPAny& any) { + if (!any.is()) { + throw std::runtime_error("LSPAny is not a number"); + } + return static_cast(any.get()); + } + + // string + static string StringFromLSPAny(const LSPAny& any) { + if (!any.is()) { + throw std::runtime_error("LSPAny is not a string"); + } + return any.get(); + } + + // === LSPAny 到容器类型的转换 === + + // vector + template + static std::vector VectorFromLSPAny(const LSPAny& any) { + if (!any.is()) { + throw std::runtime_error("LSPAny is not an array"); + } + + const auto& arr = any.get(); + std::vector result; + result.reserve(arr.size()); + for (const auto& item : arr) { + result.push_back(FromLSPAny(item)); + } + return result; + } + + // optional + template + static std::optional OptionalFromLSPAny(const LSPAny& any) { + if (any.is()) { + return std::nullopt; + } + return FromLSPAny(any); + } + + // === LSPAny 到 Struct 的转换 === + template + static T FromLSPAny(const LSPAny& any) { + return FromLSPAnyImpl(any); + } + +private: + // 内部实现,使用重载来处理不同类型 + + // boolean + static boolean FromLSPAnyImpl(const LSPAny& any, boolean*) { + return BoolFromLSPAny(any); + } + + // string + static string FromLSPAnyImpl(const LSPAny& any, string*) { + return StringFromLSPAny(any); + } + + // 数字类型 + template + static typename std::enable_if::value && !std::is_same::value, T>::type + FromLSPAnyImpl(const LSPAny& any, T*) { + return NumberFromLSPAny(any); + } + + // LSPAny 自身 + static LSPAny FromLSPAnyImpl(const LSPAny& any, LSPAny*) { + return any; + } + + // struct 类型 + template + static typename std::enable_if< + !std::is_arithmetic::value && + !std::is_same::value && + !std::is_same::value, + T + >::type FromLSPAnyImpl(const LSPAny& any, T*) { + // 序列化 LSPAny 为 JSON + std::string json; + auto ec = glz::write_json(any.value, json); + if (ec) { + throw std::runtime_error("Failed to serialize LSPAny to JSON"); + } + + // 解析 JSON 到目标类型 + T result; + ec = glz::read_json(result, json); + if (ec) { + throw std::runtime_error("Failed to parse JSON to target type"); + } + + return result; + } + + // 通过指针类型来分派 + template + static T FromLSPAnyImpl(const LSPAny& any) { + return FromLSPAnyImpl(any, static_cast(nullptr)); + } +}; + +// ===== 便利函数(可选) ===== + +// 基本类型的便利函数 +inline LSPAny ToLSPAny(boolean value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(int value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(long value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(long long value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(unsigned int value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(unsigned long value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(unsigned long long value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(float value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(double value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(long double value) { return LSPConvert::ToLSPAny(value); } +inline LSPAny ToLSPAny(const string& str) { return LSPConvert::ToLSPAny(str); } +inline LSPAny ToLSPAny(const char* str) { return LSPConvert::ToLSPAny(str); } +inline LSPAny ToLSPAny(std::nullptr_t) { return LSPConvert::ToLSPAny(nullptr); } +inline LSPAny ToLSPAny(const LSPAny& any) { return LSPConvert::ToLSPAny(any); } + +// 容器类型的便利函数 +template +LSPAny ToLSPAny(const std::vector& vec) { + return LSPConvert::ToLSPAny(vec); +} + +template +LSPAny ToLSPAny(const std::map& map) { + return LSPConvert::ToLSPAny(map); +} + +template +LSPAny ToLSPAny(const std::optional& opt) { + return LSPConvert::ToLSPAny(opt); +} + +// struct 类型的便利函数 +template +typename std::enable_if< + !std::is_arithmetic::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value, + LSPAny +>::type ToLSPAny(const T& value) { + return LSPConvert::ToLSPAny(value); +} + +// FromLSPAny 便利函数 +template +T FromLSPAny(const LSPAny& any) { + return LSPConvert::FromLSPAny(any); +} + +// ===== 测试代码 ===== + +// 测试用的结构体 +struct Message { + string jsonrpc = "2.0"; + string method; + int id = 0; +}; + +struct Position { + int line; + int character; +}; + +struct Range { + Position start; + Position end; +}; + +struct TextDocumentIdentifier { + string uri; +}; + +struct TextDocumentPositionParams { + TextDocumentIdentifier textDocument; + Position position; +}; + +struct CompletionItem { + string label; + std::optional kind; + std::optional detail; + std::optional documentation; + std::optional deprecated; + std::optional preselect; + std::optional sortText; + std::optional filterText; + std::optional insertText; +}; + +// 为测试结构体提供 glaze 元数据 +template <> +struct glz::meta { + using T = Message; + static constexpr auto value = object( + "jsonrpc", &T::jsonrpc, + "method", &T::method, + "id", &T::id + ); +}; + +template <> +struct glz::meta { + using T = Position; + static constexpr auto value = object( + "line", &T::line, + "character", &T::character + ); +}; + +template <> +struct glz::meta { + using T = Range; + static constexpr auto value = object( + "start", &T::start, + "end", &T::end + ); +}; + +template <> +struct glz::meta { + using T = TextDocumentIdentifier; + static constexpr auto value = object( + "uri", &T::uri + ); +}; + +template <> +struct glz::meta { + using T = TextDocumentPositionParams; + static constexpr auto value = object( + "textDocument", &T::textDocument, + "position", &T::position + ); +}; + +template <> +struct glz::meta { + using T = CompletionItem; + static constexpr auto value = object( + "label", &T::label, + "kind", &T::kind, + "detail", &T::detail, + "documentation", &T::documentation, + "deprecated", &T::deprecated, + "preselect", &T::preselect, + "sortText", &T::sortText, + "filterText", &T::filterText, + "insertText", &T::insertText + ); +}; + +// 测试函数 +#include + +void test_basic_conversion() { + std::cout << "=== Testing Basic Conversion ===" << std::endl; + + // 测试 struct 转换 + Message msg; + msg.method = "textDocument/completion"; + msg.id = 42; + + // Struct -> LSPAny + LSPAny any = ToLSPAny(msg); + + // 序列化查看 + std::string json; + glz::write_json(any, json); + std::cout << "Message as JSON: " << json << std::endl; + + // LSPAny -> Struct + Message restored = FromLSPAny(any); + std::cout << "Restored method: " << restored.method << std::endl; + std::cout << "Restored id: " << restored.id << std::endl; +} + +void test_vector_conversion() { + std::cout << "\n=== Testing Vector Conversion ===" << std::endl; + + std::vector positions = { + {10, 5}, + {20, 15}, + {30, 25} + }; + + // Vector -> LSPAny + LSPAny any = ToLSPAny(positions); + + std::string json; + glz::write_json(any, json); + std::cout << "Vector as JSON: " << json << std::endl; + + // LSPAny -> Vector + auto restored = LSPConvert::VectorFromLSPAny(any); + std::cout << "Restored " << restored.size() << " positions" << std::endl; +} + +void test_optional_conversion() { + std::cout << "\n=== Testing Optional Conversion ===" << std::endl; + + CompletionItem item; + item.label = "std::vector"; + item.kind = 1; + item.detail = "template class vector"; + + LSPAny any = ToLSPAny(item); + + std::string json; + glz::write_json(any, json); + std::cout << "CompletionItem as JSON: " << json << std::endl; + + // 测试 null optional + std::optional empty_opt; + LSPAny null_any = ToLSPAny(empty_opt); + glz::write_json(null_any, json); + std::cout << "Empty optional as JSON: " << json << std::endl; +} + +void test_mixed_usage() { + std::cout << "\n=== Testing Mixed Usage ===" << std::endl; + + // 创建复杂对象 + LSPObject obj; + obj["message"] = ToLSPAny(Message{"2.0", "test/method", 123}); + obj["position"] = ToLSPAny(Position{10, 20}); + obj["numbers"] = ToLSPAny(std::vector{1, 2, 3}); + obj["flag"] = LSPAny(true); + obj["value"] = LSPAny(3.14); + obj["text"] = LSPAny("Hello LSP"); + obj["empty"] = LSPAny(nullptr); + + LSPAny root(obj); + + std::string json; + glz::write_json(root, json); + std::cout << "Complex object: " << json << std::endl; + + // 提取值 + if (root.is()) { + auto& root_obj = root.get(); + + Message msg = FromLSPAny(root_obj["message"]); + std::cout << "Extracted message method: " << msg.method << std::endl; + + Position pos = FromLSPAny(root_obj["position"]); + std::cout << "Extracted position: " << pos.line << ", " << pos.character << std::endl; + } +} + +int main() { + try { + test_basic_conversion(); + test_vector_conversion(); + test_optional_conversion(); + test_mixed_usage(); + + std::cout << "\nAll tests completed!" << std::endl; + } catch (const std::exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + return 1; + } + + return 0; +}