This commit is contained in:
csh 2025-07-02 14:29:54 +08:00
parent 65b38b7fec
commit dbc95ff098
61 changed files with 6673 additions and 617 deletions

9
.clangd Normal file
View File

@ -0,0 +1,9 @@
CompileFlags:
Add: [-std=c++23]
If:
PathMatch: [.*\.inl$, .*\.tpp$]
CompileFlags:
Add:
- -xc++-header
- -std=c++23

View File

@ -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$<$<CONFIG:Debug>: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()

7
lsp-server/NOTICE.md Normal file
View File

@ -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)

3
lsp-server/README.md Normal file
View File

@ -0,0 +1,3 @@
# TSL Server
This project depends on third-party libraries licensed under MIT (see [NOTICE.md](./NOTICE.md)).

View File

@ -26,27 +26,27 @@ namespace tsl
return result;
}
std::vector<lsp::CompletionItem> TslKeywords::GetCompletionItems(const std::string& prefix)
std::vector<lsp::protocol::CompletionItem> TslKeywords::GetCompletionItems(const std::string& prefix)
{
InitKeywords();
std::vector<lsp::CompletionItem> result;
std::vector<lsp::protocol::CompletionItem> 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_)

View File

@ -2,7 +2,7 @@
#include <vector>
#include <string>
#include <unordered_set>
#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<KeywordInfo>& GetAllKeywords();
static std::vector<KeywordInfo> GetKeywordsByCategory(KeywordCategory category);
static std::vector<lsp::CompletionItem> GetCompletionItems(const std::string& prefix = "");
static std::vector<lsp::protocol::CompletionItem> GetCompletionItems(const std::string& prefix = "");
static bool IsKeyword(const std::string& word);
static KeywordCategory GetKeywordCategory(const std::string& word);

View File

@ -1,22 +1,32 @@
#include <spdlog/spdlog.h>
#include <mutex>
#include "./dispacther.hpp"
#include "../protocol/transform/facade.hpp"
namespace lsp {
namespace lsp
{
void RequestDispatcher::RegisterProvider(const std::string& method, RequestProvider handler)
{
std::unique_lock<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> lock(providers_mutex_);
return providers_.find(method) != providers_.end();
}
std::vector<std::string> RequestDispatcher::GetSupportedMethods() const
{
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
std::vector<std::string> 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;
}
}

View File

@ -1,27 +1,32 @@
#pragma once
#include "./lsp_types.hpp"
#include <functional>
#include <unordered_map>
#include <vector>
#include <shared_mutex>
#include "../protocol/protocol.hpp"
namespace lsp
{
// 请求处理函数类型
using RequestProvider = std::function<nlohmann::json(const LspRequest&)>;
// 请求处理函数类型 - 返回序列化后的 JSON 字符串
using RequestProvider = std::function<std::string(const protocol::RequestMessage&)>;
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<std::string> 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<std::string, RequestProvider> providers_;
};
}

View File

@ -1,63 +0,0 @@
#pragma once
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
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<std::string> detail;
std::optional<std::string> documentation;
std::optional<std::string> insert_text;
};
}

View File

@ -0,0 +1,56 @@
#include <mutex>
#include <spdlog/spdlog.h>
#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<std::mutex> 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<std::mutex> 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<std::mutex> 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<void(const std::string&)> callback)
{
std::lock_guard<std::mutex> lock(output_mutex_);
send_response_ = std::move(callback);
}
void RequestScheduler::SendResponse(const std::string& response)
{
std::lock_guard<std::mutex> lock(output_mutex_);
if (send_response_)
send_response_(response);
else
spdlog::error("No response callback set! Unable to send: {}", response);
}
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <mutex>
#include <functional>
#include <string>
#include <optional>
#include <unordered_map>
#include <taskflow/taskflow.hpp>
namespace lsp
{
class RequestScheduler
{
public:
using TaskFunc = std::function<std::optional<std::string>()>;
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<void(const std::string&)> 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<std::string, tf::Future<void>> running_tasks_;
std::function<void(const std::string&)> send_response_;
};
}

View File

@ -6,6 +6,7 @@
#include <fcntl.h>
#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<std::string> 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<std::streamsize>(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<glz::json_t::object_t>();
// 判断消息类型
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);
}
}

View File

@ -5,23 +5,30 @@
namespace lsp
{
class LspServer
{
public:
LspServer();
~LspServer() = default;
~LspServer();
void Run();
private:
// 读取LSP消息
std::optional<std::string> 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;
};
}

View File

@ -0,0 +1,244 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string>
#include <map>
#include <variant>
#include <vector>
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<string, LSPAny>;
using LSPArray = std::vector<LSPAny>;
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<string, LSPAny>& val);
LSPAny(std::map<string, LSPAny>&& val);
LSPAny(const std::vector<LSPAny>& val);
LSPAny(std::vector<LSPAny>&& 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<string, LSPAny>& val);
LSPAny& operator=(std::map<string, LSPAny>&& val);
LSPAny& operator=(const std::vector<LSPAny>& val);
LSPAny& operator=(std::vector<LSPAny>&& 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<typename T>
bool is() const;
template<typename T>
T& get();
template<typename T>
const T& get() const;
// 访问操作符
template<typename Visitor>
auto visit(Visitor&& visitor) const;
template<typename Visitor>
auto visit(Visitor&& visitor);
};
using ProgressToken = std::variant<integer, string>;
template<typename T>
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<string> pattern;
};
using DocumentSelector = std::vector<DocumentFilter>;
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<LSPAny> 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<string> version;
};
struct MarkdownClientCapabilities
{
string parser;
std::optional<string> version;
std::optional<std::vector<string>> allowedTags;
};
// LSP模板方法
template<typename T>
bool LSPAny::is() const
{
return std::holds_alternative<T>(value);
}
template<typename T>
T& LSPAny::get()
{
return std::get<T>(value);
}
template<typename T>
const T& LSPAny::get() const
{
return std::get<T>(value);
}
// 访问操作符
template<typename Visitor>
auto LSPAny::visit(Visitor&& visitor) const
{
return std::visit(std::forward<Visitor>(visitor), value);
}
template<typename Visitor>
auto LSPAny::visit(Visitor&& visitor)
{
return std::visit(std::forward<Visitor>(visitor), value);
}
}
#include "./lsp_any.inl"

View File

@ -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<TextDocumentSyncClientCapabilities> synchronization;
std::optional<CompletionClientCapabilities> completion;
std::optional<HoverClientCapabilities> hover;
std::optional<SignatureHelpClientCapabilities> singatureHelp;
std::optional<DeclarationClientCapabilities> declatration;
std::optional<DefinitionClientCapabilities> definition;
std::optional<TypeDefinitionClientCapabilities> typeDefinition;
std::optional<ImplementationClientCapabilities> implementation;
std::optional<ReferenceClientCapabilities> reference;
std::optional<DocumentHighlightClientCapabilities> documentHighlight;
std::optional<DocumentSymbolClientCapabilities> documentSymbol;
std::optional<CodeActionClientCapabilities> codeAction;
std::optional<CodeLensClientCapabilities> codeLens;
std::optional<DocumentLinkClientCapabilities> documentLink;
std::optional<DocumentColorClientCapabilities> colorProvider;
std::optional<DocumentFormattingClientCapabilities> formatting;
std::optional<DocumentRangeFormattingClientCapabilities> rangeFormatting;
std::optional<DocumentOnTypeFormattingClientCapabilities> onTypeFormatting;
std::optional<RenameClientCapabilities> rename;
std::optional<PublishDiagnosticsClientCapabilities> publishDiagnostics;
std::optional<FoldingRangeClientCapabilities> foldingRange;
std::optional<SelectionRangeClientCapabilities> selectionRange;
std::optional<LinkedEditingRangeClientCapabilities> linkedEditingRange;
std::optional<CallHierarchyClientCapabilities> callHierarchy;
std::optional<SemanticTokensClientCapabilities> semanticTokens;
std::optional<MonikerClientCapabilities> moniker;
std::optional<TypeHierarchyClientCapabilities> typeHierarchy;
std::optional<InlineValueClientCapabilities> inlineValue;
std::optional<InlayHintClientCapabilities> inlayHint;
std::optional<DiagnosticClientCapabilities> diagnostic;
};
struct ClientCapabilities
{
struct Workspace
{
struct FileOperations
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> didCreate;
std::optional<boolean> willCreate;
std::optional<boolean> didRename;
std::optional<boolean> willRename;
std::optional<boolean> didDelete;
std::optional<boolean> willDelete;
};
std::optional<boolean> applyEdit;
std::optional<WorkspaceEditClientCapabilities> workspaceEdit;
std::optional<DidChangeConfigurationClientCapabilities> didChangeConfiguration;
std::optional<DidChangeWatchedFilesClientCapabilities> didChangeWatchedFiles;
std::optional<WorkspaceSymbolClientCapabilities> symbol;
std::optional<ExecuteCommandClientCapabilities> executeCommand;
std::optional<boolean> workspaceFolders;
std::optional<boolean> configuration;
std::optional<SemanticTokensWorkspaceClientCapabilities> semanticTokens;
std::optional<CodeLensWorkspaceClientCapabilities> codeLens;
std::optional<FileOperations> fileOperations;
std::optional<InlineValueClientCapabilities> inlineValue;
std::optional<InlayHintClientCapabilities> inlayHint;
std::optional<DiagnosticClientCapabilities> diagnostic;
};
struct Window
{
std::optional<boolean> workDoneProgress;
std::optional<ShowMessageRequestClientCapabilities> showMessage;
std::optional<ShowDocumentClientCapabilities> showDocument;
};
struct General
{
struct StaleRequestSupport
{
boolean cancel;
std::vector<string> retryOnContentModified;
};
std::optional<StaleRequestSupport> staleRequestSupport;
std::optional<RegularExpressionsClientCapabilities> regularExpression;
std::optional<MarkdownClientCapabilities> markdown;
std::optional<std::vector<PositionEncodingKind>> positionEncodings;
};
std::optional<Workspace> workspace;
std::optional<TextDocumentClientCapabilities> textDocument;
std::optional<NotebookDocumentClientCapabilities> notebookDocument;
std::optional<Window> window;
std::optional<General> general;
std::optional<LSPAny> experimental;
};
struct ServerCapabilities
{
struct Workspace
{
struct FileOperations
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> didCreate;
std::optional<boolean> willCreate;
std::optional<boolean> didRename;
std::optional<boolean> willRename;
std::optional<boolean> didDelete;
std::optional<boolean> willDelete;
};
std::optional<WorkspaceFolderServerCapabilities> workspaceFolders;
std::optional<FileOperations> fileOperations;
};
std::optional<PositionEncodingKind> positionEncoding;
std::optional<std::variant<TextDocumentSyncOptions, TextDocumentSyncKind>> textDocumentSync;
std::optional<std::variant<NotebookDocumentSyncOptions, NotebookDocumentSyncRegistrationOptions>> notebookDocumentSync;
std::optional<CompletionOptions> completionProvider;
std::optional<std::variant<boolean, HoverOptions>> hoverProvider;
std::optional<SignatureHelpOptions> signatureHelpProvider;
std::optional<std::variant<boolean, DeclarationOptions, DeclarationRegistrationOptions>> declarationProvider;
std::optional<std::variant<boolean, DefinitionOptions>> definitionProvider;
std::optional<std::variant<boolean, TypeDefinitionOptions, TypeDefinitionRegistrationOptions>> typeDefinitionProvider;
std::optional<std::variant<boolean, ImplementationOptions, ImplementationRegistrationOptions>> implementationProvider;
std::optional<std::variant<boolean, ReferenceOptions>> referencesProvider;
std::optional<std::variant<boolean, DocumentHighlightOptions>> documentHighlightProvider;
std::optional<std::variant<boolean, DocumentSymbolOptions>> documentSymbolProvider;
std::optional<std::variant<boolean, CodeActionOptions>> codeActionProvider;
std::optional<CodeLensOptions> codeLensProvider;
std::optional<DocumentLinkOptions> documentLinkProvider;
std::optional<std::variant<boolean, DocumentColorOptions, DocumentColorRegistrationOptions>> colorProvider;
std::optional<std::variant<boolean, DocumentFormattingOptions>> documentFormattingProvider;
std::optional<std::variant<boolean, DocumentRangeFormattingOptions>> documentRangeFormattingProvider;
std::optional<DocumentOnTypeFormattingOptions> documentOnTypeFormattingProvider;
std::optional<std::variant<boolean, RenameOptions>> renameProvider;
std::optional<std::variant<boolean, FoldingRangeOptions, FoldingRangeRegistrationOptions>> foldingRangeProvider;
std::optional<ExecuteCommandOptions> executeCommandProvider;
std::optional<std::variant<boolean, SelectionRangeOptions, SelectionRangeRegistrationOptions>> selectionRangeProvider;
std::optional<std::variant<boolean, LinkedEditingRangeOptions, LinkedEditingRangeRegistrationOptions>> linkedEditingRangeProvider;
std::optional<std::variant<boolean, CallHierarchyOptions, CallHierarchyRegistrationOptions>> callHierarchyProvider;
std::optional<std::variant<SemanticTokensOptions, SemanticTokensRegistrationOptions>> semanticTokensProvider;
std::optional<std::variant<boolean, MonikerOptions, MonikerRegistrationOptions>> monikerProvider;
std::optional<std::variant<boolean, TypeHierarchyOptions, TypeHierarchyRegistrationOptions>> typeHierarchyProvider;
std::optional<std::variant<boolean, InlineValueOptions, InlineValueRegistrationOptions>> inlineValueProvider;
std::optional<std::variant<boolean, InlayHintOptions, InlayHintRegistrationOptions>> inlayHintProvider;
std::optional<std::variant<DiagnosticOptions, DiagnosticRegistrationOptions>> diagnosticProvider;
std::optional<std::variant<boolean, WorkspaceSymbolOptions>> workspaceSymbolProvider;
std::optional<Workspace> workspace;
std::optional<LSPAny> experimental;
};
struct InitializeResult
{
struct ServerInfo
{
string name;
std::optional<string> version;
};
ServerCapabilities capabilities;
ServerInfo serverInfo;
};
enum class InitializeErrorCodes
{
kUnknownProtocolVersion = 1
};
struct InitializeError
{
boolean retry;
};
struct InitializeParams : WorkDoneProgressParams
{
struct ClientInfo
{
string name;
std::optional<string> version;
};
std::optional<integer> processId;
std::optional<ClientInfo> clientInfo;
std::optional<string> locale;
std::optional<string> rootPath;
std::optional<DocumentUri> rootUri;
std::optional<LSPAny> initializationOptions;
std::optional<ClientCapabilities> capabilities;
TraceValue trace;
std::optional<std::vector<WorkspaceFolder>> workspaceFolders;
};
}

View File

@ -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<CodeActionKind> valueSet;
};
};
struct ResolveSupport
{
std::vector<string> properties;
};
std::optional<boolean> dynamicRegistration;
std::optional<CodeActionLiteralSupport> codeActionLiteralSupport;
std::optional<boolean> isPreferredSupport;
std::optional<boolean> disabledSupport;
std::optional<ResolveSupport> resolveSupport;
std::optional<boolean> honorsChangeAnnotations;
};
struct CodeActionOptions : WorkDoneProgressOptions
{
std::optional<std::vector<CodeActionKind>> codeActionKinds;
std::optional<boolean> resolveProvider;
};
struct CodeActionRegistrationOptions : TextDocumentRegistrationOptions, CodeActionOptions
{
};
enum class CodeActionTriggerKind
{
kInvoked = 1,
kAutomatic = 2
};
struct CodeActionContext
{
std::vector<Diagnostic> diagnostics;
std::optional<std::vector<CodeActionKind>> only;
std::optional<CodeActionTriggerKind> triggerKind;
};
struct CodeActionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
Range range;
CodeActionContext context;
};
struct CodeAction
{
struct Disabled
{
string reason;
};
string title;
std::optional<CodeActionKind> kind;
std::optional<std::vector<Diagnostic>> diagnostics;
std::optional<boolean> isPreferred;
std::optional<Disabled> disabled;
std::optional<WorkspaceEdit> edit;
std::optional<Command> command;
std::optional<LSPAny> data;
};
// Color
struct DocumentColorClientCapabilities
{
std::optional<boolean> 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> textEdit;
std::optional<std::vector<TextEdit>> additionalTextEdits;
};
}

View File

@ -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<string> triggerCharacter;
};
struct InsertReplaceEdit
{
string newText;
Range insert;
Range replace;
};
struct CompletionItemLabelDetails
{
std::optional<std::string> detail;
std::optional<std::string> description;
};
struct CompletionItem
{
string label;
std::optional<CompletionItemLabelDetails> labelDetails;
std::optional<CompletionItemKind> kind;
std::optional<string> detail;
std::optional<std::variant<string, MarkupContent>> documentation;
std::optional<boolean> preselect;
std::optional<string> sortText;
std::optional<string> filterText;
std::optional<string> insertText;
std::optional<InsertTextFormat> insertTextFormat;
std::optional<InsertTextMode> insertTextMode;
std::optional<std::variant<TextEdit, InsertReplaceEdit>> textEdit;
std::optional<string> textEditText;
std::optional<std::vector<TextEdit>> additionalTextEdits;
std::optional<std::vector<string>> commitCharacters;
std::optional<Command> command;
std::optional<LSPAny> data;
};
struct CompletionList
{
struct ItemDefaults
{
struct EditRange
{
Range insert;
Range replace;
};
std::optional<std::vector<string>> commitCharacters;
std::optional<std::variant<Range, EditRange>> editRange;
std::optional<InsertTextFormat> insertTextFormat;
std::optional<InsertTextMode> insertTextMode;
std::optional<LSPAny> data;
};
boolean isIncomplete;
ItemDefaults itemDefaults;
std::vector<CompletionItem> items;
};
struct CompletionClientCapabilities
{
struct CompletionItem
{
struct TagSupport
{
std::optional<std::vector<CompletionItemTag>> valueSet;
};
struct ResolveSupport
{
std::vector<string> properties;
};
struct InsertTextModeSupport
{
std::optional<std::vector<InsertTextMode>> valueSet;
};
boolean snippetSupport;
std::optional<boolean> commitCharactersSupport;
std::optional<std::vector<MarkupKind>> documentationFormat;
std::optional<boolean> deprecatedSupport;
std::optional<boolean> preselectSupport;
std::optional<TagSupport> tagSupport;
boolean insertReplaceSupport;
ResolveSupport resolveSupport;
std::optional<InsertTextModeSupport> insertTextModeSupport;
boolean labelDetailsSupport;
};
struct CompletionItemKinds
{
std::optional<std::vector<string>> valueSet;
};
struct CompletionList
{
std::vector<string> itemDefault;
};
boolean dynamicRegistration;
std::optional<CompletionItem> completionItem;
std::optional<CompletionItemKinds> completionItemKind;
std::optional<boolean> contextSupport;
std::optional<InsertTextMode> insertTextMode;
std::optional<CompletionList> completionList;
};
struct CompletionOptions : WorkDoneProgressOptions
{
struct CompletionItem
{
std::optional<boolean> labelDetailsSupport;
};
std::optional<std::vector<string>> triggerCharacters;
std::optional<std::vector<string>> allCommitCharacters;
std::optional<boolean> resolveProvider;
std::optional<CompletionItem> completionItem;
};
struct CompletionRegistrationOptions : TextDocumentRegistrationOptions, CompletionOptions
{
};
struct CompletionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
std::optional<CompletionContext> context;
};
}

View File

@ -0,0 +1,111 @@
#pragma once
#include "./basic_types.hpp"
#include "./progress.hpp"
namespace lsp::protocol
{
// Configuration
struct ConfigurationItem
{
std::optional<URI> scopeUri;
std::optional<string> section;
};
struct ConfigurationParams
{
std::vector<ConfigurationParams> items;
};
struct DidChangeConfigurationClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct DidChangeConfigurationParams
{
LSPAny settings;
};
// Command Execution
struct ExecuteCommandClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct ExecuteCommandOptions : WorkDoneProgressOptions
{
std::vector<Command> commands;
};
struct ExecuteCommandRegistrationOptions : ExecuteCommandOptions
{
};
struct ExecuteCommandParams : WorkDoneProgressParams
{
string command;
std::optional<std::vector<LSPAny>> 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<boolean> additionalPropertiesSupport;
};
std::optional<MessageActionItem> messageActionItem;
};
struct MessageActionItem
{
std::string title;
};
struct ShowMessageRequestParams
{
MessageType type;
string message;
std::optional<std::vector<MessageActionItem>> actions;
};
struct ShowDocumentClientCapabilities
{
boolean support;
};
struct ShowDocumentParams
{
URI uri;
std::optional<boolean> external;
std::optional<boolean> takeFocus;
std::optional<boolean> selection;
};
struct ShowDocumentResult
{
boolean success;
};
struct LogMessageParams
{
MessageType type;
string message;
};
}

View File

@ -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<DiagnosticSeverity> severity;
std::optional<integer> code;
std::optional<string> codeDescription;
std::optional<string> source;
string message;
std::optional<std::vector<DiagnosticTag>> tags;
std::optional<std::vector<DiagnosticRelatedInformation>> relatedInformation;
std::optional<LSPAny> data;
};
struct PublishDiagnosticsClientCapabilities
{
struct TagSupport
{
std::vector<DiagnosticTag> valueSet;
};
std::optional<boolean> relatedInformation;
std::optional<TagSupport> tagSupport;
std::optional<boolean> versionSupport;
std::optional<boolean> codeDescriptionSupport;
std::optional<boolean> dataSupport;
};
struct PublishDiagnosticsParams
{
DocumentUri uri;
std::optional<integer> version;
std::vector<Diagnostic> diagnostics;
};
struct DiagnosticClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> relatedDocumentSupport;
};
struct DiagnosticOptions : WorkDoneProgressOptions
{
std::optional<string> identifier;
boolean interFileDependencies;
boolean workspaceDiagnostics;
};
struct DiagnosticRegistrationOptions : TextDocumentRegistrationOptions, DiagnosticOptions, StaticRegistrationOptions
{
};
struct DiagnosticParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
std::optional<string> identifier;
std::optional<string> 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<string> resultId;
std::vector<Diagnostic> items;
};
struct UnchangedDocumentDiagnosticReport
{
DocumentDiagnosticReportKind kind = DocumentDiagnosticReportKindLiterals::Unchanged;
std::optional<string> resultId;
};
struct RelatedFullDocumentDiagnosticReport : FullDocumentDiagnosticReport
{
std::optional<std::map<string, std::variant<FullDocumentDiagnosticReport, UnchangedDocumentDiagnosticReport>>> relatedDocuments;
};
struct RelatedUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport
{
std::optional<std::map<string, std::variant<FullDocumentDiagnosticReport, UnchangedDocumentDiagnosticReport>>> relatedDocuments;
};
struct DocumentDiagnosticReportPartialResult
{
std::optional<std::map<string, std::variant<FullDocumentDiagnosticReport, UnchangedDocumentDiagnosticReport>>> relatedDocuments;
};
using DocumentDiagnosticReport = std::variant<RelatedFullDocumentDiagnosticReport, RelatedUnchangedDocumentDiagnosticReport>;
struct DiagnosticServerCancellationData
{
std::optional<boolean> retriggerRequest;
};
struct PreviousResultId
{
DocumentUri uri;
string value;
};
struct WorkspaceDiagnosticParams : WorkDoneProgressParams, PartialResultParams
{
std::optional<string> identifier;
std::vector<PreviousResultId> previousResultIds;
};
struct WorkspaceFullDocumentDiagnosticReport : FullDocumentDiagnosticReport
{
DocumentUri uri;
std::optional<integer> version;
};
struct WorkspaceUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport
{
DocumentUri uri;
std::optional<integer> version;
};
using WorkspaceDocumentDiagnosticReport = std::variant<WorkspaceFullDocumentDiagnosticReport, WorkspaceUnchangedDocumentDiagnosticReport>;
struct WorkspaceDiagnosticReport
{
std::vector<WorkspaceDocumentDiagnosticReport> items;
};
struct WorkspaceDiagnosticReportPartialResult
{
std::vector<WorkspaceDocumentDiagnosticReport> items;
};
struct DiagnosticWorkspaceClientCapabilities
{
std::optional<boolean> refreshSupport;
};
}

View File

@ -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<boolean> 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<boolean> dynamicRegistration;
std::optional<boolean> tooltipSupport;
};
struct DocumentLinkOptions : WorkDoneProgressOptions
{
boolean resolveProvider;
};
struct DocumentLinkRegistrationOptions : TextDocumentRegistrationOptions, DocumentLinkOptions
{
};
struct DocumentLinkParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
};
struct DocumentLink
{
Range range;
std::optional<URI> target;
std::optional<string> tooltip;
std::optional<LSPAny> data;
};
// Hover
struct HoverClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<std::vector<MarkupKind>> 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::string, MarkedStringObject>;
std::variant<MarkedString, std::vector<MarkedString>, MarkupContent> contents;
std::optional<Range> range;
};
// CodeLens
struct CodeLensClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct CodeLensOptions : WorkDoneProgressOptions
{
std::optional<boolean> resolveProvider;
};
struct CodeLensRegistrationOptions : TextDocumentRegistrationOptions, CodeLensOptions
{
};
struct CodeLensParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
};
struct CodeLens
{
Range range;
std::optional<Command> command;
std::optional<LSPAny> data;
};
struct CodeLensWorkspaceClientCapabilities
{
std::optional<boolean> 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<FoldingRangeKind> valueSet;
};
struct FoldingRange
{
std::optional<boolean> collapsedText;
};
std::optional<boolean> dynamicRegistration;
std::optional<uinteger> rangeLimit;
std::optional<boolean> lineFoldingOnly;
std::optional<FoldingRangeKinds> foldingRangeKind;
std::optional<FoldingRange> foldingRange;
};
struct FoldingRangeOptions : WorkDoneProgressOptions
{
};
struct FoldingRangeRegistrationOptions : TextDocumentRegistrationOptions, FoldingRangeOptions, StaticRegistrationOptions
{
};
struct FoldingRangeParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
};
struct FoldingRange
{
uinteger startLine;
std::optional<uinteger> startCharacter;
uinteger endLine;
std::optional<uinteger> endCharacter;
std::optional<FoldingRangeKind> kind;
std::optional<string> collapsedText;
};
// Sekection Range
struct SelectionRangeClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct SelectionRangeOptions : WorkDoneProgressOptions
{
};
struct SelectionRangeRegistrationOptions : SelectionRangeOptions, TextDocumentRegistrationOptions, StaticRegistrationOptions
{
};
struct SelectionRangeParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
std::vector<Position> positions;
};
struct SelectionRange
{
Range range;
std::optional<SelectionRange*> selectionRange;
};
}

View File

@ -0,0 +1,120 @@
#pragma once
#include "./basic_types.hpp"
#include "./registration.hpp"
namespace lsp::protocol
{
struct TextDocumentSyncClientCapabilities
{
boolean dynamicRegistration;
std::optional<boolean> willSave;
std::optional<boolean> willSaveWaitUntil;
std::optional<boolean> didSave;
};
enum class TextDocumentSyncKind
{
kNone = 0,
kFull = 1,
kIncremental = 2
};
struct TextDocumentSyncOptions
{
std::optional<boolean> openClose;
std::optional<TextDocumentSyncKind> 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<integer> 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<TextDocumentContentChangeEvent> contentChanges;
};
enum class TextDocumentSaveReason
{
Manual = 1,
AfterDelay = 2,
FocusOut = 3
};
struct WillSaveTextDocumentParams
{
TextDocumentIdentifier textDocument;
TextDocumentSaveReason reason;
};
struct SaveOptions
{
std::optional<boolean> includeText;
};
struct TextDocumentSaveRegistrationOptions : TextDocumentRegistrationOptions
{
std::optional<boolean> includeText;
};
struct DidSaveTextDocumentParams
{
TextDocumentIdentifier textDocument;
std::optional<string> text;
};
struct DidCloseTextDocumentParams
{
TextDocumentIdentifier textDocument;
};
struct TextDocumentEdit
{
// OptionalVersionedTextDocumentIdentifier textDocument;
std::variant<std::vector<TextEdit>, std::vector<AnnotatedTextEdit>> edits;
};
}

View File

@ -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<boolean> ignoreCase;
};
struct FileOperationPattern
{
string glob;
std::optional<FileOperationPatternKind> matches;
std::optional<FileOperationPatternOptions> options;
};
struct FileOperationFilter
{
std::optional<string> scheme;
FileOperationPattern pattern;
};
struct FileCreate
{
string uri;
};
struct CreateFilesParams
{
std::vector<FileCreate> files;
};
struct FileOperationRegistrationOptions
{
std::vector<FileOperationFilter> filters;
};
struct FileRename
{
string oldUri;
string newUri;
};
struct RenameFilesParams
{
std::vector<FileRename> files;
};
struct FileDelete
{
string uri;
};
struct DeleteFilesParams
{
std::vector<FileDelete> files;
};
// File Watching
struct DidChangeWatchedFilesClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> relativePatternSupport;
};
using Pattern = string;
struct RelativePattern
{
std::variant<WorkspaceFolder, URI> baseUri;
Pattern pattern;
};
using GlobPattern = std::variant<Pattern, RelativePattern>;
enum class WatchKind
{
kCreate = 1,
kChange = 2,
kDelete = 4
};
struct FileSystemWatcher
{
GlobPattern globPattern;
std::optional<WatchKind> kind;
};
struct DidChangeWatchedFilesRegistrationOptions
{
std::vector<FileSystemWatcher> watchers;
};
enum class FileChangeType
{
kCreated = 1,
kChanged = 2,
kDeleted = 3
};
struct FileEvent
{
DocumentUri uri;
FileChangeType type;
};
struct DidChangeWatchedFilesParams
{
std::vector<FileEvent> changes;
};
}

View File

@ -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<boolean> dynamicRegistration;
};
struct DocumentFormattingOptions : WorkDoneProgressOptions
{
};
struct DocumentFormattingRegistrationOptions : TextDocumentRegistrationOptions, DocumentFormattingOptions
{
};
struct FormattingOptions
{
uinteger tabSize;
boolean insertSpaces;
std::optional<boolean> trimTrailingWhitespace;
std::optional<boolean> insertFinalNewline;
std::optional<boolean> trimFinalNewlines;
std::map<string, std::variant<boolean, integer, string>> key;
};
struct DocumentFormattingParams : WorkDoneProgressParams
{
TextDocumentIdentifier textDocument;
FormattingOptions options;
};
// Document Range Formatting
struct DocumentRangeFormattingClientCapabilities
{
std::optional<boolean> 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<boolean> dynamicRegistration;
};
struct DocumentOnTypeFormattingOptions : WorkDoneProgressOptions
{
string firstTriggerCharacter;
std::optional<std::vector<string>> moreTriggerCharacter;
};
struct DocumentOnTypeFormattingRegistrationOptions : TextDocumentRegistrationOptions, DocumentOnTypeFormattingOptions
{
};
struct DocumentOnTypeFormattingParams
{
TextDocumentIdentifier textDocument;
Position position;
string ch;
FormattingOptions options;
};
}

View File

@ -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<string> properties;
};
std::optional<boolean> dynamicRegistration;
std::optional<ResolveSupport> resolveSupport;
};
struct InlayHintOptions : WorkDoneProgressOptions
{
std::optional<boolean> resolveProvider;
};
struct InlayHintRegistrationOptions : TextDocumentRegistrationOptions, InlayHintOptions, StaticRegistrationOptions
{
};
struct InlayHintParams : WorkDoneProgressParams
{
TextDocumentIdentifier textDocument;
Range range;
};
struct InlayHintLabelPart
{
string value;
std::optional<std::variant<string, MarkupContent>> tooltip;
std::optional<Location> location;
std::optional<Command> command;
};
enum class InlayHintKind
{
kType = 1,
kParameter = 2
};
struct InlayHint
{
Position position;
std::variant<string, std::vector<InlayHintLabelPart>> label;
std::optional<InlayHintKind> kind;
std::optional<std::vector<TextEdit>> textEdits;
;
std::optional<std::variant<string, MarkupContent>> tooltip;
std::optional<boolean> paddingLeft;
std::optional<boolean> paddingRight;
std::optional<LSPAny> data;
};
struct InlayHintWorkspaceClientCapabilities
{
std::optional<boolean> refreshSupport;
};
// Inline Values
struct InlineValueClientCapabilities
{
std::optional<boolean> 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<string> variableName;
std::optional<boolean> caseSensitiveLookup;
};
struct InlineValueEvaluatableExpression
{
Range range;
std::optional<string> expression;
};
using InlineValue = std::variant<InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression>;
struct InlineValueWorkspaceClientCapabilities
{
std::optional<boolean> refreshSupport;
};
// Moniker
struct MonikerClientCapabilities
{
std::optional<boolean> 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<MonikerKind> kind;
};
}

View File

@ -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<string, LSPAny>& val) :
value(val) {}
inline LSPAny::LSPAny(std::map<string, LSPAny>&& val) :
value(std::move(val)) {}
inline LSPAny::LSPAny(const std::vector<LSPAny>& val) :
value(val) {}
inline LSPAny::LSPAny(std::vector<LSPAny>&& 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<decimal>(val)) {}
inline LSPAny::LSPAny(long val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(long long val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(unsigned int val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(unsigned long val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(unsigned long long val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(float val) :
value(static_cast<decimal>(val)) {}
inline LSPAny::LSPAny(double val) :
value(val) {}
inline LSPAny::LSPAny(long double val) :
value(static_cast<decimal>(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<string, LSPAny>& val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(std::map<string, LSPAny>&& val)
{
value = std::move(val);
return *this;
}
inline LSPAny& LSPAny::operator=(const std::vector<LSPAny>& val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(std::vector<LSPAny>&& 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<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(long val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(long long val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(unsigned int val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(unsigned long val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(unsigned long long val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(float val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(double val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(long double val)
{
value = static_cast<decimal>(val);
return *this;
}
inline LSPAny& LSPAny::operator=(boolean val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(std::nullptr_t)
{
value = nullptr;
return *this;
}
}

View File

@ -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<integer, string> id;
string method;
std::optional<std::variant<LSPArray, LSPObject>> params;
};
struct ResponseError: Message
{
ErrorCode code;
string message;
std::optional<LSPAny> data;
};
struct ResponseMessage: Message
{
std::variant<integer, string> id;
std::optional<LSPAny> result;
std::optional<ResponseError> error;
};
struct NotificationMessage: Message
{
string method;
std::optional<LSPAny> params;
};
struct CancelParams
{
std::variant<integer, string> id;
};
}

View File

@ -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<boolean> dynamicRegistration;
std::optional<boolean> linkSupport;
};
struct DeclarationOptions : WorkDoneProgressOptions
{
};
struct DeclarationRegistrationOptions : DeclarationOptions, TextDocumentRegistrationOptions, StaticRegistrationOptions
{
};
struct DeclarationParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
};
// Definition
struct DefinitionClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> linkSupport;
};
struct DefinitionOptions : WorkDoneProgressOptions
{
};
struct DefinitionRegistrationOptions : TextDocumentRegistrationOptions, DefinitionOptions
{
};
struct DefinitionParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
};
// TypeDefinition
struct TypeDefinitionClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> linkSupport;
};
struct TypeDefinitionOptions : WorkDoneProgressOptions
{
};
struct TypeDefinitionRegistrationOptions : TextDocumentRegistrationOptions, TypeDefinitionOptions, StaticRegistrationOptions
{
};
struct TypeDefinitionParams : TextDocumentPositionParams, WorkDoneProgressParams
{
};
// Implementation
struct ImplementationClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> linkSupport;
};
struct ImplementationOptions : WorkDoneProgressOptions
{
};
struct ImplementationRegistrationOptions : TextDocumentRegistrationOptions, ImplementationOptions, StaticRegistrationOptions
{
};
struct ImplementationParams : WorkDoneProgressParams, PartialResultParams
{
};
// Reference
struct ReferenceContext
{
boolean includeDeclaration;
};
struct ReferenceClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct ReferenceOptions : WorkDoneProgressOptions
{
};
struct ReferenceRegistrationOptions : TextDocumentRegistrationOptions, ReferenceOptions
{
};
struct ReferenceParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
ReferenceContext context;
};
// CallHierarchy
struct CallHierarchyClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct CallHierarchyOptions : WorkDoneProgressOptions
{
};
struct CallHierarchyRegistrationOptions : TextDocumentRegistrationOptions, CallHierarchyOptions, StaticRegistrationOptions
{
};
struct CallHierarchyParams : TextDocumentPositionParams, WorkDoneProgressParams
{
};
struct CallHierarchyItem
{
string name;
SymbolKind kind;
std::vector<SymbolTag> tags;
DocumentUri uri;
Range range;
Range selectionRange;
std::optional<LSPAny> data;
};
struct CallHierarchyIncomingCallsParams : WorkDoneProgressParams, PartialResultParams
{
CallHierarchyItem item;
};
struct CallHierarchyIncomingCall
{
CallHierarchyItem from;
std::vector<Range> fromRanges;
};
struct CallHierarchyOutgoingCallsParams : WorkDoneProgressParams, PartialResultParams
{
CallHierarchyItem item;
};
struct CallHierarchyOutgoingCall
{
CallHierarchyItem from;
std::vector<Range> fromRanges;
};
// TypeHierarchy
struct TypeHierarchyClientCapabilities
{
std::optional<boolean> 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<SymbolTag> tags;
DocumentUri uri;
Range range;
Range selectionRange;
std::optional<LSPAny> data;
};
struct TypeHierarchySupertypesParams : WorkDoneProgressParams, PartialResultParams
{
TypeHierarchyItem item;
};
struct TypeHierarchySubtypesParams : WorkDoneProgressParams, PartialResultParams
{
TypeHierarchyItem item;
};
}

View File

@ -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<boolean> success;
};
struct NotebookCell
{
NotebookCellKind kind;
DocumentUri document;
std::optional<LSPObject> metadata;
std::optional<ExecutionSummary> executionSummary;
};
struct NotebookDocument
{
URI uri;
string notebookType;
integer version;
LSPObject metadata;
std::vector<NotebookCell> cells;
};
struct NotebookDocumentFilter
{
string notebookType;
std::optional<string> scheme;
std::optional<string> pattern;
};
struct NotebookCellTextDocumentFilter
{
std::variant<string, NotebookDocumentFilter> notebook;
std::optional<string> language;
};
struct NotebookSelector
{
struct Cell
{
string language;
};
std::variant<string, NotebookDocumentFilter> notebook;
std::optional<std::vector<Cell>> cells;
};
struct NotebookDocumentSyncOptions
{
std::vector<NotebookSelector> notebookSelector;
std::optional<boolean> save;
};
struct NotebookDocumentSyncRegistrationOptions : NotebookDocumentSyncOptions, StaticRegistrationOptions
{
};
struct NotebookDocumentSyncClientCapabilities
{
std::optional<boolean> dynamicRegistration;
std::optional<boolean> executionSummarySupport;
};
struct NotebookDocumentClientCapabilities
{
NotebookDocumentSyncClientCapabilities synchronization;
};
struct DidOpenNotebookDocumentParams
{
NotebookDocument notebookDocument;
std::vector<TextDocumentItem> cellTextDocuments;
};
struct VersionedNotebookDocumentIdentifier
{
integer version;
URI uri;
};
struct NotebookCellArrayChange
{
uinteger start;
uinteger deleteCount;
std::optional<std::vector<NotebookCell>> cells;
};
struct NotebookDocumentChangeEvent
{
struct Cell
{
struct Structure
{
NotebookCellArrayChange array;
std::optional<std::vector<TextDocumentItem>> didOpen;
std::optional<std::vector<TextDocumentIdentifier>> didClose;
};
struct TextContent
{
VersionedTextDocumentIdentifier document;
std::vector<TextDocumentContentChangeEvent> changes;
};
std::optional<Structure> structure;
std::optional<std::vector<NotebookCell>> data;
std::optional<TextContent> textContent;
};
std::optional<LSPObject> metadata;
std::optional<std::vector<Cell>> cells;
};
struct DidChangeNotebookDocumentParams
{
VersionedNotebookDocumentIdentifier notebookDocument;
NotebookDocumentChangeEvent change;
};
struct NotebookDocumentIdentifier
{
URI uri;
};
struct DidSaveNotebookDocumentParams
{
NotebookDocumentIdentifier notebookDocument;
};
struct DidCloseNotebookDocumentParams
{
NotebookDocumentIdentifier notebookDocument;
std::vector<TextDocumentIdentifier> cellTextDocuments;
};
}

View File

@ -0,0 +1,73 @@
#pragma once
#include "./basic_types.hpp"
namespace lsp::protocol
{
struct WorkDoneProgressBegin
{
string kind = "begin";
string title;
boolean cancellable;
std::optional<string> message;
uinteger percentage;
};
struct WorkDoneProgressReport
{
string kind = "report";
boolean cancellable;
std::optional<string> 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<string> verbose;
};
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "./basic_types.hpp"
namespace lsp::protocol
{
struct Registration
{
string id;
string method;
std::optional<LSPAny> registerOptions;
};
struct RegistrationParams
{
std::vector<Registration> registrations;
};
struct StaticRegistrationOptions
{
std::optional<string> id;
};
struct TextDocumentRegistrationOptions
{
std::optional<DocumentSelector> documentSelector;
};
struct Unregistration
{
string id;
string method;
};
struct UnregistrationParams
{
std::vector<Unregistration> unregistration;
};
} // namespace lsp::protocol

View File

@ -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<boolean> dynamicRegistration;
std::optional<boolean> prepareSupport;
std::optional<PrepareSupportDefaultBehavior> prepareSupportDefaultBehavior;
std::optional<boolean> honorsChangeAnnotations;
};
struct RenameOptions : WorkDoneProgressOptions
{
std::optional<boolean> prepareProvider;
};
struct RenameRegistrationOptions : TextDocumentRegistrationOptions, RenameOptions
{
};
struct RenameParams : TextDocumentPositionParams, WorkDoneProgressParams
{
string newName;
};
struct PrepareRenameParams : TextDocumentPositionParams, WorkDoneProgressParams
{
};
// Linked Editing Range
struct LinkedEditingRangeClientCapabilities
{
std::optional<boolean> dynamicRegistration;
};
struct LinkedEditingRangeOptions : WorkDoneProgressOptions
{
};
struct LinkedEditingRangeRegistrationOptions : TextDocumentRegistrationOptions, LinkedEditingRangeOptions, StaticRegistrationOptions
{
};
struct LinkedEditingRangeParams : TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams
{
};
struct LinkedEditingRanges
{
std::vector<Range> ranges;
std::optional<string> wordPattern;
};
}

View File

@ -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<string> tokenType;
std::vector<string> tokenModifiers;
};
struct SemanticTokensClientCapabilities
{
struct Requests
{
struct Full
{
std::optional<boolean> delta;
};
std::optional<boolean> range;
std::variant<Full, boolean> full;
};
std::optional<boolean> dynamicRegistration;
std::vector<string> tokenType;
std::vector<string> tokenModifiers;
std::optional<boolean> overlappingTokenSupport;
std::optional<boolean> multilineTokenSupport;
std::optional<boolean> serverCancelSupport;
std::optional<boolean> augmentsSyntaxTokens;
};
struct SemanticTokensOptions : WorkDoneProgressOptions
{
struct Full
{
std::optional<boolean> delta;
};
SemanticTokensLegend legend;
std::optional<boolean> range;
std::variant<Full, boolean> full;
};
struct SemanticTokensRegistrationOptions : TextDocumentRegistrationOptions, SemanticTokensOptions, StaticRegistrationOptions
{
};
struct SemanticTokensParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
};
struct SemanticTokens
{
std::optional<string> resultId;
std::vector<uinteger> data;
};
struct SemanticTokensPartialResult
{
std::vector<uinteger> data;
};
struct SemanticTokensDeltaParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
string previousResultId;
};
struct SemanticTokensEdit
{
uinteger start;
uinteger deleteCount;
std::optional<uinteger> data;
};
struct SemanticTokensDelta
{
std::optional<string> resultId;
std::optional<std::vector<SemanticTokensEdit>> edits;
};
struct SemanticTokensDeltaPartialResult
{
std::optional<std::vector<SemanticTokensEdit>> edits;
};
struct SemanticTokensRangeParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
Range range;
};
struct SemanticTokensWorkspaceClientCapabilities
{
std::optional<boolean> refreshSupport;
};
}

View File

@ -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<boolean> labelOffsetSupport;
};
std::optional<std::vector<MarkupKind>> documentationFormat;
std::optional<ParammeterInformation> parameterInformation;
std::optional<boolean> activeParameterSupport;
};
std::optional<boolean> dynamicRegistration;
std::optional<SignatureInformation> signatureInformation;
std::optional<boolean> contextSupport;
};
struct SignatureHelpOptions : WorkDoneProgressOptions
{
std::optional<std::vector<string>> triggerCharacters;
std::optional<std::vector<string>> retriggerCharacters;
};
struct SignatureHelpRegistrationOptions : TextDocumentRegistrationOptions, SignatureHelpOptions
{
};
enum class SignatureHelpTriggerKind
{
kInvoked = 1,
kTriggerCharacter = 2,
kContentChange = 3
};
struct ParammeterInformation
{
string label;
std::variant<string, MarkupContent> documentation;
};
struct SignatureInformation
{
string label;
std::optional<std::variant<string, MarkupContent>> documentation;
std::optional<std::vector<ParammeterInformation>> parameters;
std::optional<uinteger> activeParameter;
};
struct SignatureHelp
{
std::vector<SignatureInformation> signatures;
std::optional<uinteger> activeSignature;
std::optional<uinteger> activeParameter;
};
struct SignatureHelpContext
{
SignatureHelpTriggerKind triggerKind;
string triggerCharacter;
boolean isRetrigger;
std::optional<SignatureHelp> activeSignatureHelp;
};
struct SignatureHelpParams : TextDocumentPositionParams, WorkDoneProgressParams
{
std::optional<SignatureHelpContext> contextSupport;
};
}

View File

@ -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<string> detail;
SymbolKind kind;
std::optional<std::vector<SymbolTag>> tags;
std::optional<boolean> deprecated;
Range range;
Range selectionRange;
std::optional<std::vector<DocumentSymbol>> children;
};
struct SymbolInformation
{
string name;
SymbolKind kind;
std::optional<std::vector<SymbolTag>> tags;
std::optional<boolean> deprecated;
Location location;
std::optional<string> containerName;
};
struct DocumentSymbolClientCapabilities
{
struct SymbolKinds
{
std::vector<SymbolKind> valueSet;
};
struct TagSupport
{
std::optional<std::vector<CompletionItemTag>> valueSet;
};
std::optional<boolean> dynamicRegistration;
std::optional<SymbolKinds> symbolKind;
std::optional<boolean> hierarchicalDocumentSymbolSupport;
std::optional<TagSupport> tagSupport;
std::optional<boolean> labelSupport;
};
struct DocumentSymbolOptions : WorkDoneProgressOptions
{
std::optional<string> label;
};
struct DocumentSymbolRegistrationOptions : TextDocumentRegistrationOptions, DocumentSymbolOptions
{
};
struct DocumentSymbolParams : WorkDoneProgressParams, PartialResultParams
{
TextDocumentIdentifier textDocument;
};
struct WorkspaceSymbolClientCapabilities
{
struct SymbolKinds
{
std::vector<SymbolKind> valueSet;
};
struct TagSupport
{
std::optional<std::vector<CompletionItemTag>> valueSet;
};
struct ResolveSupport
{
std::vector<string> properties;
};
std::optional<boolean> dynamicRegistration;
std::optional<SymbolKinds> symbolKind;
std::optional<TagSupport> tagSupport;
std::optional<ResolveSupport> resolveSupport;
};
struct WorkspaceSymbolOptions : WorkDoneProgressOptions
{
std::optional<boolean> resolveProvider;
};
struct WorkspaceSymbolRegistrationOptions : WorkspaceSymbolOptions
{
};
struct WorkspaceSymbolParams : WorkDoneProgressParams, PartialResultParams
{
string query;
};
struct WorkspaceSymbol
{
struct LocationUriOnly
{
DocumentUri uri;
};
string name;
SymbolKind kind;
std::optional<std::vector<SymbolTag>> tags;
std::optional<string> containerName;
std::variant<Location, LocationUriOnly> location;
std::optional<LSPAny> data;
};
}

View File

@ -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<CreateFileOptions> options;
std::optional<string> annotationId;
};
struct RenameFileOptions
{
boolean overwrite;
boolean ignoreIfExists;
};
struct RenameFile
{
string kind = "rename";
DocumentUri oldUri;
DocumentUri newUri;
std::optional<RenameFileOptions> options;
std::optional<string> annotationId;
};
struct DeleteFileOptions
{
boolean recursive;
boolean ignoreIfNotExists;
};
struct DeleteFile
{
string kind = "delete";
DocumentUri uri;
std::optional<DeleteFileOptions> options;
std::optional<string> annotationId;
};
struct WorkspaceEdit
{
std::map<DocumentUri, std::vector<TextEdit>> changes;
std::variant<std::vector<TextDocumentEdit>, std::vector<CreateFile>, std::vector<RenameFile>, std::vector<DeleteFile>> documentChanges;
std::optional<std::map<string, ChangeAnnotation>> 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<bool> groupsOnLabel;
};
boolean documentChanges;
std::optional<std::vector<ResourceOperationKind>> resourceOperations;
std::optional<FailureHandlingKind> failureHandling;
std::optional<boolean> normalizesLineEndings;
ChangeAnnotationSupport changeAnnotationSupport;
};
struct WorkspaceFolder
{
URI uri;
string name;
};
struct WorkspaceFolderServerCapabilities
{
std::optional<boolean> supported;
std::optional<std::variant<string, boolean>> changeNotifications;
};
struct WorkspaceFoldersChangeEvent
{
std::vector<WorkspaceFolder> added;
std::vector<WorkspaceFolder> removed;
};
struct ApplyWorkspaceEditParams
{
std::optional<string> label;
WorkspaceEdit edit;
};
struct ApplyWorkspaceEditResult
{
boolean applied;
std::optional<string> failureReason;
std::optional<std::vector<Diagnostic>> failedChanges;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
#pragma once
#include <type_traits>
#include <stdexcept>
#include <exception>
#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<typename T>
struct is_lsp_basic_type: std::false_type {};
template<> struct is_lsp_basic_type<protocol::boolean> : std::true_type {};
template<> struct is_lsp_basic_type<protocol::string> : std::true_type {};
template<> struct is_lsp_basic_type<protocol::decimal> : std::true_type {};
template<> struct is_lsp_basic_type<std::nullptr_t> : std::true_type {};
template<typename T>
struct is_lsp_container_type : std::false_type {};
template<> struct is_lsp_container_type<protocol::LSPObject> : std::true_type {};
template<> struct is_lsp_container_type<protocol::LSPArray> : std::true_type {};
template<> struct is_lsp_container_type<protocol::LSPAny> : std::true_type {};
// struct类型判断
template<typename T>
struct is_user_struct : std::integral_constant<bool,
!std::is_arithmetic<T>::value &&
!std::is_same<T, protocol::string>::value &&
!std::is_same<T, const char*>::value &&
!std::is_same<T, std::nullptr_t>::value &&
!is_lsp_basic_type<T>::value &&
!is_lsp_container_type<T>::value> {};
}

View File

@ -0,0 +1,93 @@
#pragma once
#include "./transformer.hpp"
namespace lsp::transform
{
// ===== 全局便利函数 =====
// 基本类型
template<typename T>
protocol::LSPAny LSPAny(const T& obj);
// // struct 类型
// template<typename T>
// typename std::enable_if<is_user_struct<T>::value, protocol::LSPAny>::type LSPAny(const T& value);
// 容器类型
template<typename T>
protocol::LSPAny LSPAny(const std::vector<T>& vec);
template<typename T>
protocol::LSPAny LSPAny(const std::map<protocol::string, T>& map);
template<typename T>
protocol::LSPAny LSPAny(const std::optional<T>& opt);
template<typename T>
T As(const protocol::LSPAny& any);
template<typename T, typename From>
std::optional<T> As(const std::optional<From>& opt);
template<typename T, typename... Ts>
T As(const std::variant<Ts...>& var);
template<typename T>
T As(const protocol::LSPObject& obj);
template<typename T>
T As(const protocol::LSPArray& arr);
template<typename T>
T Number(const protocol::LSPAny& any);
template<typename T>
std::vector<T> Vector(const protocol::LSPAny& any);
template<typename T>
std::optional<T> Optional(const protocol::LSPAny& any);
template<typename T>
protocol::LSPObject LSPObject(const T& obj);
template<typename T>
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<protocol::LSPObject>(); }
inline bool IsArray(const protocol::LSPAny& any) { return any.is<protocol::LSPArray>(); }
inline bool IsString(const protocol::LSPAny& any) { return any.is<protocol::string>(); }
inline bool IsNumber(const protocol::LSPAny& any) { return any.is<protocol::decimal>(); }
inline bool IsBool(const protocol::LSPAny& any) { return any.is<protocol::boolean>(); }
inline bool IsNull(const protocol::LSPAny& any) { return any.is<std::nullptr_t>(); }
}
// === 外观接口:调试功能 ===
namespace debug
{
inline std::string GetTypeName(const protocol::LSPAny& any)
{
if (any.is<protocol::LSPObject>())
return "LSPObject";
if (any.is<protocol::LSPArray>())
return "LSPArray";
if (any.is<protocol::string>())
return "string";
if (any.is<protocol::decimal>())
return "decimal";
if (any.is<protocol::boolean>())
return "boolean";
if (any.is<std::nullptr_t>())
return "null";
return "unknown";
}
}
}
#include "./facade.inl"

View File

@ -0,0 +1,120 @@
#pragma once
#include "./facade.hpp"
namespace lsp::transform
{
template<typename T>
inline protocol::LSPAny LSPAny(const T& obj)
{
return LSPAnyConverter::ToLSPAny(obj);
}
template<typename T>
inline protocol::LSPAny LSPAny(const std::vector<T>& vec)
{
return LSPAnyConverter::ToLSPAny(vec);
}
template<typename T>
inline protocol::LSPAny LSPAny(const std::map<protocol::string, T>& map)
{
return LSPAnyConverter::ToLSPAny(map);
}
template<typename T>
inline protocol::LSPAny LSPAny(const std::optional<T>& opt)
{
return LSPAnyConverter::ToLSPAny(opt);
}
template<typename T>
inline T As(const protocol::LSPAny& any)
{
return LSPAnyConverter::FromLSPAny<T>(any);
}
template<typename T, typename From>
inline std::optional<T> As(const std::optional<From>& opt)
{
return LSPAnyConverter::As<T>(opt);
}
template<typename T, typename... Ts>
inline T As(const std::variant<Ts...>& var)
{
return LSPAnyConverter::As<T>(var);
}
template<typename T>
inline T As(const protocol::LSPObject& obj)
{
return LSPAnyConverter::As<T>(obj);
}
template<typename T>
inline T As(const protocol::LSPArray& arr)
{
return LSPAnyConverter::As<T>(arr);
}
template<typename T>
inline T Number(const protocol::LSPAny& any)
{
return LSPAnyConverter::ToNumber<T>(any);
}
template<typename T>
inline std::vector<T> Vector(const protocol::LSPAny& any)
{
return LSPAnyConverter::ToVector<T>(any);
}
template<typename T>
inline std::optional<T> Optional(const protocol::LSPAny& any)
{
return LSPAnyConverter::ToOptional<T>(any);
}
template<typename T>
inline protocol::LSPObject LSPObject(const T& obj)
{
// 如果已经是 LSPAny直接获取其中的 LSPObject
if constexpr (std::is_same_v<T, protocol::LSPAny>)
{
return LSPAnyConverter::ToLSPObject(obj);
}
else
{
// 否则先转换为 LSPAny再获取 LSPObject
protocol::LSPAny any = LSPAnyConverter::ToLSPAny(obj);
return LSPAnyConverter::ToLSPObject(any);
}
}
template<typename T>
inline protocol::LSPArray LSPArray(const T& container)
{
// 如果已经是 LSPAny直接获取其中的 LSPArray
if constexpr (std::is_same_v<T, protocol::LSPAny>)
{
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);
}
}

View File

@ -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<typename T>
static protocol::LSPAny ToLSPAny(const std::vector<T>& vec);
template<typename T>
static protocol::LSPAny ToLSPAny(const std::map<protocol::string, T>& map);
template<typename T>
static protocol::LSPAny ToLSPAny(const std::optional<T>& opt);
// === Struct 到 LSPAny 的转换 ===
template<typename T>
static typename std::enable_if<is_user_struct<T>::value, protocol::LSPAny>::type ToLSPAny(const T& obj);
template<typename T>
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, protocol::boolean>::value, T>::type ToNumber(const protocol::LSPAny& any);
// === LSPAny 到容器类型的转换 ===
template<typename T>
static std::vector<T> ToVector(const protocol::LSPAny& any);
template<typename T>
static std::optional<T> ToOptional(const protocol::LSPAny& any);
template<typename T>
static typename std::enable_if<is_user_struct<T>::value, T>::type FromLSPAny(const protocol::LSPAny& any);
// 处理 std::optional<T>
template<typename T, typename From>
static std::optional<T> As(const std::optional<From>& opt);
// 处理 std::variant<Ts...>
template<typename T, typename... Ts>
static T As(const std::variant<Ts...>& var);
// 处理 LSPObject 和 LSPArray
template<typename T>
static T As(const protocol::LSPObject& obj);
template<typename T>
static T As(const protocol::LSPArray& arr);
private:
template<typename T>
static T FromLSPAnyImpl(const protocol::LSPAny& any);
};
}
#include "./transformer.inl"

View File

@ -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<protocol::boolean>())
throw ConversionError("LSPAny does not contain a boolean");
return any.get<protocol::boolean>();
}
inline protocol::string LSPAnyConverter::ToString(const protocol::LSPAny& any)
{
if (!any.is<protocol::string>())
throw ConversionError("LSPAny does not contain a string");
return any.get<protocol::string>();
}
inline protocol::LSPObject LSPAnyConverter::ToLSPObject(const protocol::LSPAny& any)
{
if (!any.is<protocol::LSPObject>())
throw ConversionError("LSPAny does not contain LSPObject");
return any.get<protocol::LSPObject>();
}
inline protocol::LSPArray LSPAnyConverter::ToLSPArray(const protocol::LSPAny& any)
{
if (!any.is<protocol::LSPArray>())
throw ConversionError("LSPAny does not contain LSPArray");
return any.get<protocol::LSPArray>();
}
// 模板实现
template<typename T>
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::vector<T>& 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<typename T>
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::map<protocol::string, T>& map)
{
protocol::LSPObject obj;
for (const auto& [key, value] : map)
obj[key] = ToLSPAny(value);
return protocol::LSPAny(std::move(obj));
}
template<typename T>
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::optional<T>& opt)
{
if (opt.has_value())
return ToLSPAny(*opt);
return protocol::LSPAny(nullptr);
}
template<typename T>
inline typename std::enable_if<is_user_struct<T>::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<typename T>
inline typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, protocol::boolean>::value, T>::type LSPAnyConverter::ToNumber(const protocol::LSPAny& any)
{
if (!any.is<protocol::decimal>())
throw ConversionError("LSPAny does not contain a number");
return static_cast<T>(any.get<protocol::decimal>());
}
template<typename T>
inline std::vector<T> LSPAnyConverter::ToVector(const protocol::LSPAny& any)
{
if (!any.is<protocol::LSPArray>())
throw ConversionError("LSPAny does not contain an array");
const auto& arr = any.get<protocol::LSPArray>();
std::vector<T> result;
result.reserve(arr.size());
for (const auto& item : arr)
result.push_back(FromLSPAny<T>(item));
return result;
}
template<typename T>
inline std::optional<T> LSPAnyConverter::ToOptional(const protocol::LSPAny& any)
{
if (any.is<std::nullptr_t>())
return std::nullopt;
return FromLSPAny<T>(any);
}
template<typename T>
inline typename std::enable_if<is_user_struct<T>::value, T>::type
LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
{
return FromLSPAnyImpl<T>(any);
}
// 处理 std::optional<T>
template<typename T, typename From>
inline std::optional<T> LSPAnyConverter::As(const std::optional<From>& opt)
{
if (opt.has_value())
return As<T>(*opt);
return std::nullopt;
}
// 处理 std::variant<Ts...>
template<typename T, typename... Ts>
inline T LSPAnyConverter::As(const std::variant<Ts...>& var)
{
return std::visit([](const auto& val) -> T {
return As<T>(val);
}, var);
}
// 处理 LSPObject
template<typename T>
inline T LSPAnyConverter::As(const protocol::LSPObject& obj)
{
protocol::LSPAny any(obj);
return FromLSPAny<T>(any);
}
// 处理 LSPArray
template<typename T>
inline T LSPAnyConverter::As(const protocol::LSPArray& arr)
{
protocol::LSPAny any(arr);
return FromLSPAny<T>(any);
}
// FromLSPAnyImpl 实现
template<typename T>
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()));
}
}
}

View File

@ -0,0 +1,23 @@
#include <spdlog/spdlog.h>
#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;
}
}

View File

@ -1,7 +1,6 @@
#pragma once
#include <string>
#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);
};
}

View File

@ -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);
});
}

View File

@ -1,55 +1,44 @@
#include <spdlog/spdlog.h>
#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;
}
}

View File

@ -1,20 +1,18 @@
#pragma once
#include "../base/provider_interface.hpp"
#include <nlohmann/json.hpp>
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();
};
}

View File

@ -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";
}

View File

@ -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;
};

View File

@ -1,211 +1,159 @@
#include <spdlog/spdlog.h>
#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<protocol::CompletionParams>(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<int>(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<CompletionItem> allItems;
std::vector<protocol::CompletionItem> 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<CompletionItem>& 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<std::string>();
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<protocol::CompletionItem> 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<protocol::CompletionItem> items;
std::string CompletionProvider::ExtractPrefix(const nlohmann::json& params)
{
// 方法1: 直接从参数中获取prefix
if (params.contains("prefix"))
{
std::string prefix = params["prefix"].get<std::string>();
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<std::string>();
spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'");
return prefix;
}
// TODO: 理想情况下,应该从文档内容和位置计算前缀
// 这需要维护文档内容的状态
spdlog::trace("ExtractPrefix: No prefix found, returning empty string");
return "";
}
std::vector<CompletionItem> CompletionProvider::ProvideKeywordCompletions(const std::string& prefix)
{
std::vector<CompletionItem> 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<CompletionItem> CompletionProvider::ProvideContextualCompletions(
const std::string& uri,
const nlohmann::json& position,
const std::string& prefix)
std::vector<protocol::CompletionItem> 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<CompletionItem> items;
spdlog::debug("{}: Processing contextual completions for URI: {}", GetProviderName(), textDocument.uri);
std::vector<protocol::CompletionItem> 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;
}
}

View File

@ -2,7 +2,7 @@
#include <string>
#include <vector>
#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<CompletionItem>& 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<CompletionItem> ProvideKeywordCompletions(const std::string& prefix);
std::vector<CompletionItem> 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<protocol::CompletionItem> ProvideKeywordCompletions(const std::string& prefix);
std::vector<protocol::CompletionItem> ProvideContextualCompletions(const protocol::TextDocumentIdentifier& textDocument, const protocol::Position& position, const std::string& prefix);
private:
tsl::TslKeywords tsl_keywords_;

View File

@ -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"];
}
}
}
}
}

View File

@ -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);
};
}

View File

@ -3,46 +3,23 @@
namespace lsp::providers::text_document
{
std::unordered_map<std::string, std::string> 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 : "";
}
}

View File

@ -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<std::string, std::string> document_store;
};
}

View File

@ -1,35 +1,24 @@
#include <spdlog/spdlog.h>
#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";
}

View File

@ -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;
};

View File

@ -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"

311
test/test_glaz.cpp Normal file
View File

@ -0,0 +1,311 @@
#include <iostream>
#include <vector>
#include "../lsp-server/src/protocol/protocol.hpp"
#include "../lsp-server/src/protocol/transform/facade.hpp"
using namespace lsp::protocol;
using namespace lsp;
template<typename T>
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<RequestMessage>(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<Position> 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<std::vector<Position>>(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<LSPAny> 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<string>{"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<CompletionParams>(obj);
// // 2. 从 variant 转换
// std::variant<lsp::protocol::LSPArray, lsp::protocol::LSPObject> var = [> ... <];
// auto result = lsp::transform::As<CompletionParams>(var);
// // 3. 从 optional 转换
// std::optional<lsp::protocol::LSPObject> opt = [> ... <];
// auto result = lsp::transform::As<CompletionParams>(opt); // 返回 optional<CompletionParams>
// // 4. 从复杂的嵌套类型转换
// std::optional<std::variant<lsp::protocol::LSPArray, lsp::protocol::LSPObject>> complex = [> ... <];
// auto result = lsp::transform::As<CompletionParams>(complex); // 返回 optional<CompletionParams>
// // 5. 实际使用场景
// if (request.params.has_value()) {
// // 直接处理 optional<variant<LSPArray, LSPObject>>
// auto completionParams = lsp::transform::As<lsp::protocol::CompletionParams>(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<decltype(id)>;
if constexpr (std::is_same_v<T, lsp::protocol::string>) {
std::cout << "ID (string): " << id << std::endl;
} else if constexpr (std::is_same_v<T, lsp::protocol::integer>) {
std::cout << "ID (integer): " << id << std::endl;
}
}, request.id);
auto completionParams = transform::As<CompletionParams>(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;
}

View File

@ -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

711
test/test_lspany2.cpp Normal file
View File

@ -0,0 +1,711 @@
#include <glaze/glaze.hpp>
#include <variant>
#include <map>
#include <vector>
#include <string>
#include <cstdint>
#include <optional>
#include <type_traits>
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<string, LSPAny>;
using LSPArray = std::vector<LSPAny>;
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<string, LSPAny>& val) : value(val) {}
LSPAny(std::map<string, LSPAny>&& val) : value(std::move(val)) {}
LSPAny(const std::vector<LSPAny>& val) : value(val) {}
LSPAny(std::vector<LSPAny>&& 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<decimal>(val)) {}
LSPAny(long val) : value(static_cast<decimal>(val)) {}
LSPAny(long long val) : value(static_cast<decimal>(val)) {}
LSPAny(unsigned int val) : value(static_cast<decimal>(val)) {}
LSPAny(unsigned long val) : value(static_cast<decimal>(val)) {}
LSPAny(unsigned long long val) : value(static_cast<decimal>(val)) {}
LSPAny(float val) : value(static_cast<decimal>(val)) {}
LSPAny(double val) : value(val) {}
LSPAny(long double val) : value(static_cast<decimal>(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<string, LSPAny>& val) {
value = val;
return *this;
}
LSPAny& operator=(std::map<string, LSPAny>&& val) {
value = std::move(val);
return *this;
}
LSPAny& operator=(const std::vector<LSPAny>& val) {
value = val;
return *this;
}
LSPAny& operator=(std::vector<LSPAny>&& 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<decimal>(val);
return *this;
}
LSPAny& operator=(long val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(long long val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(unsigned int val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(unsigned long val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(unsigned long long val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(float val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(double val) {
value = val;
return *this;
}
LSPAny& operator=(long double val) {
value = static_cast<decimal>(val);
return *this;
}
LSPAny& operator=(boolean val) {
value = val;
return *this;
}
LSPAny& operator=(std::nullptr_t) {
value = nullptr;
return *this;
}
// 类型检查辅助函数
template<typename T>
bool is() const { return std::holds_alternative<T>(value); }
template<typename T>
T& get() { return std::get<T>(value); }
template<typename T>
const T& get() const { return std::get<T>(value); }
// 访问操作符
template<typename Visitor>
auto visit(Visitor&& visitor) const {
return std::visit(std::forward<Visitor>(visitor), value);
}
template<typename Visitor>
auto visit(Visitor&& visitor) {
return std::visit(std::forward<Visitor>(visitor), value);
}
};
// glaze 自动支持 std::variant无需额外配置
namespace glz {
template <>
struct meta<LSPAny> {
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<typename T>
static LSPAny ToLSPAny(const std::vector<T>& vec) {
LSPArray arr;
arr.reserve(vec.size());
for (const auto& item : vec) {
arr.push_back(ToLSPAny(item));
}
return LSPAny(std::move(arr));
}
// map
template<typename T>
static LSPAny ToLSPAny(const std::map<string, T>& map) {
LSPObject obj;
for (const auto& [key, value] : map) {
obj[key] = ToLSPAny(value);
}
return LSPAny(std::move(obj));
}
// optional
template<typename T>
static LSPAny ToLSPAny(const std::optional<T>& opt) {
if (opt.has_value()) {
return ToLSPAny(*opt);
}
return LSPAny(nullptr);
}
// === Struct 到 LSPAny 的转换(最低优先级) ===
template<typename T>
static typename std::enable_if<
!std::is_arithmetic<T>::value &&
!std::is_same<T, string>::value &&
!std::is_same<T, const char*>::value &&
!std::is_same<T, std::nullptr_t>::value &&
!std::is_same<T, LSPAny>::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<boolean>()) {
throw std::runtime_error("LSPAny is not a boolean");
}
return any.get<boolean>();
}
// 数字类型
template<typename T>
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, boolean>::value, T>::type
NumberFromLSPAny(const LSPAny& any) {
if (!any.is<decimal>()) {
throw std::runtime_error("LSPAny is not a number");
}
return static_cast<T>(any.get<decimal>());
}
// string
static string StringFromLSPAny(const LSPAny& any) {
if (!any.is<string>()) {
throw std::runtime_error("LSPAny is not a string");
}
return any.get<string>();
}
// === LSPAny 到容器类型的转换 ===
// vector
template<typename T>
static std::vector<T> VectorFromLSPAny(const LSPAny& any) {
if (!any.is<LSPArray>()) {
throw std::runtime_error("LSPAny is not an array");
}
const auto& arr = any.get<LSPArray>();
std::vector<T> result;
result.reserve(arr.size());
for (const auto& item : arr) {
result.push_back(FromLSPAny<T>(item));
}
return result;
}
// optional
template<typename T>
static std::optional<T> OptionalFromLSPAny(const LSPAny& any) {
if (any.is<std::nullptr_t>()) {
return std::nullopt;
}
return FromLSPAny<T>(any);
}
// === LSPAny 到 Struct 的转换 ===
template<typename T>
static T FromLSPAny(const LSPAny& any) {
return FromLSPAnyImpl<T>(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<typename T>
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, boolean>::value, T>::type
FromLSPAnyImpl(const LSPAny& any, T*) {
return NumberFromLSPAny<T>(any);
}
// LSPAny 自身
static LSPAny FromLSPAnyImpl(const LSPAny& any, LSPAny*) {
return any;
}
// struct 类型
template<typename T>
static typename std::enable_if<
!std::is_arithmetic<T>::value &&
!std::is_same<T, string>::value &&
!std::is_same<T, LSPAny>::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<typename T>
static T FromLSPAnyImpl(const LSPAny& any) {
return FromLSPAnyImpl(any, static_cast<T*>(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<typename T>
LSPAny ToLSPAny(const std::vector<T>& vec) {
return LSPConvert::ToLSPAny(vec);
}
template<typename T>
LSPAny ToLSPAny(const std::map<string, T>& map) {
return LSPConvert::ToLSPAny(map);
}
template<typename T>
LSPAny ToLSPAny(const std::optional<T>& opt) {
return LSPConvert::ToLSPAny(opt);
}
// struct 类型的便利函数
template<typename T>
typename std::enable_if<
!std::is_arithmetic<T>::value &&
!std::is_same<T, string>::value &&
!std::is_same<T, const char*>::value &&
!std::is_same<T, std::nullptr_t>::value &&
!std::is_same<T, LSPAny>::value,
LSPAny
>::type ToLSPAny(const T& value) {
return LSPConvert::ToLSPAny(value);
}
// FromLSPAny 便利函数
template<typename T>
T FromLSPAny(const LSPAny& any) {
return LSPConvert::FromLSPAny<T>(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<int> kind;
std::optional<string> detail;
std::optional<string> documentation;
std::optional<boolean> deprecated;
std::optional<boolean> preselect;
std::optional<string> sortText;
std::optional<string> filterText;
std::optional<string> insertText;
};
// 为测试结构体提供 glaze 元数据
template <>
struct glz::meta<Message> {
using T = Message;
static constexpr auto value = object(
"jsonrpc", &T::jsonrpc,
"method", &T::method,
"id", &T::id
);
};
template <>
struct glz::meta<Position> {
using T = Position;
static constexpr auto value = object(
"line", &T::line,
"character", &T::character
);
};
template <>
struct glz::meta<Range> {
using T = Range;
static constexpr auto value = object(
"start", &T::start,
"end", &T::end
);
};
template <>
struct glz::meta<TextDocumentIdentifier> {
using T = TextDocumentIdentifier;
static constexpr auto value = object(
"uri", &T::uri
);
};
template <>
struct glz::meta<TextDocumentPositionParams> {
using T = TextDocumentPositionParams;
static constexpr auto value = object(
"textDocument", &T::textDocument,
"position", &T::position
);
};
template <>
struct glz::meta<CompletionItem> {
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 <iostream>
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<Message>(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<Position> 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<Position>(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<typename T> 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<string> 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<int>{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<LSPObject>()) {
auto& root_obj = root.get<LSPObject>();
Message msg = FromLSPAny<Message>(root_obj["message"]);
std::cout << "Extracted message method: " << msg.method << std::endl;
Position pos = FromLSPAny<Position>(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;
}