♻️ 使用`module`重构所有代码

This commit is contained in:
csh 2025-12-07 23:07:03 +08:00
parent 549f1d1b0a
commit f7d5a74615
369 changed files with 2272844 additions and 2202476 deletions

3
.gitignore vendored
View File

@ -4,3 +4,6 @@ vscode/out
lsp-server/build
lsp-server/.conan
lsp-server/CMakeUserPresets.json
lsp-server/test/test_tree_sitter/.tree-sitter-config/tree-sitter/config.json
lsp-server/test/test_tree_sitter/.tree-sitter-config.json
lsp-server/.claude/settings.local.json

View File

@ -1,9 +1,25 @@
cmake_minimum_required(VERSION 4.0)
cmake_minimum_required(VERSION 4.2)
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
project(tsl-server LANGUAGES C CXX)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_MODULE_STD 1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "Building with GCC; C++ modules support is experimental for this project")
else()
message(WARNING "Unsupported compiler for modules: ${CMAKE_CXX_COMPILER_ID}. Build may fail.")
endif()
#
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)

View File

@ -1,14 +1,14 @@
[settings]
os=Linux
arch=x86_64
compiler=gcc
compiler.version=15
compiler.libcxx=libstdc++11
compiler=clang
compiler.version=20
compiler.libcxx=libc++
compiler.cppstd=23
build_type=Release
[conf]
tools.build:compiler_executables={"c": "gcc", "cpp": "g++"}
tools.build:compiler_executables={"c": "/usr/bin/clang", "cpp": "/usr/bin/clang++"}
tools.cmake.cmaketoolchain:generator=Ninja
tools.cmake.cmake_layout:build_folder=.
tools.cmake.cmake_layout:build_folder_vars=[]

View File

@ -1,25 +1,24 @@
# Windows cross (MinGW GCC 14) from Linux host
# Windows cross (Clang) from Linux host
[settings]
os=Windows
arch=x86_64
compiler=gcc
compiler.version=14
compiler.libcxx=libstdc++11
compiler.threads=posix
compiler.exception=seh
compiler=clang
compiler.version=20
compiler.runtime=static
compiler.libcxx=libc++
compiler.cppstd=23
build_type=Release
[conf]
tools.build:compiler_executables={"c": "x86_64-w64-mingw32-gcc", "cpp": "x86_64-w64-mingw32-g++"}
tools.build:compiler_executables={"c": "/opt/llvm-mingw/llvm-mingw-ucrt/bin/x86_64-w64-mingw32-clang", "cpp": "/opt/llvm-mingw/llvm-mingw-ucrt/bin/x86_64-w64-mingw32-clang++"}
tools.gnu:host_triplet=x86_64-w64-mingw32
tools.cmake.cmaketoolchain:generator=Ninja
tools.cmake.cmaketoolchain:system_name=Windows
tools.cmake.cmaketoolchain:system_processor=x86_64
tools.cmake.cmaketoolchain:system_version=11
tools.cmake.cmake_layout:build_folder=.
tools.cmake.cmake_layout:build_folder_vars=[]
tools.cmake.cmaketoolchain:system_version=11
[buildenv]
RC=x86_64-w64-mingw32-windres

View File

@ -1,28 +0,0 @@
# Windows MSYS2 UCRT, GCC 14 (adjust paths if msys2 is elsewhere)
[settings]
os=Windows
os.subsystem=msys2
arch=x86_64
compiler=gcc
compiler.version=14
compiler.libcxx=libstdc++11
compiler.threads=posix
compiler.exception=seh
compiler.cppstd=23
build_type=Release
[conf]
tools.build:compiler_executables={"c": "C:/Programs/msys2/ucrt64/bin/gcc.exe", "cpp": "C:/Programs/msys2/ucrt64/bin/g++.exe"}
tools.gnu:host_triplet=x86_64-w64-mingw32
tools.cmake.cmaketoolchain:generator=Ninja
tools.cmake.cmaketoolchain:system_name=Windows
tools.cmake.cmaketoolchain:system_processor=x86_64
tools.cmake.cmake_layout:build_folder=.
tools.cmake.cmake_layout:build_folder_vars=[]
tools.cmake.cmaketoolchain:system_version=11
[buildenv]
MSYSTEM=UCRT64
CHERE_INVOKING=1
PKG_CONFIG_PATH=C:/Programs/msys2/ucrt64/lib/pkgconfig

View File

@ -16,22 +16,7 @@ fmt/*:header_only=True
[layout]
cmake_layout
# Quick build (only test_ast):
# CONAN_HOME=/tmp/conan-home conan install . \
# -pr:h conan/profiles/linux-x86_64-gcc-15 \
# -pr:b conan/profiles/linux-x86_64-gcc-15 \
# -of build/arch-server --build=missing
# cmake -S . -B build/arch-server \
# -DCMAKE_TOOLCHAIN_FILE=$PWD/build/arch-server/Release/generators/conan_toolchain.cmake \
# -DBUILD_TESTS=ON
# cmake --build build/arch-server --target test_ast
# Cross build to Windows (MinGW from Linux):
# CONAN_HOME=/tmp/conan-home conan install . \
# -pr:h conan/profiles/windows-x86_64-gcc-15-cross \
# -pr:b conan/profiles/linux-x86_64-gcc-15 \
# -of build/win-cross --build=missing
# cmake -S . -B build/win-cross \
# -DCMAKE_TOOLCHAIN_FILE=$PWD/build/win-cross/Release/generators/conan_toolchain.cmake \
# -DBUILD_TESTS=ON
# cmake --build build/win-cross --target test_ast
# 使用 Clang+libc++ 构建:
# CONAN_HOME=/tmp/conan-home conan install . -pr:h=conan/profiles/linux-x86_64-clang -pr:b=conan/profiles/linux-x86_64-clang -of build/clang --build=missing
# cmake -S . -B build/clang -DCMAKE_TOOLCHAIN_FILE=$PWD/build/clang/Release/generators/conan_toolchain.cmake -DBUILD_TESTS=ON
# cmake --build build/clang --target tsl-server

View File

@ -6,11 +6,13 @@ if(DEFINED CMAKE_TOOLCHAIN_FILE)
message(STATUS ">>> CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}")
endif()
# MinGW/MSYS2
if(MINGW)
add_link_options(-static -static-libgcc -static-libstdc++)
elseif(UNIX AND NOT APPLE) # Linux
add_link_options(-static-libgcc -static-libstdc++)
# 使 Clang GCC C++ Modules
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "Building with Clang modules support")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "Building with GCC; C++ modules support is experimental for this project")
else()
message(WARNING "Unsupported compiler for modules: ${CMAKE_CXX_COMPILER_ID}. Build may fail.")
endif()
if(WIN32)
@ -36,57 +38,114 @@ if(NOT TARGET fmt::fmt-header-only)
message(WARNING "fmt header-only target not found, using shared library")
endif()
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
set(SOURCES
main.cpp
utils/args_parser.cpp
main.cppm
utils/args_parser.cppm
utils/string.cpp
utils/text_coordinates.cpp
core/dispacther.cpp
core/server.cpp
scheduler/async_executor.cpp
language/ast/deserializer.cpp
language/ast/detail.cpp
language/ast/tree_sitter_utils.cpp
language/symbol/builder.cpp
language/symbol/index/location.cpp
language/symbol/index/coordinator.cpp
language/symbol/index/scope.cpp
language/symbol/store.cpp
language/symbol/table.cpp
manager/bootstrap.cpp
manager/document.cpp
manager/parser.cpp
manager/symbol.cpp
manager/detail/text_document.cpp
manager/manager_hub.cpp
language/semantic/graph/call.cpp
language/semantic/graph/inheritance.cpp
language/semantic/graph/reference.cpp
language/semantic/semantic_model.cpp
language/semantic/type_system.cpp
language/semantic/name_resolver.cpp
language/semantic/analyzer.cpp
language/semantic/token_collector.cpp
language/keyword/repo.cpp
provider/base/bootstrap.cpp
provider/base/interface.cpp
provider/initialize/initialize.cpp
provider/initialized/initialized.cpp
provider/text_document/did_open.cpp
provider/text_document/did_change.cpp
provider/text_document/did_close.cpp
provider/text_document/definition.cpp
provider/text_document/completion.cpp
# provider/text_document/semantic_tokens.cpp
provider/shutdown/shutdown.cpp
provider/exit/exit.cpp
provider/cancel_request/cancel_request.cpp
provider/trace/set_trace.cpp
provider/completion_item/resolve.cpp
utils/text_coordinates.cppm
core/dispacther.cppm
core/server.cppm
scheduler/async_executor.cppm
language/symbol/internal/builder.cppm
language/symbol/index/location.cppm
language/symbol/index/coordinator.cppm
language/symbol/index/scope.cppm
language/symbol/internal/store.cppm
language/symbol/internal/table.cppm
manager/bootstrap.cppm
manager/document.cppm
manager/parser.cppm
manager/symbol.cppm
manager/detail/text_document.cppm
manager/manager_hub.cppm
language/semantic/graph/call.cppm
language/semantic/graph/inheritance.cppm
language/semantic/graph/reference.cppm
language/semantic/interface.cppm
language/semantic/semantic_model.cppm
language/semantic/type_system.cppm
language/semantic/name_resolver.cppm
language/semantic/analyzer.cppm
language/semantic/token_collector.cppm
tree-sitter/parser.c
tree-sitter/scanner.c)
file(GLOB_RECURSE PROVIDER_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/provider/*.cppm")
file(GLOB_RECURSE MANAGER_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/manager/*.cppm")
add_executable(${PROJECT_NAME} ${SOURCES})
if(TARGET std_module)
add_dependencies(${PROJECT_NAME} std_module)
endif()
target_sources(
${PROJECT_NAME}
PRIVATE
FILE_SET cxx_modules TYPE CXX_MODULES FILES bridge/glaze.cppm
bridge/spdlog.cppm
bridge/taskflow.cppm
bridge/tree_sitter.cppm
main.cppm
language/ast/ast.cppm
language/ast/types.cppm
language/ast/deserializer.cppm
language/ast/deserializer_impl.cppm
language/ast/ts_utils.cppm
language/ast/detail.cppm
utils/string.cppm
utils/args_parser.cppm
utils/text_coordinates.cppm
protocol/common/basic_types.cppm
protocol/common/message.cppm
protocol/common/registration.cppm
protocol/window/progress.cppm
protocol/initialize/configuration.cppm
protocol/initialize/capabilities.cppm
protocol/workspace/workspace.cppm
protocol/workspace/file_operations.cppm
protocol/workspace/notebook.cppm
protocol/text_document/document_sync.cppm
protocol/text_document/completion.cppm
protocol/text_document/code_actions.cppm
protocol/text_document/diagnostics.cppm
protocol/text_document/document_features.cppm
protocol/text_document/formatting.cppm
protocol/text_document/inline_features.cppm
protocol/text_document/navigation.cppm
protocol/text_document/rename.cppm
protocol/text_document/semantic_tokens.cppm
protocol/text_document/signature_help.cppm
protocol/text_document/symbols.cppm
codec/common.cppm
codec/transformer.cppm
codec/facade.cppm
protocol/types.cppm
protocol/protocol.cppm
language/symbol/types.cppm
language/symbol/internal/builder.cppm
language/symbol/internal/store.cppm
language/symbol/internal/table.cppm
language/symbol/index/coordinator.cppm
language/symbol/index/location.cppm
language/symbol/index/scope.cppm
language/symbol/symbol.cppm
language/semantic/interface.cppm
language/semantic/semantic.cppm
language/semantic/type_system.cppm
language/semantic/name_resolver.cppm
language/semantic/semantic_model.cppm
language/semantic/analyzer.cppm
language/semantic/token_collector.cppm
language/semantic/graph/call.cppm
language/semantic/graph/inheritance.cppm
language/semantic/graph/reference.cppm
language/keyword/repo.cppm
scheduler/async_executor.cppm
${MANAGER_MODULES}
core/dispacther.cppm
core/server.cppm
${PROVIDER_MODULES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE SPDLOG_HEADER_ONLY
@ -99,8 +158,32 @@ target_link_libraries(
$<$<PLATFORM_ID:Linux>:Threads::Threads> # 使
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(
${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic $<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3>)
if(WIN32)
# libunwind libunwind.dll
set(_orig_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_library(LIBUNWIND_STATIC NAMES unwind libunwind)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_orig_suffixes})
if(NOT LIBUNWIND_STATIC)
# Fallback to known llvm-mingw sysroot layout (used by windows-cross toolchain)
set(_unwind_candidate
"/opt/llvm-mingw/llvm-mingw-ucrt/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32/lib/libunwind.a"
)
if(EXISTS "${_unwind_candidate}")
set(LIBUNWIND_STATIC "${_unwind_candidate}")
endif()
endif()
if(LIBUNWIND_STATIC)
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBUNWIND_STATIC})
else()
message(WARNING "libunwind static library not found; executable may require libunwind.dll at runtime")
endif()
endif()
target_compile_options(
${PROJECT_NAME}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-import-implementation-partition-unit-in-interface-unit
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3>)

View File

@ -0,0 +1,21 @@
module;
// Global module fragment: pull in third-party headers
#include <glaze/glaze.hpp>
export module glaze;
import std;
// Re-export glaze namespace
export namespace glz
{
using namespace ::glz;
// Explicitly export commonly used items for better documentation
using ::glz::format_error;
using ::glz::meta;
using ::glz::object;
using ::glz::read_json;
using ::glz::write_json;
}

View File

@ -0,0 +1,60 @@
module;
// Global module fragment: pull in third-party headers
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
export module spdlog;
import std;
// Re-export spdlog namespace
export namespace spdlog
{
using namespace ::spdlog;
// Explicitly export commonly used items for better documentation
// Logger management
using ::spdlog::get;
using ::spdlog::create;
using ::spdlog::drop;
using ::spdlog::drop_all;
using ::spdlog::set_default_logger;
using ::spdlog::default_logger;
using ::spdlog::shutdown;
// Logging functions
using ::spdlog::trace;
using ::spdlog::debug;
using ::spdlog::info;
using ::spdlog::warn;
using ::spdlog::error;
using ::spdlog::critical;
// Configuration
using ::spdlog::set_level;
using ::spdlog::get_level;
using ::spdlog::set_pattern;
using ::spdlog::flush_on;
using ::spdlog::flush_every;
// Types
using logger = ::spdlog::logger;
using ::spdlog::sink_ptr;
// Level enum
namespace level
{
using ::spdlog::level::level_enum;
}
// Common sinks
namespace sinks
{
using ::spdlog::sinks::stdout_color_sink_mt;
using ::spdlog::sinks::stderr_color_sink_mt;
using ::spdlog::sinks::basic_file_sink_mt;
}
}

View File

@ -0,0 +1,21 @@
module;
// Global module fragment: pull in third-party headers
#include <taskflow/taskflow.hpp>
export module taskflow;
import std;
// Re-export taskflow namespace
export namespace tf
{
using namespace ::tf;
// Explicitly export commonly used items for better documentation
using ::tf::Executor;
using ::tf::Taskflow;
using ::tf::Task;
using ::tf::Subflow;
using ::tf::Future;
}

View File

@ -0,0 +1,65 @@
module;
// Global module fragment: pull in third-party headers
#include <stdint.h> // For uint32_t used by tree-sitter API
#include <tree_sitter/api.h>
export module tree_sitter;
import std;
// Re-export stdint macros that modules don't automatically provide
export constexpr uint32_t UINT32_MAX_VALUE = UINT32_MAX;
// Re-export tree_sitter types and functions
export {
// C standard types needed by tree-sitter API
using ::uint32_t;
using ::uint16_t;
using ::size_t;
// C API types (already in global namespace)
using ::TSNode;
using ::TSParser;
using ::TSTree;
using ::TSLanguage;
using ::TSSymbol;
using ::TSPoint;
using ::TSRange;
using ::TSInput;
using ::TSInputEdit;
using ::TSQuery;
using ::TSQueryCursor;
using ::TSQueryCapture;
using ::TSQueryMatch;
// C API functions
using ::ts_parser_new;
using ::ts_parser_delete;
using ::ts_parser_set_language;
using ::ts_parser_parse_string;
using ::ts_tree_delete;
using ::ts_tree_edit;
using ::ts_tree_root_node;
using ::ts_node_descendant_for_byte_range;
using ::ts_node_child;
using ::ts_node_child_count;
using ::ts_node_type;
using ::ts_node_start_byte;
using ::ts_node_end_byte;
using ::ts_node_start_point;
using ::ts_node_end_point;
using ::ts_node_string;
using ::ts_node_is_null;
using ::ts_node_is_named;
using ::ts_node_is_missing;
using ::ts_node_is_error;
using ::ts_node_parent;
using ::ts_node_child_by_field_name;
using ::ts_node_field_name_for_child;
using ::ts_node_has_changes;
using ::ts_node_eq;
}
// Forward declare language function (to be defined elsewhere)
export extern "C" const TSLanguage* tree_sitter_tsf(void);

View File

@ -1,13 +1,12 @@
#pragma once
#include <type_traits>
#include <stdexcept>
#include <vector>
#include <map>
#include <optional>
#include <variant>
#include "../detail/basic_types.hpp"
module;
namespace lsp::transform
export module lsp.codec.common;
import std;
import lsp.protocol.common.basic_types;
export namespace lsp::codec
{
/// 转换错误异常类
class ConversionError : public std::runtime_error

View File

@ -1,12 +1,61 @@
#pragma once
// #include "./facade.hpp"
module;
namespace lsp::transform
import glaze;
export module lsp.codec.facade;
import std;
import lsp.codec.transformer;
import lsp.protocol.common.basic_types;
export namespace lsp::codec
{
// ==================== JSON 序列化/反序列化 ====================
template<typename T>
std::optional<T> Deserialize(const std::string& json);
template<typename T>
inline std::optional<T> Deserialize(const std::string& json)
std::optional<std::string> Serialize(const T& obj);
inline constexpr auto ToLSPAny = [](const auto& value) {
return LSPAnyConverter::ToLSPAny(value);
};
inline constexpr auto FromLSPAny = []<typename T>(const auto& input) -> T {
using InputType = std::decay_t<decltype(input)>;
protocol::LSPAny any;
if constexpr (std::is_same_v<InputType, std::variant<protocol::LSPArray, protocol::LSPObject>>)
any = std::visit([](const auto& v) -> protocol::LSPAny { return v; }, input);
else
any = input;
return LSPAnyConverter::FromLSPAny<T>(any);
};
namespace check
{
bool IsObject(const protocol::LSPAny& any);
bool IsArray(const protocol::LSPAny& any);
bool IsString(const protocol::LSPAny& any);
bool IsNumber(const protocol::LSPAny& any);
bool IsBool(const protocol::LSPAny& any);
bool IsNull(const protocol::LSPAny& any);
}
namespace debug
{
std::string GetTypeName(const protocol::LSPAny& any);
std::string GetIdString(const std::variant<int, std::string>& id);
}
}
namespace lsp::codec
{
// ==================== JSON 序列化/反序列化 ====================
template<typename T>
std::optional<T> Deserialize(const std::string& json)
{
T obj;
auto ce = glz::read_json(obj, json);
@ -17,7 +66,7 @@ namespace lsp::transform
}
template<typename T>
inline std::optional<std::string> Serialize(const T& obj)
std::optional<std::string> Serialize(const T& obj)
{
std::string json;
auto ce = glz::write_json(obj, json);
@ -28,7 +77,6 @@ namespace lsp::transform
}
// ==================== 类型检查 ====================
namespace check
{
inline bool IsObject(const protocol::LSPAny& any)
@ -48,9 +96,7 @@ namespace lsp::transform
inline bool IsNumber(const protocol::LSPAny& any)
{
return any.Is<protocol::integer>() ||
any.Is<protocol::uinteger>() ||
any.Is<protocol::decimal>();
return any.Is<protocol::integer>() || any.Is<protocol::uinteger>() || any.Is<protocol::decimal>();
}
inline bool IsBool(const protocol::LSPAny& any)
@ -65,7 +111,6 @@ namespace lsp::transform
}
// ==================== 调试工具 ====================
namespace debug
{
inline std::string GetTypeName(const protocol::LSPAny& any)

View File

@ -1,51 +1,73 @@
#pragma once
module;
namespace lsp::transform
import glaze;
export module lsp.codec.transformer;
import std;
import lsp.codec.common;
import lsp.protocol.common.basic_types;
export namespace lsp::codec
{
// ==================== ToLSPAny 实现 ====================
class LSPAnyConverter
{
public:
template<typename T>
static protocol::LSPAny ToLSPAny(const T& value);
template<typename T>
static T FromLSPAny(const protocol::LSPAny& any);
private:
template<typename T>
static T ExtractNumber(const protocol::LSPAny& any);
template<typename T>
static T ConvertViaJson(const protocol::LSPAny& any);
template<typename T>
static protocol::LSPAny SerializeViaJson(const T& obj);
};
}
// ==================== 实现 ====================
namespace lsp::codec
{
template<typename T>
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const T& value)
protocol::LSPAny LSPAnyConverter::ToLSPAny(const T& value)
{
using Type = std::decay_t<T>;
// LSPAny 直接返回
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
{
return value;
}
// LSPObject 直接返回
else if constexpr (std::is_same_v<Type, protocol::LSPObject>)
{
return protocol::LSPAny(value);
}
// LSPArray 直接返回
else if constexpr (std::is_same_v<Type, protocol::LSPArray>)
{
return protocol::LSPAny(value);
}
else if constexpr (std::is_same_v<Type, const char*> ||
std::is_same_v<Type, char*>)
else if constexpr (std::is_same_v<Type, const char*> || std::is_same_v<Type, char*>)
{
return protocol::LSPAny(protocol::string(value));
}
// LSP 基本类型boolean, string, decimal, nullptr
else if constexpr (is_lsp_basic_type_v<Type>)
{
return protocol::LSPAny(value);
}
// 整数类型(依赖 LSPAny 模板构造函数)
else if constexpr (std::is_integral_v<Type> &&
!std::is_same_v<Type, bool>)
else if constexpr (std::is_integral_v<Type> && !std::is_same_v<Type, bool>)
{
return protocol::LSPAny(value);
}
// 浮点类型
else if constexpr (std::is_floating_point_v<Type>)
{
return protocol::LSPAny(value);
}
// vector → LSPArray
else if constexpr (is_vector_v<Type>)
{
protocol::LSPArray arr;
@ -54,7 +76,6 @@ namespace lsp::transform
arr.push_back(ToLSPAny(item));
return protocol::LSPAny(std::move(arr));
}
// map → LSPObject
else if constexpr (is_map_v<Type>)
{
protocol::LSPObject obj;
@ -62,14 +83,12 @@ namespace lsp::transform
obj[key] = ToLSPAny(val);
return protocol::LSPAny(std::move(obj));
}
// optional → LSPAny or null
else if constexpr (is_optional_v<Type>)
{
if (value.has_value())
return ToLSPAny(*value);
return protocol::LSPAny(nullptr);
}
// 用户结构体 → LSPAny (通过 JSON)
else if constexpr (is_user_struct_v<Type>)
{
return SerializeViaJson(value);
@ -80,54 +99,43 @@ namespace lsp::transform
}
}
// ==================== FromLSPAny 实现 ====================
template<typename T>
inline T LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
T LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
{
using Type = std::decay_t<T>;
// LSPAny 直接返回
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
{
return any;
}
// LSPObject 提取
else if constexpr (std::is_same_v<Type, protocol::LSPObject>)
{
if (!any.Is<protocol::LSPObject>())
throw ConversionError("LSPAny does not contain LSPObject");
return any.Get<protocol::LSPObject>();
}
// LSPArray 提取
else if constexpr (std::is_same_v<Type, protocol::LSPArray>)
{
if (!any.Is<protocol::LSPArray>())
throw ConversionError("LSPAny does not contain LSPArray");
return any.Get<protocol::LSPArray>();
}
// boolean 提取
else if constexpr (std::is_same_v<Type, bool>)
{
if (!any.Is<protocol::boolean>())
throw ConversionError("LSPAny does not contain a boolean");
return any.Get<protocol::boolean>();
}
// string 提取
else if constexpr (std::is_same_v<Type, protocol::string> ||
std::is_same_v<Type, std::string>)
else if constexpr (std::is_same_v<Type, protocol::string> || std::is_same_v<Type, std::string>)
{
if (!any.Is<protocol::string>())
throw ConversionError("LSPAny does not contain a string");
return any.Get<protocol::string>();
}
// 数字类型提取
else if constexpr (std::is_arithmetic_v<Type> &&
!std::is_same_v<Type, bool>)
else if constexpr (std::is_arithmetic_v<Type> && !std::is_same_v<Type, bool>)
{
return ExtractNumber<Type>(any);
}
// vector 提取
else if constexpr (is_vector_v<Type>)
{
if (!any.Is<protocol::LSPArray>())
@ -140,7 +148,6 @@ namespace lsp::transform
result.push_back(FromLSPAny<typename Type::value_type>(item));
return result;
}
// map 提取
else if constexpr (is_map_v<Type>)
{
if (!any.Is<protocol::LSPObject>())
@ -152,14 +159,12 @@ namespace lsp::transform
result[key] = FromLSPAny<typename Type::mapped_type>(val);
return result;
}
// optional 提取
else if constexpr (is_optional_v<Type>)
{
if (any.Is<std::nullptr_t>())
return std::nullopt;
return FromLSPAny<typename Type::value_type>(any);
}
// 用户结构体提取(通过 JSON
else if constexpr (is_user_struct_v<Type>)
{
return ConvertViaJson<Type>(any);
@ -170,46 +175,44 @@ namespace lsp::transform
}
}
// ==================== 辅助工具实现 ====================
template<typename T>
inline T LSPAnyConverter::ExtractNumber(const protocol::LSPAny& any)
T LSPAnyConverter::ExtractNumber(const protocol::LSPAny& any)
{
if (any.Is<protocol::integer>())
if constexpr (std::is_integral_v<T>)
{
return static_cast<T>(any.Get<protocol::integer>());
if (any.Is<protocol::uinteger>())
return static_cast<T>(any.Get<protocol::uinteger>());
if (any.Is<protocol::integer>())
return static_cast<T>(any.Get<protocol::integer>());
}
else if (any.Is<protocol::uinteger>())
else if constexpr (std::is_floating_point_v<T>)
{
return static_cast<T>(any.Get<protocol::uinteger>());
}
else if (any.Is<protocol::decimal>())
{
return static_cast<T>(any.Get<protocol::decimal>());
}
else
{
throw ConversionError("LSPAny does not contain a number (integer/uinteger/decimal)");
if (any.Is<protocol::decimal>())
return static_cast<T>(any.Get<protocol::decimal>());
}
throw ConversionError("LSPAny does not contain a compatible numeric type");
}
template<typename T>
inline T LSPAnyConverter::ConvertViaJson(const protocol::LSPAny& any)
T LSPAnyConverter::ConvertViaJson(const protocol::LSPAny& any)
{
try
{
// 序列化 LSPAny 为 JSON
if (any.Is<std::nullptr_t>())
throw ConversionError("LSPAny is null; cannot convert to target type");
std::string json;
auto ec = glz::write_json(any.value, json);
auto ec = glz::write_json(any, 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);
T obj;
ec = glz::read_json(obj, json);
if (ec)
throw ConversionError("Failed to parse JSON to target type: " + std::string(glz::format_error(ec, json)));
return result;
return obj;
}
catch (const std::exception& e)
{
@ -218,23 +221,21 @@ namespace lsp::transform
}
template<typename T>
inline protocol::LSPAny LSPAnyConverter::SerializeViaJson(const T& obj)
protocol::LSPAny LSPAnyConverter::SerializeViaJson(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);
protocol::LSPAny any;
ec = glz::read_json(any, json);
if (ec)
throw ConversionError("Failed to parse JSON to LSPAny: " + std::string(glz::format_error(ec, json)));
return result;
return any;
}
catch (const std::exception& e)
{

View File

@ -1,186 +0,0 @@
#include <spdlog/spdlog.h>
#include <mutex>
#include "./dispacther.hpp"
#include "../protocol/transform/facade.hpp"
namespace lsp::core
{
RequestDispatcher::RequestDispatcher()
{
// 预创建 lifecycle callback避免每次调用时都创建新的 lambda
context_lifecycle_callback_ = [this](provider::ServerLifecycleEvent event) {
NotifyAllLifecycleListeners(event);
};
}
void RequestDispatcher::SetRequestScheduler(scheduler::AsyncExecutor* scheduler)
{
async_executor_ = scheduler;
spdlog::debug("Request scheduler set");
}
void RequestDispatcher::SetManagerHub(manager::ManagerHub* manager_hub)
{
manager_hub_ = manager_hub;
spdlog::debug("Manager hub bound to dispatcher");
}
void RequestDispatcher::RegisterRequestProvider(std::shared_ptr<provider::IRequestProvider> provider)
{
std::unique_lock<std::shared_mutex> lock(providers_mutex_);
std::string method = provider->GetMethod();
providers_[method] = provider;
}
void RequestDispatcher::RegisterNotificationProvider(std::shared_ptr<provider::INotificationProvider> provider)
{
std::unique_lock<std::shared_mutex> lock(notification_providers_mutex_);
std::string method = provider->GetMethod();
notification_providers_[method] = provider;
}
void RequestDispatcher::RegisterLifecycleCallback(provider::LifecycleCallback callback)
{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
lifecycle_callbacks_.push_back(std::move(callback));
}
std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request)
{
provider::ExecutionContext context(context_lifecycle_callback_, *async_executor_, *manager_hub_);
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
auto it = providers_.find(request.method);
if (it != providers_.end())
{
auto provider = it->second;
lock.unlock();
try
{
spdlog::debug("Dispatching request [{}] to provider [{}]", request.method, provider->GetProviderName());
return provider->ProvideResponse(request, context);
}
catch (const std::exception& e)
{
spdlog::error("Provider error for method [{}]: {}", request.method, e.what());
return provider::BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, e.what());
}
}
return HandleUnknownRequest(request);
}
void RequestDispatcher::Dispatch(const protocol::NotificationMessage& notification)
{
provider::ExecutionContext context(context_lifecycle_callback_, *async_executor_, *manager_hub_);
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
auto it = notification_providers_.find(notification.method);
if (it != notification_providers_.end())
{
auto provider = it->second;
lock.unlock();
try
{
spdlog::debug("Dispatching notification [{}] to provider [{}]", notification.method, provider->GetProviderName());
provider->HandleNotification(notification, context);
return;
}
catch (const std::exception& e)
{
spdlog::error("Notification provider [{}] threw exception for method '{}': {}", provider->GetProviderName(), notification.method, e.what());
return;
}
}
HandleUnknownNotification(notification);
}
bool RequestDispatcher::SupportsRequest(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::GetSupportedRequests() const
{
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
std::vector<std::string> methods;
methods.reserve(providers_.size());
for (const auto& [method, _] : providers_)
methods.push_back(method);
return methods;
}
std::vector<std::string> RequestDispatcher::GetSupportedNotifications() const
{
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
std::vector<std::string> methods;
methods.reserve(notification_providers_.size());
for (const auto& [method, provider] : notification_providers_)
methods.push_back(method);
return methods;
}
std::vector<std::string> RequestDispatcher::GetAllSupportedMethods() const
{
auto requests = GetSupportedRequests();
auto notifications = GetSupportedNotifications();
// 合并两个列表
requests.insert(requests.end(), notifications.begin(), notifications.end());
return requests;
}
void RequestDispatcher::NotifyAllLifecycleListeners(provider::ServerLifecycleEvent event)
{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
std::string event_name;
switch (event)
{
case provider::ServerLifecycleEvent::kInitializing:
event_name = "Initializing";
break;
case provider::ServerLifecycleEvent::kInitialized:
event_name = "Initialized";
break;
case provider::ServerLifecycleEvent::kInitializeFailed:
event_name = "InitializeFailed";
break;
case provider::ServerLifecycleEvent::kShuttingDown:
event_name = "ShuttingDown";
break;
case provider::ServerLifecycleEvent::kShutdown:
event_name = "Shutdown";
break;
}
spdlog::info("Lifecycle event: {}", event_name);
for (const auto& callback : lifecycle_callbacks_)
{
try
{
callback(event);
}
catch (const std::exception& e)
{
spdlog::error("Lifecycle callback error: {}", e.what());
}
}
}
std::string RequestDispatcher::HandleUnknownRequest(const protocol::RequestMessage& request)
{
return provider::BuildErrorResponseMessage(request, protocol::ErrorCodes::MethodNotFound, "Method not found: " + request.method);
}
void RequestDispatcher::HandleUnknownNotification(const protocol::NotificationMessage& notification)
{
spdlog::debug("No handler found for notification: {}", notification.method);
// 通知没有响应,所以只记录日志
}
}

View File

@ -0,0 +1,296 @@
module;
export module lsp.core.dispacther;
import spdlog;
import std;
import lsp.protocol.types;
import lsp.codec.facade;
import lsp.scheduler.async_executor;
import lsp.manager.manager_hub;
namespace transform = lsp::codec;
export namespace lsp::core
{
enum class ServerLifecycleEvent
{
kInitializing,
kInitialized,
kInitializeFailed,
kShuttingDown,
kShutdown
};
using LifecycleCallback = std::function<void(ServerLifecycleEvent)>;
class IProvider
{
public:
virtual ~IProvider() = default;
virtual std::string GetMethod() const = 0;
virtual std::string GetProviderName() const = 0;
};
class IRequestProvider : public IProvider
{
public:
virtual ~IRequestProvider() = default;
virtual std::string ProvideResponse(const protocol::RequestMessage& request, class ExecutionContext& execution_context) = 0;
};
class INotificationProvider : public IProvider
{
public:
virtual ~INotificationProvider() = default;
virtual void HandleNotification(const protocol::NotificationMessage& notification, class ExecutionContext& execution_context) = 0;
};
class ExecutionContext
{
public:
ExecutionContext(LifecycleCallback lifecycle_callback,
scheduler::AsyncExecutor& scheduler,
manager::ManagerHub& manager_hub) :
lifecycle_callback_(lifecycle_callback),
async_executor_(scheduler),
manager_hub_(manager_hub) {}
scheduler::AsyncExecutor& GetScheduler() const { return async_executor_; }
manager::ManagerHub& GetManagerHub() const { return manager_hub_; }
void TriggerLifecycleEvent(ServerLifecycleEvent event) const
{
if (lifecycle_callback_)
lifecycle_callback_(event);
}
private:
LifecycleCallback lifecycle_callback_;
scheduler::AsyncExecutor& async_executor_;
manager::ManagerHub& manager_hub_;
};
std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message);
class RequestDispatcher
{
public:
RequestDispatcher();
~RequestDispatcher() = default;
void SetRequestScheduler(scheduler::AsyncExecutor* scheduler);
void SetManagerHub(manager::ManagerHub* manager_hub);
void RegisterRequestProvider(std::shared_ptr<IRequestProvider> provider);
void RegisterNotificationProvider(std::shared_ptr<INotificationProvider> provider);
void RegisterLifecycleCallback(LifecycleCallback callback);
std::string Dispatch(const protocol::RequestMessage& request);
void Dispatch(const protocol::NotificationMessage& notification);
bool SupportsRequest(const std::string& method) const;
bool SupportsNotification(const std::string& method) const;
std::vector<std::string> GetSupportedRequests() const;
std::vector<std::string> GetSupportedNotifications() const;
std::vector<std::string> GetAllSupportedMethods() const;
private:
void NotifyAllLifecycleListeners(ServerLifecycleEvent event);
std::string HandleUnknownRequest(const protocol::RequestMessage& request);
void HandleUnknownNotification(const protocol::NotificationMessage& notification);
private:
mutable std::shared_mutex providers_mutex_;
std::unordered_map<std::string, std::shared_ptr<IRequestProvider>> providers_;
mutable std::shared_mutex notification_providers_mutex_;
std::unordered_map<std::string, std::shared_ptr<INotificationProvider>> notification_providers_;
std::mutex callbacks_mutex_;
std::vector<LifecycleCallback> lifecycle_callbacks_;
LifecycleCallback context_lifecycle_callback_;
scheduler::AsyncExecutor* async_executor_ = nullptr;
manager::ManagerHub* manager_hub_ = nullptr;
};
}
namespace lsp::core
{
RequestDispatcher::RequestDispatcher()
{
context_lifecycle_callback_ = [this](ServerLifecycleEvent event) {
NotifyAllLifecycleListeners(event);
};
}
void RequestDispatcher::SetRequestScheduler(scheduler::AsyncExecutor* scheduler)
{
async_executor_ = scheduler;
spdlog::debug("Request scheduler set");
}
void RequestDispatcher::SetManagerHub(manager::ManagerHub* manager_hub)
{
manager_hub_ = manager_hub;
spdlog::debug("Manager hub bound to dispatcher");
}
void RequestDispatcher::RegisterRequestProvider(std::shared_ptr<IRequestProvider> provider)
{
std::unique_lock<std::shared_mutex> lock(providers_mutex_);
std::string method = provider->GetMethod();
providers_[method] = provider;
}
void RequestDispatcher::RegisterNotificationProvider(std::shared_ptr<INotificationProvider> provider)
{
std::unique_lock<std::shared_mutex> lock(notification_providers_mutex_);
std::string method = provider->GetMethod();
notification_providers_[method] = provider;
}
void RequestDispatcher::RegisterLifecycleCallback(LifecycleCallback callback)
{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
lifecycle_callbacks_.push_back(std::move(callback));
}
std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request)
{
std::shared_ptr<IRequestProvider> provider = nullptr;
{
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
auto it = providers_.find(request.method);
if (it != providers_.end())
provider = it->second;
}
if (!provider)
return HandleUnknownRequest(request);
if (!async_executor_ || !manager_hub_)
{
spdlog::error("RequestDispatcher dependencies not set");
return "{}";
}
ExecutionContext context(context_lifecycle_callback_, *async_executor_, *manager_hub_);
return provider->ProvideResponse(request, context);
}
void RequestDispatcher::Dispatch(const protocol::NotificationMessage& notification)
{
std::shared_ptr<INotificationProvider> provider = nullptr;
{
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
auto it = notification_providers_.find(notification.method);
if (it != notification_providers_.end())
provider = it->second;
}
if (!provider)
{
HandleUnknownNotification(notification);
return;
}
if (!async_executor_ || !manager_hub_)
{
spdlog::error("NotificationDispatcher dependencies not set");
return;
}
ExecutionContext context(context_lifecycle_callback_, *async_executor_, *manager_hub_);
provider->HandleNotification(notification, context);
}
bool RequestDispatcher::SupportsRequest(const std::string& method) const
{
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
return providers_.contains(method);
}
bool RequestDispatcher::SupportsNotification(const std::string& method) const
{
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
return notification_providers_.contains(method);
}
std::vector<std::string> RequestDispatcher::GetSupportedRequests() const
{
std::vector<std::string> methods;
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
methods.reserve(providers_.size());
for (const auto& [method, _] : providers_)
methods.push_back(method);
return methods;
}
std::vector<std::string> RequestDispatcher::GetSupportedNotifications() const
{
std::vector<std::string> methods;
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
methods.reserve(notification_providers_.size());
for (const auto& [method, _] : notification_providers_)
methods.push_back(method);
return methods;
}
std::vector<std::string> RequestDispatcher::GetAllSupportedMethods() const
{
std::vector<std::string> methods = GetSupportedRequests();
std::vector<std::string> notifications = GetSupportedNotifications();
methods.insert(methods.end(), notifications.begin(), notifications.end());
return methods;
}
void RequestDispatcher::NotifyAllLifecycleListeners(ServerLifecycleEvent event)
{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
for (const auto& callback : lifecycle_callbacks_)
{
if (callback)
callback(event);
}
}
std::string RequestDispatcher::HandleUnknownRequest(const protocol::RequestMessage& request)
{
spdlog::warn("No request provider registered for method: {}", request.method);
protocol::ResponseMessage response;
response.id = request.id;
response.error = protocol::ResponseError{
.jsonrpc = "2.0",
.code = static_cast<protocol::integer>(protocol::ErrorCodes::MethodNotFound),
.message = "Method not supported",
.data = std::nullopt
};
auto json = transform::Serialize(response);
return json.value_or("{}");
}
void RequestDispatcher::HandleUnknownNotification(const protocol::NotificationMessage& notification)
{
spdlog::warn("No notification provider registered for method: {}", notification.method);
}
std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message)
{
protocol::ResponseMessage response;
response.id = request.id;
protocol::ResponseError error;
error.code = static_cast<protocol::integer>(code);
error.message = message;
response.error = error;
auto json = transform::Serialize(response);
if (json.has_value())
return json.value();
spdlog::error("Failed to serialize error response.");
return R"({"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":"Failed to serialize error response"}})";
}
}

View File

@ -1,55 +0,0 @@
#pragma once
#include <memory>
#include <vector>
#include <shared_mutex>
#include "../protocol/protocol.hpp"
#include "../provider/base/interface.hpp"
#include "../manager/manager_hub.hpp"
namespace lsp::core
{
class RequestDispatcher
{
public:
RequestDispatcher();
~RequestDispatcher() = default;
void SetRequestScheduler(scheduler::AsyncExecutor* scheduler);
void SetManagerHub(manager::ManagerHub* manager_hub);
void RegisterRequestProvider(std::shared_ptr<provider::IRequestProvider> provider);
void RegisterNotificationProvider(std::shared_ptr<provider::INotificationProvider> provider);
void RegisterLifecycleCallback(provider::LifecycleCallback callback);
std::string Dispatch(const protocol::RequestMessage& request);
void Dispatch(const protocol::NotificationMessage& notification);
bool SupportsRequest(const std::string& method) const;
bool SupportsNotification(const std::string& method) const;
std::vector<std::string> GetSupportedRequests() const;
std::vector<std::string> GetSupportedNotifications() const;
std::vector<std::string> GetAllSupportedMethods() const;
private:
void NotifyAllLifecycleListeners(provider::ServerLifecycleEvent event);
std::string HandleUnknownRequest(const protocol::RequestMessage& request);
void HandleUnknownNotification(const protocol::NotificationMessage& notification);
private:
mutable std::shared_mutex providers_mutex_;
std::unordered_map<std::string, std::shared_ptr<provider::IRequestProvider>> providers_;
mutable std::shared_mutex notification_providers_mutex_;
std::unordered_map<std::string, std::shared_ptr<provider::INotificationProvider>> notification_providers_;
std::mutex callbacks_mutex_;
std::vector<provider::LifecycleCallback> lifecycle_callbacks_;
provider::LifecycleCallback context_lifecycle_callback_;
// 依赖
scheduler::AsyncExecutor* async_executor_ = nullptr;
manager::ManagerHub* manager_hub_ = nullptr;
};
}

View File

@ -1,351 +0,0 @@
#include <exception>
#include <iostream>
#include <thread>
#include <spdlog/spdlog.h>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
#include "../provider/base/bootstrap.hpp"
#include "../manager/bootstrap.hpp"
#include "../protocol/transform/facade.hpp"
#include "../utils/args_parser.hpp"
#include "./server.hpp"
namespace lsp::core
{
LspServer::LspServer(size_t concurrency) : async_executor_(concurrency)
{
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
InitializeManagerHub();
RegisterProviders();
spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetAllSupportedMethods().size());
}
LspServer::~LspServer()
{
is_shutting_down_ = true;
spdlog::info("LSP server shutting down...");
}
void LspServer::Run()
{
spdlog::info("LSP server starting main loop...");
// 设置二进制模式
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stdin), _O_BINARY);
#endif
while (!is_shutting_down_)
{
try
{
std::optional<std::string> message = ReadMessage();
if (!message)
{
if (std::cin.eof())
{
spdlog::info("End of input stream, exiting main loop");
break; // EOF
}
spdlog::debug("No message received, continuing...");
std::this_thread::sleep_for(std::chrono::milliseconds(5));
continue;
}
HandleMessage(*message);
}
catch (const std::exception& e)
{
spdlog::error("Error in main loop: {}", e.what());
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
spdlog::info("LSP server main loop ended");
}
std::optional<std::string> LspServer::ReadMessage()
{
std::string line;
size_t content_length = 0;
// 读取 LSP Header
while (std::getline(std::cin, line))
{
// 去掉尾部 \r
if (!line.empty() && line.back() == '\r')
{
line.pop_back();
}
if (line.empty())
{
break; // 空行表示 header 结束
}
if (line.find("Content-Length:") == 0)
{
std::string length_str = line.substr(15); // 跳过 "Content-Length:"
size_t start = length_str.find_first_not_of(" ");
if (start != std::string::npos)
{
length_str = length_str.substr(start);
try
{
content_length = std::stoul(length_str);
spdlog::trace("Content-Length: {}", content_length);
}
catch (const std::exception& e)
{
spdlog::error("Failed to parse Content-Length: {}", e.what());
return std::nullopt;
}
}
}
}
if (content_length == 0)
{
spdlog::debug("No Content-Length found in header");
return std::nullopt;
}
// 读取内容体
std::string body(content_length, '\0');
std::cin.read(&body[0], content_length);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{
spdlog::error("Read incomplete message body, expected: {}, got: {}", content_length, std::cin.gcount());
return std::nullopt;
}
spdlog::trace("Received message: {}", body);
return body;
}
void LspServer::HandleMessage(const std::string& raw_message)
{
if (auto notification = transform::Deserialize<protocol::NotificationMessage>(raw_message))
HandleNotification(*notification);
else if (auto request = transform::Deserialize<protocol::RequestMessage>(raw_message))
HandleRequest(*request);
else if (auto response = transform::Deserialize<protocol::ResponseMessage>(raw_message))
HandleResponse(*response);
else
spdlog::error("Failed to deserialize message as any LSP message type");
}
void LspServer::HandleRequest(const protocol::RequestMessage& request)
{
std::string request_id = transform::debug::GetIdString(request.id);
spdlog::debug("Processing request - id: {}, method: {}", request_id, request.method);
// 检查是否可以处理请求
if (!CanProcessRequest(request.method))
{
SendStateError(request);
}
else
{
// 决定同步还是异步处理
if (RequiresSyncProcessing(request.method))
{
SendResponse(dispatcher_.Dispatch(request));
}
else
{
// 异步处理
async_executor_.Submit(request_id, [this, request]() -> std::optional<std::string> {
if (is_shutting_down_)
{
spdlog::debug("Skipping request {} due to shutdown", request.method);
return std::nullopt;
}
try
{
return dispatcher_.Dispatch(request);
}
catch (const std::exception& e)
{
spdlog::error("Request processing failed: {}", e.what());
return provider::BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, e.what());
} }, [this](const std::optional<std::string>& response, bool cancelled) {
if (!cancelled && response)
SendResponse(*response); });
}
spdlog::debug("Processing request method: {}", request.method);
}
}
void LspServer::HandleNotification(const protocol::NotificationMessage& notification)
{
spdlog::debug("Processing notification - method: {}", notification.method);
try
{
dispatcher_.Dispatch(notification);
}
catch (const std::exception& e)
{
spdlog::error("Notification processing failed for '{}': {}", notification.method, e.what());
}
}
void LspServer::HandleResponse(const protocol::ResponseMessage& response)
{
std::string id_str = transform::debug::GetIdString(response.id);
spdlog::debug("Received response - id: {}", id_str);
}
void LspServer::OnLifecycleEvent(provider::ServerLifecycleEvent event)
{
switch (event)
{
case provider::ServerLifecycleEvent::kInitializing:
spdlog::info("Server initializing...");
break;
case provider::ServerLifecycleEvent::kInitialized:
is_initialized_ = true;
spdlog::info("Server initialized successfully");
break;
case provider::ServerLifecycleEvent::kInitializeFailed:
is_initialized_ = false;
spdlog::error("Server initialization failed");
break;
case provider::ServerLifecycleEvent::kShuttingDown:
is_shutting_down_ = true;
spdlog::info("Server entering shutdown state");
break;
case provider::ServerLifecycleEvent::kShutdown:
is_shutting_down_ = true;
spdlog::info("Server shutdown complete");
break;
}
}
bool LspServer::RequiresSyncProcessing(const std::string& method) const
{
static const std::unordered_set<std::string> sync_methods = {
"initialize",
"shutdown"
};
return sync_methods.count(method) > 0;
}
bool LspServer::CanProcessRequest(const std::string& method) const
{
if (!is_initialized_)
return method == "initialize" || method == "exit";
if (is_shutting_down_)
return method == "exit";
return true;
}
void LspServer::HandleCancelRequest(const protocol::NotificationMessage& notification)
{
spdlog::debug("Handle cancel request - method: {}", notification.method);
try
{
if (!notification.params)
{
spdlog::warn("Cancel request missing params");
return;
}
auto params = transform::FromLSPAny.template operator()<protocol::CancelParams>(notification.params.value());
std::string id_to_cancel = transform::debug::GetIdString(params.id);
bool cancelled = async_executor_.Cancel(id_to_cancel);
spdlog::debug("Cancel request {} {}", id_to_cancel, cancelled ? "succeeded" : "not found");
}
catch (const std::exception& e)
{
spdlog::error("Failed to handle cancel request: {}", e.what());
}
}
void LspServer::SendResponse(const std::string& response)
{
std::lock_guard<std::mutex> lock(output_mutex_);
size_t byte_length = response.length();
std::string header = "Content-Length: " + std::to_string(byte_length) + "\r\n\r\n";
// 发送 header 和 body
std::cout.write(header.c_str(), header.length());
std::cout.write(response.c_str(), response.length());
std::cout.flush();
spdlog::trace("Response sent - length: {}", byte_length);
spdlog::trace("Response sent - body: {}", response);
}
void LspServer::InitializeManagerHub()
{
spdlog::debug("Initializing manager hub...");
auto& config = utils::ArgsParser::Instance().GetConfig();
// 准备系统库路径
std::vector<std::string> system_lib_paths = {
config.interpreter_path + "funcext",
config.interpreter_path + "data"
};
manager::bootstrap::InitializeManagerHub(manager_hub_, async_executor_, system_lib_paths);
spdlog::debug("Manager hub created, system library loading in background");
}
void LspServer::RegisterProviders()
{
spdlog::debug("Registering providers...");
// 设置调度器
dispatcher_.SetRequestScheduler(&async_executor_);
// 注册生命周期回调
dispatcher_.RegisterLifecycleCallback(
[this](provider::ServerLifecycleEvent event) {
OnLifecycleEvent(event);
});
// 设置 SessionProvider 通过 ExecutionContext 访问)
dispatcher_.SetManagerHub(&manager_hub_);
// 注册所有 Provider
provider::RegisterAllProviders(dispatcher_);
spdlog::debug("Providers registered");
}
void LspServer::SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message)
{
spdlog::warn("Sending error response - method: {}, code: {}, message: {}", request.method, static_cast<int>(code), message);
std::string error_response = provider::BuildErrorResponseMessage(request, code, message);
SendResponse(error_response);
}
void LspServer::SendStateError(const protocol::RequestMessage& request)
{
if (!is_initialized_)
SendError(request, protocol::ErrorCodes::ServerNotInitialized, "Server not initialized");
else if (is_shutting_down_)
SendError(request, protocol::ErrorCodes::InvalidRequest, "Server is shutting down, only 'exit' is allowed");
else
SendError(request, protocol::ErrorCodes::InternalError, "Request not allowed in current state");
}
}

View File

@ -0,0 +1,432 @@
module;
#ifdef _WIN32
#include <cstdio>
#include <fcntl.h>
#include <io.h>
#endif
export module lsp.core.server;
import spdlog;
import std;
import lsp.core.dispacther;
import lsp.protocol;
import lsp.codec.facade;
import lsp.manager.manager_hub;
import lsp.scheduler.async_executor;
import lsp.provider.base.interface;
import lsp.provider.base.registry;
import lsp.provider.initialize.initialize;
import lsp.provider.initialized.initialized;
import lsp.provider.text_document.did_open;
import lsp.provider.text_document.did_change;
import lsp.provider.text_document.did_close;
import lsp.provider.text_document.completion;
import lsp.provider.trace.set_trace;
import lsp.provider.shutdown.shutdown;
import lsp.provider.cancel_request.cancel_request;
import lsp.provider.exit.exit;
import lsp.provider.completion_item.resolve;
namespace transform = lsp::codec;
export namespace lsp::core
{
class LspServer
{
public:
LspServer(std::size_t concurrency = std::thread::hardware_concurrency());
~LspServer();
void Run();
private:
// 读取LSP消息
std::optional<std::string> ReadMessage();
// 处理LSP请求 - 返回序列化的响应或空字符串(对于通知)
void HandleMessage(const std::string& raw_message);
// 发送LSP响应
void SendResponse(const std::string& response);
// 处理不同类型的消息
void HandleRequest(const protocol::RequestMessage& request);
void HandleNotification(const protocol::NotificationMessage& notification);
void HandleResponse(const protocol::ResponseMessage& response);
// 生命周期事件处理
void OnLifecycleEvent(provider::ServerLifecycleEvent event);
// 判断是否需要同步处理
bool RequiresSyncProcessing(const std::string& method) const;
// 检查是否可以处理请求
bool CanProcessRequest(const std::string& method) const;
// 处理取消请求
void HandleCancelRequest(const protocol::NotificationMessage& notification);
private:
void InitializeManagerHub();
void RegisterProviders();
// 错误处理
void SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message);
void SendStateError(const protocol::RequestMessage& request);
private:
RequestDispatcher dispatcher_;
scheduler::AsyncExecutor async_executor_;
manager::ManagerHub manager_hub_;
std::atomic<bool> is_initialized_ = false;
std::atomic<bool> is_shutting_down_ = false;
std::mutex output_mutex_;
};
}
namespace lsp::core
{
LspServer::LspServer(std::size_t concurrency) : async_executor_(concurrency)
{
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
InitializeManagerHub();
RegisterProviders();
spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetAllSupportedMethods().size());
}
LspServer::~LspServer()
{
is_shutting_down_ = true;
spdlog::info("LSP server shutting down...");
}
void LspServer::Run()
{
spdlog::info("LSP server starting main loop...");
// 设置二进制模式
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stdin), _O_BINARY);
#endif
while (!is_shutting_down_)
{
try
{
std::optional<std::string> message = ReadMessage();
if (!message)
{
if (std::cin.eof())
{
spdlog::info("End of input stream, exiting main loop");
break; // EOF
}
spdlog::debug("No message received, continuing...");
std::this_thread::sleep_for(std::chrono::milliseconds(5));
continue;
}
HandleMessage(*message);
}
catch (const std::exception& e)
{
spdlog::error("Error in main loop: {}", e.what());
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
spdlog::info("LSP server main loop ended");
}
std::optional<std::string> LspServer::ReadMessage()
{
std::string line;
std::size_t content_length = 0;
// 读取 LSP Header
while (std::getline(std::cin, line))
{
// 去掉尾部 \\r
if (!line.empty() && line.back() == '\r')
{
line.pop_back();
}
if (line.empty())
{
break; // 空行表示 header 结束
}
if (line.rfind("Content-Length:", 0) == 0)
{
std::string length_str = line.substr(15); // 跳过 "Content-Length:"
std::size_t start = length_str.find_first_not_of(' ');
if (start != std::string::npos)
{
length_str = length_str.substr(start);
try
{
content_length = std::stoul(length_str);
spdlog::trace("Content-Length: {}", content_length);
}
catch (const std::exception& e)
{
spdlog::error("Failed to parse Content-Length: {}", e.what());
return std::nullopt;
}
}
}
}
if (content_length == 0)
{
spdlog::debug("No Content-Length found in header");
return std::nullopt;
}
// 读取内容体
std::string body(content_length, '\0');
std::cin.read(&body[0], content_length);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{
spdlog::error("Failed to read expected content length: {} bytes, got {} bytes", content_length, std::cin.gcount());
return std::nullopt;
}
spdlog::trace("Received message: {}", body);
return body;
}
void LspServer::HandleMessage(const std::string& raw_message)
{
auto any = transform::Deserialize<protocol::LSPAny>(raw_message);
if (!any || !any->Is<protocol::LSPObject>())
{
spdlog::warn("Failed to parse message: {}", raw_message);
return;
}
const auto& obj = any->Get<protocol::LSPObject>();
const bool has_id = obj.find("id") != obj.end();
const bool has_method = obj.find("method") != obj.end();
const bool has_result = obj.find("result") != obj.end();
const bool has_error = obj.find("error") != obj.end();
if (has_method && has_id)
{
if (auto request = transform::Deserialize<protocol::RequestMessage>(raw_message))
HandleRequest(*request);
else
spdlog::warn("Failed to parse request message");
return;
}
if (has_method)
{
if (auto notification = transform::Deserialize<protocol::NotificationMessage>(raw_message))
HandleNotification(*notification);
else
spdlog::warn("Failed to parse notification message");
return;
}
if (has_id && (has_result || has_error))
{
if (auto response = transform::Deserialize<protocol::ResponseMessage>(raw_message))
HandleResponse(*response);
else
spdlog::warn("Failed to parse response message");
return;
}
spdlog::warn("Unrecognized message: {}", raw_message);
}
void LspServer::SendResponse(const std::string& response)
{
if (response.empty())
return;
std::lock_guard<std::mutex> lock(output_mutex_);
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
<< response << std::flush;
}
void LspServer::HandleRequest(const protocol::RequestMessage& request)
{
spdlog::debug("Handling request: {}", request.method);
if (request.method == "shutdown")
{
auto response = dispatcher_.Dispatch(request);
SendResponse(response);
is_shutting_down_ = true;
return;
}
// 未初始化时的特殊处理
if (!is_initialized_ && request.method != "initialize")
{
SendStateError(request);
return;
}
auto response = dispatcher_.Dispatch(request);
SendResponse(response);
}
void LspServer::HandleNotification(const protocol::NotificationMessage& notification)
{
spdlog::debug("Handling notification: {}", notification.method);
if (notification.method == "exit")
{
is_shutting_down_ = true;
return;
}
// 处理取消请求
if (notification.method == "$/cancelRequest")
{
HandleCancelRequest(notification);
return;
}
// 未初始化时只接受 initialized/exit
if (!is_initialized_ && notification.method != "initialized" && notification.method != "exit")
{
spdlog::warn("Server not initialized; ignoring notification: {}", notification.method);
return;
}
dispatcher_.Dispatch(notification);
}
void LspServer::HandleResponse(const protocol::ResponseMessage& response)
{
const std::string id = response.id.value_or("<no id>");
spdlog::debug("Received response: {}", id);
// 当前服务器作为 client 的场景较少,这里暂时不处理
}
void LspServer::OnLifecycleEvent(provider::ServerLifecycleEvent event)
{
switch (event)
{
case provider::ServerLifecycleEvent::kInitialized:
is_initialized_ = true;
spdlog::info("Server initialized");
break;
case provider::ServerLifecycleEvent::kShutdown:
is_shutting_down_ = true;
spdlog::info("Server shutting down");
break;
default:
break;
}
}
bool LspServer::RequiresSyncProcessing(const std::string& method) const
{
static const std::unordered_set<std::string> kSyncMethods = {
"initialize",
"shutdown",
"exit",
"$/setTrace",
};
return kSyncMethods.contains(method);
}
bool LspServer::CanProcessRequest(const std::string& method) const
{
if (is_shutting_down_)
return false;
// 初始化前,只处理 initialize 请求
if (!is_initialized_ && method != "initialize")
return false;
return true;
}
void LspServer::HandleCancelRequest(const protocol::NotificationMessage& notification)
{
if (!notification.params.has_value())
return;
protocol::CancelParams params =
transform::FromLSPAny.template operator()<protocol::CancelParams>(notification.params.value());
const std::string id_string = std::visit([](const auto& value) -> std::string {
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, int>)
return std::to_string(value);
else
return value;
},
params.id);
spdlog::debug("Cancel request received for id: {}", id_string);
// TODO: 实现请求取消逻辑
}
void LspServer::InitializeManagerHub()
{
manager_hub_.Initialize();
}
void LspServer::RegisterProviders()
{
dispatcher_.SetRequestScheduler(&async_executor_);
dispatcher_.SetManagerHub(&manager_hub_);
dispatcher_.RegisterLifecycleCallback([this](ServerLifecycleEvent event) {
OnLifecycleEvent(event);
});
spdlog::info("Registering LSP providers...");
provider::RegisterProvider<provider::Initialize>(dispatcher_);
provider::RegisterProvider<provider::Initialized>(dispatcher_);
provider::RegisterProvider<provider::text_document::DidOpen>(dispatcher_);
provider::RegisterProvider<provider::text_document::DidChange>(dispatcher_);
provider::RegisterProvider<provider::text_document::DidClose>(dispatcher_);
provider::RegisterProvider<provider::text_document::Completion>(dispatcher_);
// provider::RegisterProvider<provider::text_document::SemanticTokensRange>(dispatcher_);
// provider::RegisterProvider<provider::text_document::SemanticTokensFull>(dispatcher_);
// provider::RegisterProvider<provider::text_document::SemanticTokensFullDelta>(dispatcher_);
provider::RegisterProvider<provider::completion_item::Resolve>(dispatcher_);
provider::RegisterProvider<provider::SetTrace>(dispatcher_);
provider::RegisterProvider<provider::Shutdown>(dispatcher_);
provider::RegisterProvider<provider::CancelRequest>(dispatcher_);
provider::RegisterProvider<provider::Exit>(dispatcher_);
spdlog::info("Registered {} LSP providers", dispatcher_.GetAllSupportedMethods().size());
}
void LspServer::SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message)
{
protocol::ResponseMessage response;
response.id = request.id;
protocol::ResponseError error;
error.code = static_cast<protocol::integer>(code);
error.message = message;
response.error = error;
auto json = transform::Serialize(response);
if (json)
{
SendResponse(*json);
}
}
void LspServer::SendStateError(const protocol::RequestMessage& request)
{
SendError(request, protocol::ErrorCodes::ServerNotInitialized, "Server not initialized");
}
}

View File

@ -1,62 +0,0 @@
#pragma once
#include <atomic>
#include <optional>
#include <string>
#include "./dispacther.hpp"
#include "../scheduler/async_executor.hpp"
#include "../manager/manager_hub.hpp"
namespace lsp::core
{
class LspServer
{
public:
LspServer(size_t concurrency = std::thread::hardware_concurrency());
~LspServer();
void Run();
private:
// 读取LSP消息
std::optional<std::string> ReadMessage();
// 处理LSP请求 - 返回序列化的响应或空字符串(对于通知)
void HandleMessage(const std::string& raw_message);
// 发送LSP响应
void SendResponse(const std::string& response);
// 处理不同类型的消息
void HandleRequest(const protocol::RequestMessage& request);
void HandleNotification(const protocol::NotificationMessage& notification);
void HandleResponse(const protocol::ResponseMessage& response);
// 生命周期事件处理
void OnLifecycleEvent(provider::ServerLifecycleEvent event);
// 判断是否需要同步处理
bool RequiresSyncProcessing(const std::string& method) const;
// 检查是否可以处理请求
bool CanProcessRequest(const std::string& method) const;
// 处理取消请求
void HandleCancelRequest(const protocol::NotificationMessage& notification);
private:
void InitializeManagerHub();
void RegisterProviders();
// 错误处理
void SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message);
void SendStateError(const protocol::RequestMessage& request);
private:
RequestDispatcher dispatcher_;
scheduler::AsyncExecutor async_executor_;
manager::ManagerHub manager_hub_;
std::atomic<bool> is_initialized_ = false;
std::atomic<bool> is_shutting_down_ = false;
std::mutex output_mutex_;
};
}

View File

@ -1,541 +0,0 @@
# 从反序列化到符号表 - 设计架构
## 📐 系统架构总览
```
┌─────────────────────────────────────────────────────────────────┐
│ Source Code │
│ (tsl) │
└────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Tree-sitter Parser │
│ (外部依赖,生成 TSTree) │
└────────────────────────┬────────────────────────────────────────┘
┌────────┐
│ TSTree │ Parse Tree (CST)
│ TSNode │
└────┬───┘
┌─────────────────────────────────────────────────────────────────┐
│ AST 反序列化层 (ast/) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ deserializer.hpp/cpp │ │
│ │ - ParseRoot() │ │
│ │ - ParseNode() │ │
│ │ - ParseVarDeclaration(), ParseFunctionDefinition()... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ types.hpp │ │
│ │ - ASTNode (variant) │ │
│ │ - VarDeclaration, FunctionDefinition, ClassDefinition │ │
│ │ - Expression, Block, Signature, Parameter... │ │
│ └─────────────────────────────────────────────────────────┘ │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────┐
│ vector<ASTNode> │ 抽象语法树
│ ParseResult │
└──────┬─────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 符号表构建层 (symbol/) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Phase 1: Collection (builder/collector.hpp/cpp) │ │
│ │ - 遍历 AST │ │
│ │ - 创建符号 (Variable, Function, Class...) │ │
│ │ - 插入符号表 │ │
│ │ - 建立作用域层次 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Phase 2: Resolution (builder/resolver.hpp/cpp) │ │
│ │ - 解析 Unit 依赖关系 │ │
│ │ - 解析类继承关系 │ │
│ │ - 检测循环依赖 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Phase 3: Validation (builder/validator.hpp/cpp) │ │
│ │ - 验证类型引用 │ │
│ │ - 验证方法覆盖 │ │
│ │ - 验证访问权限 │ │
│ │ - 验证虚方法 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Coordinator (builder/builder.hpp/cpp) │ │
│ │ - 协调三阶段流程 │ │
│ │ - 错误收集和报告 │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────┐
│ SymbolRegistry │ 完整的符号表
│ - GlobalScope │
│ - Units │
└──────┬─────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 │
│ - LSP 服务 (代码补全、跳转定义、查找引用...) │
│ - 语义分析 │
│ - 代码生成 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 🔄 数据流详解
### 1. 解析阶段 (Tree-sitter → AST)
```
Source Code
├─→ Tree-sitter Parser
│ │
│ └─→ TSTree (Concrete Syntax Tree)
│ │
│ └─→ TSNode (每个语法节点)
└─→ AST Deserializer
├─→ ParseRoot(TSNode, source)
│ │
│ └─→ ParseNode() 递归解析
│ │
│ ├─→ ParseVarDeclaration()
│ ├─→ ParseFunctionDefinition()
│ ├─→ ParseClassDefinition()
│ └─→ ...
└─→ vector<ASTNode>
└─→ AST (抽象语法树)
```
**关键点**
- Tree-sitter 生成 CST具体语法树包含所有语法细节
- Deserializer 将 CST 转换为 AST抽象语法树去除语法细节
- AST 使用 `std::variant<...>` 表示不同类型的节点
### 2. 符号表构建阶段 (AST → Symbol Table)
```
vector<ASTNode>
├─→ Phase 1: Collection
│ │
│ ├─→ 遍历每个 ASTNode
│ │ │
│ │ ├─→ Visit(UnitDefinition)
│ │ │ └─→ CreateUnit → AddUnit
│ │ │
│ │ ├─→ Visit(ClassDefinition)
│ │ │ └─→ CreateClass → Insert
│ │ │ │
│ │ │ └─→ Visit(ClassMembers)
│ │ │
│ │ ├─→ Visit(FunctionDefinition)
│ │ │ └─→ CreateFunction → InsertFunction
│ │ │ │
│ │ │ └─→ CollectFunctionBody
│ │ │
│ │ └─→ Visit(VarDeclaration)
│ │ └─→ CreateVariable → Insert
│ │
│ └─→ 结果:带有符号但引用未解析的符号表
├─→ Phase 2: Resolution
│ │
│ ├─→ ResolveUnitDependencies()
│ │ │
│ │ └─→ 对每个 Unit.uses 查找目标 Unit
│ │ └─→ 检测循环依赖
│ │
│ └─→ ResolveClassHierarchy()
│ │
│ └─→ 对每个 Class.parent_names 查找父类
│ └─→ 检测循环继承
└─→ Phase 3: Validation
├─→ ValidateTypeReferences()
│ └─→ 检查所有类型名是否存在
├─→ ValidateMethodOverrides()
│ └─→ 检查 override 是否合法
└─→ ValidateVirtualMethods()
└─→ 检查虚方法表一致性
```
---
## 🏗️ 模块职责划分
### 📦 AST 模块 (ast/)
| 组件 | 职责 | 输入 | 输出 |
|------|------|------|------|
| **deserializer** | 将 Tree-sitter 的 CST 转换为 AST | TSNode, source code | vector<ASTNode> |
| **types** | 定义 AST 节点类型 | - | 类型定义 |
**设计原则**
- 无状态转换(纯函数)
- 错误容忍(收集错误而非中断)
- 完整信息保留(位置、类型、文本)
---
### 📦 符号表模块 (symbol/)
#### 🔹 core/ - 核心类型
| 文件 | 职责 | 关键类型 |
|------|------|---------|
| **error** | 定义错误类型 | ErrorKind, Error |
| **symbol** | 定义所有符号类型 | Symbol, Variable, Function, Class... |
| **table** | 管理作用域和符号 | Table, LookupResult |
| **registry** | 管理全局资源 | SymbolRegistry |
#### 🔹 builder/ - 构建流程
| 文件 | 职责 | 主要方法 |
|------|------|---------|
| **reporter** | 收集和报告错误 | Report(), GetErrors() |
| **collector** | 阶段1: 收集符号 | Collect(), Visit() |
| **resolver** | 阶段2: 解析引用 | Resolve(), ResolveUnitDependencies() |
| **validator** | 阶段3: 验证完整性 | Validate(), ValidateTypeReferences() |
| **builder** | 协调三阶段流程 | Build() |
#### 🔹 factory/ - 工厂和管理
| 文件 | 职责 | 主要功能 |
|------|------|---------|
| **factory** | 创建符号实例 | CreateVariable(), CreateClass()... |
| **scope_manager** | 管理当前作用域 | EnterScope(), ScopeGuard (RAII) |
#### 🔹 utils/ - 工具类
| 文件 | 职责 | 主要功能 |
|------|------|---------|
| **type** | 类型操作工具 | TypeFactory, TypeQuery, TypeFormatter |
| **signature** | 签名操作工具 | SignatureComparator, SignatureFormatter |
| **class_member** | 成员查找 | FindMember(), GetAllMethods() |
| **class_hierarchy** | 层次分析 | BuildVTable(), ValidateHierarchy() |
---
## 🔗 模块间依赖关系
```
┌─────────────────────────────────────────────────────────┐
│ Application │
└──────────────────────┬──────────────────────────────────┘
│ uses
┌─────────────────────────────────────────────────────────┐
│ symbol/builder/builder │
│ (SymbolTableBuilder) │
└───────┬──────────────────────────────────┬──────────────┘
│ uses │ uses
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ symbol/builder/ │ │ symbol/core/ │
│ - collector │◄─────────────│ - registry │
│ - resolver │ uses │ - table │
│ - validator │ │ - symbol │
│ - reporter │ │ - error │
└────────┬─────────┘ └────────┬─────────┘
│ uses │
▼ │
┌──────────────────┐ │
│ symbol/factory/ │◄──────────────────────┘
│ - factory │ uses
│ - scope_manager │
└────────┬─────────┘
│ uses
┌──────────────────┐
│ symbol/utils/ │
│ - type │
│ - signature │
│ - class_member │
│ - class_hierarchy│
└──────────────────┘
```
**依赖规则**
- 上层模块依赖下层模块
- builder 依赖 core, factory, utils
- factory 依赖 core, utils
- utils 依赖 core
- core 无外部依赖(除了 ast
---
## 🎯 关键设计模式
### 1. Visitor 模式 (AST 遍历)
```cpp
// Collector 访问 AST
class Collector {
bool Visit(const ast::ASTNode& node) {
if (auto* unit = std::get_if<ast::UnitDefinition>(&node))
return CollectUnit(*unit);
if (auto* cls = std::get_if<ast::ClassDefinition>(&node))
return CollectClass(*cls);
// ...
}
};
```
**优点**
- 分离数据结构和操作
- 易于添加新的访问操作
- 类型安全(使用 variant
### 2. Factory 模式 (符号创建)
```cpp
// 统一的符号创建入口
class Factory {
static VariablePtr CreateVariable(...);
static FunctionPtr CreateFunction(...);
static ClassPtr CreateClass(...);
};
```
**优点**
- 统一创建逻辑
- 易于维护和扩展
- 可以添加缓存、验证等逻辑
### 3. RAII 模式 (作用域管理)
```cpp
// 自动管理作用域切换
class ScopeGuard {
~ScopeGuard() { /* 自动恢复 */ }
};
// 使用
auto guard = scope_manager.EnterScope(new_scope);
// 作用域内操作
// guard 析构时自动恢复
```
**优点**
- 异常安全
- 自动资源管理
- 避免忘记恢复
### 4. Builder 模式 (符号表构建)
```cpp
class SymbolTableBuilder {
bool Build(nodes) {
Collector collector(...);
collector.Collect(nodes);
ReferenceResolver resolver(...);
resolver.Resolve();
Validator validator(...);
validator.Validate();
}
};
```
**优点**
- 分步构建复杂对象
- 易于理解和维护
- 可以中断和恢复
### 5. Strategy 模式 (错误报告)
```cpp
class ErrorReporter {
void Report(ErrorKind kind, ...);
};
// 不同组件使用同一报告器
Collector collector(registry, reporter);
Resolver resolver(registry, reporter);
Validator validator(registry, reporter);
```
**优点**
- 统一错误处理
- 易于替换报告策略
- 集中管理错误
---
## 📊 性能考虑
### 1. 时间复杂度
| 阶段 | 复杂度 | 说明 |
|------|--------|------|
| **解析** | O(n) | n = 源代码大小 |
| **收集** | O(m) | m = AST 节点数 |
| **解析引用** | O(u + c) | u = Unit数, c = Class数 |
| **验证** | O(s) | s = 符号总数 |
**总体**O(n + m + s) ≈ O(n),线性时间
### 2. 空间复杂度
| 数据结构 | 空间 | 优化 |
|---------|------|------|
| **AST** | O(m) | 构建符号表后可释放 |
| **符号表** | O(s) | 使用智能指针共享 |
| **作用域树** | O(d) | d = 作用域深度,通常很小 |
**总体**O(n),线性空间
### 3. 优化技术
- **增量更新**:只重新解析修改的文件
- **缓存**:缓存符号查找结果
- **延迟加载**:按需加载 Unit 内容
- **并行处理**:独立 Unit 可并行构建
---
## 🛡️ 错误处理策略
### 错误分类
| 错误类型 | 严重性 | 处理 |
|---------|--------|------|
| **语法错误** | Fatal | Tree-sitter 处理 |
| **结构错误** | Error | 收集但继续 |
| **类型错误** | Error | 收集但继续 |
| **警告** | Warning | 记录不中断 |
### 错误恢复
```
错误发生
├─→ 记录错误信息(位置、消息)
├─→ 尝试继续处理
│ └─→ 跳过当前节点
│ 或使用默认值
└─→ 返回 false 或 Error
└─→ 上层决定是否继续
```
**原则**
- 尽可能收集多个错误
- 不因一个错误而停止整个过程
- 提供详细的错误位置和上下文
---
## 🔍 使用示例
### 完整流程
```cpp
#include "ast/deserializer.hpp"
#include "symbol/builder/builder.hpp"
int main() {
// 1. 解析源代码
std::string source = ReadFile("program.pas");
TSTree* tree = ts_parser_parse_string(parser, nullptr,
source.c_str(),
source.length());
TSNode root = ts_tree_root_node(tree);
// 2. 反序列化为 AST
auto parse_result = ast::deserializer::ParseRoot(root, source);
if (parse_result.HasErrors()) {
// 处理解析错误
for (const auto& error : parse_result.errors) {
std::cerr << error.message << std::endl;
}
}
// 3. 构建符号表
symbol::SymbolTableBuilder builder;
bool success = builder.Build(parse_result.nodes);
if (!success) {
// 处理符号表错误
for (const auto& error : builder.GetErrors()) {
std::cerr << error.ToString() << std::endl;
}
return 1;
}
// 4. 使用符号表
auto& registry = builder.GetRegistry();
// 查找符号
auto result = registry.GlobalScope()->Lookup("MyClass");
if (result) {
auto* cls = dynamic_cast<symbol::Class*>(result.symbol);
// 使用类信息...
}
// 查找 Unit
auto unit = registry.FindUnit("System");
if (unit) {
// 使用 Unit 信息...
}
return 0;
}
```
---
## 📝 总结
### 核心理念
1. **分层清晰**:解析 → AST → 符号表,每层职责明确
2. **错误容忍**:收集所有错误,不因一个错误而停止
3. **三阶段构建**:收集 → 解析 → 验证,逐步完善符号表
4. **类型安全**:使用 variant, dynamic_cast 等确保类型安全
5. **资源管理**:使用智能指针和 RAII 自动管理资源
### 扩展点
- **新的 AST 节点**:在 types.hpp 添加类型,在 deserializer 添加解析
- **新的符号类型**:在 symbol.hpp 添加类型,在 factory 添加创建方法
- **新的验证规则**:在 validator 添加验证方法
- **新的工具函数**:在 utils/ 添加工具类
### 最佳实践
- 保持模块职责单一
- 使用前向声明减少编译依赖
- 在 .cpp 文件中包含完整定义
- 使用 RAII 管理资源
- 收集错误而非立即中断

View File

@ -0,0 +1,8 @@
module;
export module lsp.language.ast;
// 导出接口分区
export import :types;
export import :deserializer;
export import :ts_utils;

View File

@ -0,0 +1,9 @@
module;
export module lsp.language.ast:deserializer;
export import :types;
// 虽然有警告,但这是正确的做法:接口分区需要实现分区来提供函数体
// 警告可以通过编译器选项 -Wno-import-implementation-partition-unit-in-interface-unit 屏蔽
import :deserializer_impl;

View File

@ -1,86 +0,0 @@
#pragma once
#include <vector>
#include "./types.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
namespace lsp::language::ast
{
// ===== 解析错误 =====
enum class ErrorSeverity
{
Warning,
Error,
Fatal
};
struct ParseError
{
Location location;
std::string node_type;
std::string message;
ErrorSeverity severity = ErrorSeverity::Error;
static ParseError Create(const Location& loc, const std::string& type, const std::string& msg)
{
return { loc, type, msg, ErrorSeverity::Error };
}
static ParseError Missing(const Location& loc, const std::string& type)
{
return Create(loc, type, "Syntax error: missing " + type);
}
static ParseError Unexpected(const Location& loc, const std::string& type, const std::string& context = "")
{
std::string msg = "Syntax error: unexpected token";
if (!context.empty())
msg += " in " + context;
return Create(loc, type, msg);
}
};
// ===== 解析结果 =====
struct ParseResult
{
std::unique_ptr<Program> root;
std::vector<ParseError> errors;
bool HasErrors() const { return !errors.empty(); }
bool IsSuccess() const { return errors.empty(); }
size_t ErrorCount() const { return errors.size(); }
};
// ===== 增量解析结果 =====
struct IncrementalParseResult
{
ParseResult result;
size_t nodes_parsed = 0; // 重新解析的节点数
size_t nodes_unchanged = 0; // 未变化的节点数
size_t TotalNodes() const { return nodes_parsed + nodes_unchanged; }
double ChangeRate() const
{
return TotalNodes() > 0 ? static_cast<double>(nodes_parsed) / TotalNodes() : 0.0;
}
};
// ===== Deserializer 类 =====
class Deserializer
{
public:
Deserializer();
~Deserializer() = default;
Deserializer(const Deserializer&) = delete;
Deserializer& operator=(const Deserializer&) = delete;
ParseResult Parse(TSNode root, const std::string& source);
IncrementalParseResult ParseIncremental(TSNode root, const std::string& source);
static std::vector<ParseError> DiagnoseSyntax(TSNode root, const std::string& source);
};
}

View File

@ -1,11 +1,16 @@
#include "./detail.hpp"
#include "./tree_sitter_utils.hpp"
#include "./deserializer.hpp"
module;
module lsp.language.ast:deserializer_impl;
import tree_sitter;
import std;
import :types;
import :detail;
import :ts_utils;
namespace lsp::language::ast
{
// ==================== Deserializer 类实现 ====================
Deserializer::Deserializer()
{
detail::RegisterStatementParsers();
@ -18,13 +23,13 @@ namespace lsp::language::ast
detail::ParseContext ctx(source, result.errors);
auto program = std::make_unique<Program>();
program->span = ts::NodeLocation(root);
program->span = ts_utils::NodeLocation(root);
uint32_t count = ts_node_child_count(root);
for (uint32_t i = 0; i < count; i++)
std::uint32_t count = ts_node_child_count(root);
for (std::uint32_t i = 0; i < count; i++)
{
TSNode child = ts_node_child(root, i);
if (ts::IsComment(child))
if (ts_utils::IsComment(child))
continue;
if (!ts_node_is_named(child))
continue;
@ -35,7 +40,6 @@ namespace lsp::language::ast
}
result.root = std::move(program);
// 收集语法错误
auto syntax_errors = detail::CollectSyntaxErrors(root, source);
result.errors.insert(result.errors.end(),
std::make_move_iterator(syntax_errors.begin()),
@ -51,13 +55,13 @@ namespace lsp::language::ast
detail::ParseContext ctx(source, result.result.errors);
auto program = std::make_unique<Program>();
program->span = ts::NodeLocation(root);
program->span = ts_utils::NodeLocation(root);
uint32_t count = ts_node_child_count(root);
for (uint32_t i = 0; i < count; i++)
std::uint32_t count = ts_node_child_count(root);
for (std::uint32_t i = 0; i < count; i++)
{
TSNode child = ts_node_child(root, i);
if (ts::IsComment(child))
if (ts_utils::IsComment(child))
continue;
if (!ts_node_is_named(child))
continue;

View File

@ -1,175 +0,0 @@
#pragma once
#include <functional>
#include <unordered_map>
#include "./types.hpp"
#include "./deserializer.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
namespace lsp::language::ast::detail
{
// ===== 统一的解析上下文 =====
class ParseContext
{
public:
ParseContext(const std::string& source, std::vector<ParseError>& errors) :
source_(source), errors_(errors) {}
const std::string& Source() const { return source_; }
std::vector<ParseError>& Errors() { return errors_; }
const std::vector<ParseError>& Errors() const { return errors_; }
private:
const std::string& source_;
std::vector<ParseError>& errors_;
};
// ===== 语句解析器注册表 =====
class StatementParserRegistry
{
public:
using ParserFunc = std::function<StatementPtr(TSNode, ParseContext&)>;
static StatementParserRegistry& Instance()
{
static StatementParserRegistry instance;
return instance;
}
void Register(const std::string& type, ParserFunc parser)
{
parsers_[type] = parser;
}
ParserFunc Get(const std::string& type) const
{
auto it = parsers_.find(type);
return it != parsers_.end() ? it->second : nullptr;
}
private:
StatementParserRegistry() = default;
StatementParserRegistry(const StatementParserRegistry&) = delete;
StatementParserRegistry& operator=(const StatementParserRegistry&) = delete;
private:
std::unordered_map<std::string, ParserFunc> parsers_;
};
// ===== 初始化 =====
void RegisterStatementParsers();
// ===== 基础辅助函数 =====
bool IsSyntaxErrorNode(TSNode node);
std::vector<ParseError> CollectSyntaxErrors(TSNode node, std::string_view source);
// ===== 操作符映射 =====
BinaryOperator StringToBinaryOperator(const std::string& op_text);
AssignmentOperator StringToAssignmentOperator(const std::string& op_text);
// 访问修饰符和方法修饰符解析
AccessModifier ParseAccessModifier(TSNode node, ParseContext& ctx);
MethodModifier ParseMethodModifier(TSNode node, ParseContext& ctx);
ReferenceModifier ParseReferenceModifier(TSNode node, ParseContext& ctx);
// 方法和属性声明解析
std::unique_ptr<MethodDeclaration> ParseMethodDeclaration(TSNode node, ParseContext& ctx);
std::unique_ptr<PropertyDeclaration> ParsePropertyDeclaration(TSNode node, ParseContext& ctx);
// ===== 通用解析函数 =====
std::vector<std::unique_ptr<Parameter>> ParseParameters(TSNode params_node, ParseContext& ctx);
// ===== 表达式解析 =====
ExpressionPtr ParseExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseAssignmentExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseTernaryExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseBinaryExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseUnaryPlusExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseUnaryMinusExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParsePrefixIncrementExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParsePrefixDecrementExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParsePostfixIncrementExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParsePostfixDecrementExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseLogicalNotExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseBitwiseNotExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseDerivativeExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseFunctionPointerExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseMatrixTransposeExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseExprOperatorExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseCallExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseSubscriptExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseAttributeExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseNewExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseEchoExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseRaiseExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseInheritedExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseArrayExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseParenthesizedExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseAnonymousFunctionExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParseTSSQLExpression(TSNode node, ParseContext& ctx);
ExpressionPtr ParsePrimaryExpression(TSNode node, ParseContext& ctx);
LValue ParseLValue(TSNode node, ParseContext& ctx);
std::unique_ptr<UnpackPattern> ParseUnpackPattern(TSNode node, ParseContext& ctx);
// ===== 语句解析 =====
StatementPtr ParseStatement(TSNode node, ParseContext& ctx);
// 顶层定义
StatementPtr ParseUnitDefinition(TSNode node, ParseContext& ctx);
StatementPtr ParseClassDefinition(TSNode node, ParseContext& ctx);
StatementPtr ParseExternalMethodDefinition(TSNode node, ParseContext& ctx);
StatementPtr ParseFunctionDeclaration(TSNode node, ParseContext& ctx);
StatementPtr ParseFunctionDefinition(TSNode node, ParseContext& ctx);
// 编译指令
StatementPtr ParseCompilerDirectiveStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseConditionalBlockStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseConditionalDirectiveStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseMatrixIterationStatement(TSNode node, ParseContext& ctx);
// 控制流语句
StatementPtr ParseIfStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseForInStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseForToStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseWhileStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseRepeatStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseCaseStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseTryStatement(TSNode node, ParseContext& ctx);
// 代码块
StatementPtr ParseBlockStatement(TSNode node, ParseContext& ctx);
// 跳转语句
StatementPtr ParseBreakStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseContinueStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseReturnStatement(TSNode node, ParseContext& ctx);
StatementPtr ParseUsesStatement(TSNode node, ParseContext& ctx);
// 声明语句
StatementPtr ParseVarDeclaration(TSNode node, ParseContext& ctx);
StatementPtr ParseStaticDeclaration(TSNode node, ParseContext& ctx);
StatementPtr ParseGlobalDeclaration(TSNode node, ParseContext& ctx);
StatementPtr ParseConstDeclaration(TSNode node, ParseContext& ctx);
// 匿名函数
StatementPtr ParseAnonymousFunctionStatement(TSNode node, ParseContext& ctx);
// 表达式语句
StatementPtr ParseExpressionStatement(TSNode node, ParseContext& ctx);
}

View File

@ -1,23 +0,0 @@
#pragma once
#include <string>
#include "./types.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
namespace lsp::language::ast::ts
{
std::string Text(TSNode node, std::string_view source);
Location NodeLocation(TSNode node);
inline bool IsComment(TSNode node)
{
std::string_view type = ts_node_type(node);
return type == "line_comment" ||
type == "block_comment" ||
type == "nested_comment";
}
}

View File

@ -1,6 +1,30 @@
#include "./tree_sitter_utils.hpp"
module;
namespace lsp::language::ast::ts
export module lsp.language.ast:ts_utils;
import tree_sitter;
import std;
import :types;
export namespace lsp::language::ast::ts_utils
{
std::string Text(TSNode node, std::string_view source);
Location NodeLocation(TSNode node);
inline bool IsComment(TSNode node)
{
std::string_view type = ts_node_type(node);
return type == "line_comment" ||
type == "block_comment" ||
type == "nested_comment";
}
}
namespace lsp::language::ast::ts_utils
{
std::string Text(TSNode node, std::string_view source)
{

View File

@ -1,13 +1,12 @@
#pragma once
module;
#include <string>
#include <vector>
#include <memory>
#include <optional>
#include <cstdint>
#include <variant>
namespace lsp::language::ast
export module lsp.language.ast:types;
import tree_sitter;
import std;
export namespace lsp::language::ast
{
enum class LiteralKind
{
@ -272,12 +271,12 @@ namespace lsp::language::ast
// ===== 基础类型 =====
struct Location
{
uint32_t start_line = 0;
uint32_t start_column = 0;
uint32_t end_line = 0;
uint32_t end_column = 0;
uint32_t start_offset = 0;
uint32_t end_offset = 0;
std::uint32_t start_line = 0;
std::uint32_t start_column = 0;
std::uint32_t end_line = 0;
std::uint32_t end_column = 0;
std::uint32_t start_offset = 0;
std::uint32_t end_offset = 0;
};
struct TypeAnnotation
@ -288,6 +287,12 @@ namespace lsp::language::ast
// ===== 前向声明 =====
class ASTVisitor;
template<typename Variant, typename Visitor>
decltype(auto) VisitVariant(Variant&& variant, Visitor&& visitor)
{
return std::visit(std::forward<Visitor>(visitor), std::forward<Variant>(variant));
}
class ASTNode;
class Expression;
class Statement;
@ -371,6 +376,62 @@ namespace lsp::language::ast
using ExpressionPtr = std::unique_ptr<Expression>;
using StatementPtr = std::unique_ptr<Statement>;
enum class ErrorSeverity
{
Warning,
Error,
Fatal
};
struct ParseError
{
Location location;
std::string node_type;
std::string message;
ErrorSeverity severity = ErrorSeverity::Error;
static ParseError Create(const Location& loc, const std::string& type, const std::string& msg)
{
return { loc, type, msg, ErrorSeverity::Error };
}
static ParseError Missing(const Location& loc, const std::string& type)
{
return Create(loc, type, "Syntax error: missing " + type);
}
static ParseError Unexpected(const Location& loc, const std::string& type, const std::string& context = "")
{
std::string msg = "Syntax error: unexpected token";
if (!context.empty())
msg += " in " + context;
return Create(loc, type, msg);
}
};
struct ParseResult
{
std::unique_ptr<Program> root;
std::vector<ParseError> errors;
bool HasErrors() const { return !errors.empty(); }
bool IsSuccess() const { return errors.empty(); }
std::size_t ErrorCount() const { return errors.size(); }
};
struct IncrementalParseResult
{
ParseResult result;
std::size_t nodes_parsed = 0;
std::size_t nodes_unchanged = 0;
std::size_t TotalNodes() const { return nodes_parsed + nodes_unchanged; }
double ChangeRate() const
{
return TotalNodes() > 0 ? static_cast<double>(nodes_parsed) / TotalNodes() : 0.0;
}
};
class ASTVisitor
{
public:
@ -981,6 +1042,7 @@ namespace lsp::language::ast
UsesStatement() { kind = NodeKind::kUsesStatement; }
void Accept(ASTVisitor& visitor) override { visitor.VisitUsesStatement(*this); }
public:
std::vector<Unit> units;
};
@ -1486,4 +1548,19 @@ namespace lsp::language::ast
}
}
// Forward declaration for Deserializer class
class Deserializer
{
public:
Deserializer();
~Deserializer() = default;
Deserializer(const Deserializer&) = delete;
Deserializer& operator=(const Deserializer&) = delete;
ParseResult Parse(TSNode root, const std::string& source);
IncrementalParseResult ParseIncremental(TSNode root, const std::string& source);
static std::vector<ParseError> DiagnoseSyntax(TSNode root, const std::string& source);
};
}

View File

@ -1,126 +0,0 @@
#include "./repo.hpp"
namespace lsp::language::keyword
{
Repo& Repo::Instance()
{
static Repo instance;
return instance;
}
Repo::Repo()
{
Load();
}
std::vector<Info> Repo::GetAll()
{
std::vector<Info> keyword;
keyword.reserve(keywords_.size());
for (const auto& [key, value] : keywords_)
keyword.push_back(value);
return keyword;
}
std::vector<Info> Repo::FindByPrefix(std::string_view prefix)
{
std::vector<Info> keyword;
for (const auto& [key, value] : keywords_)
{
if (key.starts_with(prefix))
keyword.push_back(value);
}
return keyword;
}
std::optional<Info> Repo::FindByName(std::string_view name)
{
auto target = keywords_.find(std::string(name));
if (target == keywords_.end())
return std::nullopt;
return target->second;
}
void Repo::Load()
{
keywords_.reserve(200);
LoadProgramStructure();
LoadDataTypes();
LoadClasses();
LoadControlFlow();
LoadOperators();
LoadSql();
LoadBuiltins();
LoadConstants();
}
void Repo::LoadProgramStructure()
{
keywords_["program"] = {"program", Kind::kProgramStructure, ""};
keywords_["function"] = {"function", Kind::kProgramStructure, ""};
keywords_["procedure"] = {"procedure", Kind::kProgramStructure, ""};
keywords_["unit"] = {"unit", Kind::kProgramStructure, ""};
keywords_["uses"] = {"uses", Kind::kProgramStructure, ""};
keywords_["implementation"] = {"implementation", Kind::kProgramStructure, ""};
keywords_["interface"] = {"interface", Kind::kProgramStructure, ""};
keywords_["initialization"] = {"initialization", Kind::kProgramStructure, ""};
keywords_["finalization"] = {"finalization", Kind::kProgramStructure, ""};
}
void Repo::LoadDataTypes()
{
keywords_["string"] = {"string", Kind::kDataTypes, ""};
keywords_["integer"] = {"integer", Kind::kDataTypes, ""};
keywords_["boolean"] = {"boolean", Kind::kDataTypes, ""};
keywords_["int64"] = {"int64", Kind::kDataTypes, ""};
keywords_["real"] = {"real", Kind::kDataTypes, ""};
keywords_["array"] = {"array", Kind::kDataTypes, ""};
}
void Repo::LoadClasses()
{
keywords_["type"] = {"type", Kind::kClassTypes, ""};
keywords_["class"] = {"class", Kind::kClassTypes, ""};
keywords_["new"] = {"new", Kind::kClassTypes, ""};
}
void Repo::LoadControlFlow()
{
keywords_["if"] = {"if", Kind::kConditionals, ""};
keywords_["for"] = {"for", Kind::kLoops, ""};
keywords_["while"] = {"while", Kind::kLoops, ""};
keywords_["case"] = {"case", Kind::kConditionals, ""};
}
void Repo::LoadOperators()
{
keywords_["and"] = {"and", Kind::kLogicalOperators, ""};
keywords_["or"] = {"or", Kind::kLogicalOperators, ""};
keywords_["div"] = {"div", Kind::kArithmeticOperators, ""};
keywords_["mod"] = {"mod", Kind::kArithmeticOperators, ""};
}
void Repo::LoadSql()
{
keywords_["select"] = {"select", Kind::kSqlControl, ""};
keywords_["update"] = {"update", Kind::kSqlControl, ""};
}
void Repo::LoadBuiltins()
{
keywords_["echo"] = {"echo", Kind::kBuiltinFunctions, ""};
keywords_["mtic"] = {"mtic", Kind::kBuiltinFunctions, ""};
keywords_["mtoc"] = {"mtoc", Kind::kBuiltinFunctions, ""};
}
void Repo::LoadConstants()
{
keywords_["true"] = {"true", Kind::kBooleanConstants, ""};
keywords_["false"] = {"false", Kind::kBooleanConstants, ""};
keywords_["nil"] = {"nil", Kind::kNullConstants, ""};
keywords_["inf"] = {"inf", Kind::kMathConstants, ""};
keywords_["nan"] = {"nan", Kind::kMathConstants, ""};
}
}

View File

@ -0,0 +1,202 @@
module;
export module lsp.language.keyword;
import std;
export namespace lsp::language::keyword
{
enum class Kind
{
kProgramStructure,
kDataTypes,
kClassTypes,
kClassModifiers,
kAccessModifiers,
kPropertyAccessors,
kConstructors,
kVariableModifiers,
kConditionals,
kLoops,
kBranchControl,
kReturnControl,
kBlockControl,
kReferences,
kNamespace,
kExceptions,
kLogicalOperators,
kArithmeticOperators,
kBitwiseOperators,
kSetOperators,
kSqlControl,
kSqlOperators,
kSqlKeywords,
kCallingConventions,
kSystemKeywords,
kBuiltinVariables,
kBuiltinFunctions,
kBooleanConstants,
kMathConstants,
kNullConstants
};
struct Info
{
std::string keyword;
Kind category;
std::string description;
};
class Repo
{
public:
Repo(const Repo&) = delete;
Repo& operator=(const Repo&) = delete;
static Repo& Instance();
std::vector<Info> GetAll();
std::vector<Info> FindByPrefix(std::string_view prefix);
std::optional<Info> FindByName(std::string_view name);
private:
Repo();
void Load();
void LoadProgramStructure();
void LoadDataTypes();
void LoadClasses();
void LoadControlFlow();
void LoadOperators();
void LoadSql();
void LoadBuiltins();
void LoadConstants();
private:
std::unordered_map<std::string, Info> keywords_;
};
}
namespace lsp::language::keyword
{
Repo& Repo::Instance()
{
static Repo instance;
return instance;
}
Repo::Repo()
{
Load();
}
std::vector<Info> Repo::GetAll()
{
std::vector<Info> keyword;
keyword.reserve(keywords_.size());
for (const auto& [_, value] : keywords_)
keyword.push_back(value);
return keyword;
}
std::vector<Info> Repo::FindByPrefix(std::string_view prefix)
{
std::vector<Info> keyword;
for (const auto& [key, value] : keywords_)
{
if (key.starts_with(prefix))
keyword.push_back(value);
}
return keyword;
}
std::optional<Info> Repo::FindByName(std::string_view name)
{
auto target = keywords_.find(std::string(name));
if (target == keywords_.end())
return std::nullopt;
return target->second;
}
void Repo::Load()
{
keywords_.reserve(200);
LoadProgramStructure();
LoadDataTypes();
LoadClasses();
LoadControlFlow();
LoadOperators();
LoadSql();
LoadBuiltins();
LoadConstants();
}
void Repo::LoadProgramStructure()
{
keywords_["program"] = { "program", Kind::kProgramStructure, "" };
keywords_["function"] = { "function", Kind::kProgramStructure, "" };
keywords_["procedure"] = { "procedure", Kind::kProgramStructure, "" };
keywords_["unit"] = { "unit", Kind::kProgramStructure, "" };
keywords_["uses"] = { "uses", Kind::kProgramStructure, "" };
keywords_["implementation"] = { "implementation", Kind::kProgramStructure, "" };
keywords_["interface"] = { "interface", Kind::kProgramStructure, "" };
keywords_["initialization"] = { "initialization", Kind::kProgramStructure, "" };
keywords_["finalization"] = { "finalization", Kind::kProgramStructure, "" };
}
void Repo::LoadDataTypes()
{
keywords_["string"] = { "string", Kind::kDataTypes, "" };
keywords_["integer"] = { "integer", Kind::kDataTypes, "" };
keywords_["boolean"] = { "boolean", Kind::kDataTypes, "" };
keywords_["int64"] = { "int64", Kind::kDataTypes, "" };
keywords_["real"] = { "real", Kind::kDataTypes, "" };
keywords_["array"] = { "array", Kind::kDataTypes, "" };
}
void Repo::LoadClasses()
{
keywords_["type"] = { "type", Kind::kClassTypes, "" };
keywords_["class"] = { "class", Kind::kClassTypes, "" };
keywords_["new"] = { "new", Kind::kClassTypes, "" };
}
void Repo::LoadControlFlow()
{
keywords_["if"] = { "if", Kind::kConditionals, "" };
keywords_["for"] = { "for", Kind::kLoops, "" };
keywords_["while"] = { "while", Kind::kLoops, "" };
keywords_["case"] = { "case", Kind::kConditionals, "" };
}
void Repo::LoadOperators()
{
keywords_["and"] = { "and", Kind::kLogicalOperators, "" };
keywords_["or"] = { "or", Kind::kLogicalOperators, "" };
keywords_["div"] = { "div", Kind::kArithmeticOperators, "" };
keywords_["mod"] = { "mod", Kind::kArithmeticOperators, "" };
}
void Repo::LoadSql()
{
keywords_["select"] = { "select", Kind::kSqlControl, "" };
keywords_["update"] = { "update", Kind::kSqlControl, "" };
}
void Repo::LoadBuiltins()
{
keywords_["echo"] = { "echo", Kind::kBuiltinFunctions, "" };
keywords_["mtic"] = { "mtic", Kind::kBuiltinFunctions, "" };
keywords_["mtoc"] = { "mtoc", Kind::kBuiltinFunctions, "" };
}
void Repo::LoadConstants()
{
keywords_["true"] = { "true", Kind::kBooleanConstants, "" };
keywords_["false"] = { "false", Kind::kBooleanConstants, "" };
keywords_["nil"] = { "nil", Kind::kNullConstants, "" };
keywords_["inf"] = { "inf", Kind::kMathConstants, "" };
keywords_["nan"] = { "nan", Kind::kMathConstants, "" };
}
}

View File

@ -1,37 +0,0 @@
#pragma once
#include <optional>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "./types.hpp"
namespace lsp::language::keyword
{
class Repo
{
public:
Repo(const Repo&) = delete;
Repo& operator=(const Repo&) = delete;
static Repo& Instance();
std::vector<Info> GetAll();
std::vector<Info> FindByPrefix(std::string_view prefix);
std::optional<Info> FindByName(std::string_view name);
private:
Repo();
void Load();
void LoadProgramStructure();
void LoadDataTypes();
void LoadClasses();
void LoadControlFlow();
void LoadOperators();
void LoadSql();
void LoadBuiltins();
void LoadConstants();
// std::unordered_map<std::string, Info, std::hash<std::string_view>, std::equal_to<>> keywords_;
std::unordered_map<std::string, Info> keywords_;
};
}

View File

@ -1,49 +0,0 @@
#pragma once
#include <string>
#include <optional>
#include "../../protocol/protocol.hpp"
namespace lsp::language::keyword
{
enum class Kind
{
kProgramStructure, // program, function, procedure, unit, uses, implementation, interface, initialization, finalization
kDataTypes, // string, integer, boolean, int64, real, array
kClassTypes, // type, class, fakeclass, new
kClassModifiers, // override, overload, virtual, property, self, inherited
kAccessModifiers, // public, protected, private, published
kPropertyAccessors, // read, write
kConstructors, // create, destroy, operator
kVariableModifiers, // external, const, out, var, global, static
kConditionals, // if, else, then, case, of
kLoops, // for, while, do, downto, step, until, repeat, to
kBranchControl, // break, continue, goto, label, exit
kReturnControl, // return, debugreturn, debugrunenv, debugrunenvdo
kBlockControl, // begin, end, with
kReferences, // weakref, autoref
kNamespace, // namespace
kExceptions, // except, raise, try, finally, exceptobject
kLogicalOperators, // and, in, is, not, or
kArithmeticOperators, // div, mod
kBitwiseOperators, // ror, rol, shr, shl
kSetOperators, // union, minus, union2
kSqlControl, // select, vselect, sselect, update, delete, mselect
kSqlOperators, // on, like, in (SQL context)
kSqlKeywords, // sqlin, from, where, group, by, order, distinct, join
kCallingConventions, // cdecl, pascal, stdcall, safecall, fastcall, register
kSystemKeywords, // setuid, sudo
kBuiltinVariables, // paramcount, realparamcount, params, system, tslassigning, likeeps, likeepsrate
kBuiltinFunctions, // echo, mtic, mtoc
kBooleanConstants, // false, true
kMathConstants, // inf, nan
kNullConstants // nil
};
struct Info
{
std::string keyword;
Kind category;
std::string description;
};
}

View File

@ -1,6 +1,13 @@
#include <utility>
module;
#include "./analyzer.hpp"
export module lsp.language.semantic:analyzer;
import std;
import :interface;
import lsp.language.ast;
import lsp.language.symbol;
import lsp.utils.string;
namespace lsp::language::semantic
{
@ -404,7 +411,7 @@ namespace lsp::language::semantic
{
VisitExpression(*node.discriminant);
}
for (size_t i = 0; i < node.branches.size(); ++i)
for (std::size_t i = 0; i < node.branches.size(); ++i)
{
const auto& branch = node.branches[i];
@ -446,9 +453,7 @@ namespace lsp::language::semantic
}
auto& context = *current_unit_context_;
auto* target = current_unit_section_ == symbol::UnitVisibility::kInterface
? &context.interface_imports
: &context.implementation_imports;
auto* target = current_unit_section_ == symbol::UnitVisibility::kInterface ? &context.interface_imports : &context.implementation_imports;
for (const auto& unit : node.units)
{
@ -1021,9 +1026,7 @@ namespace lsp::language::semantic
}
// 获取当前上下文的导入列表
const auto& imports = current_unit_section_ == symbol::UnitVisibility::kInterface
? current_unit_context_->interface_imports
: current_unit_context_->implementation_imports;
const auto& imports = current_unit_section_ == symbol::UnitVisibility::kInterface ? current_unit_context_->interface_imports : current_unit_context_->implementation_imports;
// 从后往前查找(最后导入的优先级最高)
for (auto it = imports.rbegin(); it != imports.rend(); ++it)

View File

@ -1,137 +0,0 @@
#pragma once
#include <functional>
#include <unordered_map>
#include <vector>
#include "../ast/types.hpp"
#include "../symbol/table.hpp"
#include "./semantic_model.hpp"
namespace lsp::language::semantic
{
class Analyzer : public ast::ASTVisitor
{
public:
explicit Analyzer(symbol::SymbolTable& symbol_table, SemanticModel& semantic_model);
using ExternalSymbolProvider = std::function<std::optional<symbol::Symbol>(const std::string&)>;
void SetExternalSymbolProvider(ExternalSymbolProvider provider);
void Analyze(ast::ASTNode& root);
void VisitProgram(ast::Program& node) override;
void VisitUnitDefinition(ast::UnitDefinition& node) override;
void VisitClassDefinition(ast::ClassDefinition& node) override;
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
void VisitVarDeclaration(ast::VarDeclaration& node) override;
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
void VisitClassMember(ast::ClassMember& node) override;
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
void VisitBlockStatement(ast::BlockStatement& node) override;
void VisitIfStatement(ast::IfStatement& node) override;
void VisitForInStatement(ast::ForInStatement& node) override;
void VisitForToStatement(ast::ForToStatement& node) override;
void VisitWhileStatement(ast::WhileStatement& node) override;
void VisitRepeatStatement(ast::RepeatStatement& node) override;
void VisitCaseStatement(ast::CaseStatement& node) override;
void VisitTryStatement(ast::TryStatement& node) override;
void VisitUsesStatement(ast::UsesStatement& node) override;
void VisitIdentifier(ast::Identifier& node) override;
void VisitCallExpression(ast::CallExpression& node) override;
void VisitAttributeExpression(ast::AttributeExpression& node) override;
void VisitAssignmentExpression(ast::AssignmentExpression& node) override;
void VisitLiteral(ast::Literal& node) override;
void VisitBinaryExpression(ast::BinaryExpression& node) override;
void VisitTernaryExpression(ast::TernaryExpression& node) override;
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
void VisitArrayExpression(ast::ArrayExpression& node) override;
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
void VisitNewExpression(ast::NewExpression& node) override;
void VisitEchoExpression(ast::EchoExpression& node) override;
void VisitRaiseExpression(ast::RaiseExpression& node) override;
void VisitInheritedExpression(ast::InheritedExpression& node) override;
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
void VisitBreakStatement(ast::BreakStatement& node) override;
void VisitContinueStatement(ast::ContinueStatement& node) override;
void VisitReturnStatement(ast::ReturnStatement& node) override;
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
void VisitColumnReference(ast::ColumnReference& node) override;
void VisitUnpackPattern(ast::UnpackPattern& node) override;
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
void VisitCompilerDirective(ast::CompilerDirective& node) override;
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
void VisitTSLXBlock(ast::TSLXBlock& node) override;
void VisitParameter(ast::Parameter& node) override;
private:
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
void VisitExpression(ast::Expression& expr);
void ProcessLValue(const ast::LValue& lvalue);
std::optional<symbol::SymbolId> ResolveParentClass(const ast::ClassDefinition::ParentClass& parent);
std::optional<symbol::ScopeId> ScopeAt(const ast::Location& location) const;
std::optional<symbol::SymbolId> ResolveByName(const std::string& name);
std::optional<symbol::SymbolId> FindMethodInClass(symbol::SymbolId class_id, const std::string& method_name) const;
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner_id) const;
std::optional<symbol::SymbolId> ResolveIdentifier(const std::string& name, const ast::Location& location);
std::optional<symbol::SymbolId> ResolveFromUses(const std::string& name);
void TrackReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_write = false);
void TrackCall(symbol::SymbolId callee, const ast::Location& location);
std::shared_ptr<Type> InferExpressionType(ast::Expression& expr);
std::shared_ptr<Type> GetDeclaredTypeForSymbol(symbol::SymbolId symbol_id);
std::optional<symbol::SymbolId> ResolveClassSymbol(const std::string& name, const ast::Location& location);
std::optional<symbol::SymbolId> ResolveLValueSymbol(const ast::LValue& lvalue);
void RegisterParameterTypes(symbol::SymbolId function_id, const std::vector<std::unique_ptr<ast::Parameter>>& parameters);
private:
struct UnitContext
{
std::string unit_name;
std::vector<symbol::UnitImport> interface_imports;
std::vector<symbol::UnitImport> implementation_imports;
};
symbol::SymbolTable& symbol_table_;
SemanticModel& semantic_model_;
std::optional<symbol::SymbolId> current_function_id_;
std::optional<symbol::SymbolId> current_class_id_;
std::optional<UnitContext> current_unit_context_;
std::optional<symbol::UnitVisibility> current_unit_section_;
ExternalSymbolProvider external_symbol_provider_;
std::unordered_map<std::string, symbol::SymbolId> imported_symbols_;
};
}

View File

@ -1,6 +1,11 @@
#include "call.hpp"
module;
#include <algorithm>
export module lsp.language.semantic:graph.call;
import std;
import :interface;
import lsp.language.ast;
namespace lsp::language::semantic::graph
{

View File

@ -1,31 +0,0 @@
#pragma once
#include <unordered_map>
#include <vector>
#include "../interface.hpp"
#include "../types.hpp"
namespace lsp::language::semantic::graph
{
using symbol::SymbolId;
class Call : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
const std::vector<semantic::Call>& callers(SymbolId id) const;
const std::vector<semantic::Call>& callees(SymbolId id) const;
private:
std::unordered_map<SymbolId, std::vector<semantic::Call>> callers_map_;
std::unordered_map<SymbolId, std::vector<semantic::Call>> callees_map_;
};
}

View File

@ -1,7 +1,10 @@
#include "inheritance.hpp"
module;
#include <algorithm>
export module lsp.language.semantic:graph.inheritance;
import std;
import :interface;
namespace lsp::language::semantic::graph
{

View File

@ -1,33 +0,0 @@
#pragma once
#include <unordered_map>
#include <vector>
#include "../interface.hpp"
#include "../../symbol/types.hpp"
namespace lsp::language::semantic::graph
{
using symbol::SymbolId;
class Inheritance : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddInheritance(SymbolId derived, SymbolId base);
const std::vector<SymbolId>& base_classes(SymbolId id) const;
const std::vector<SymbolId>& derived_classes(SymbolId id) const;
bool IsSubclassOf(SymbolId derived, SymbolId base) const;
private:
std::unordered_map<SymbolId, std::vector<SymbolId>> base_classes_;
std::unordered_map<SymbolId, std::vector<SymbolId>> derived_classes_;
};
}

View File

@ -1,6 +1,11 @@
#include "reference.hpp"
module;
#include <algorithm>
export module lsp.language.semantic:graph.reference;
import std;
import :interface;
import lsp.language.ast;
namespace lsp::language::semantic::graph
{

View File

@ -1,31 +0,0 @@
#pragma once
#include <optional>
#include <unordered_map>
#include <vector>
#include "../interface.hpp"
#include "../types.hpp"
namespace lsp::language::semantic::graph
{
using symbol::SymbolId;
class Reference : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
const std::vector<semantic::Reference>& references(SymbolId id) const;
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const;
private:
std::unordered_map<SymbolId, std::vector<semantic::Reference>> references_;
};
}

View File

@ -0,0 +1,812 @@
module;
export module lsp.language.semantic:interface;
import tree_sitter;
import std;
import lsp.language.ast;
import lsp.language.symbol;
import lsp.utils.string;
import lsp.protocol;
export namespace lsp::language::semantic
{
struct Reference
{
ast::Location location;
symbol::SymbolId symbol_id;
bool is_definition;
bool is_write;
};
struct Call
{
symbol::SymbolId caller;
symbol::SymbolId callee;
ast::Location call_site;
};
struct Inheritance
{
symbol::SymbolId derived;
symbol::SymbolId base;
};
class ISemanticGraph
{
public:
virtual ~ISemanticGraph() = default;
virtual void OnSymbolRemoved(symbol::SymbolId id) = 0;
virtual void Clear() = 0;
};
enum class TypeKind
{
kPrimitive,
kClass,
kArray,
kFunction,
kOptional,
kVoid,
kUnknown,
kError
};
enum class PrimitiveTypeKind
{
kInt,
kFloat,
kString,
kBool,
kChar
};
struct TypeCompatibility
{
bool is_compatible = false;
int conversion_cost = -1;
bool requires_cast = false;
static TypeCompatibility Exact()
{
return { true, 0, false };
}
static TypeCompatibility Implicit(int cost)
{
return { true, cost, false };
}
static TypeCompatibility ExplicitCast(int cost)
{
return { true, cost, true };
}
static TypeCompatibility Incompatible()
{
return { false, -1, false };
}
};
class Type;
class PrimitiveType
{
public:
explicit PrimitiveType(PrimitiveTypeKind kind) : kind_(kind) {}
PrimitiveTypeKind kind() const { return kind_; }
std::string ToString() const;
bool operator==(const PrimitiveType& other) const
{
return kind_ == other.kind_;
}
private:
PrimitiveTypeKind kind_;
};
class ClassType
{
public:
explicit ClassType(symbol::SymbolId class_id) : class_id_(class_id) {}
symbol::SymbolId class_id() const { return class_id_; }
bool operator==(const ClassType& other) const
{
return class_id_ == other.class_id_;
}
private:
symbol::SymbolId class_id_;
};
class ArrayType
{
public:
explicit ArrayType(std::shared_ptr<Type> element_type) : element_type_(std::move(element_type)) {}
const Type& element_type() const { return *element_type_; }
std::shared_ptr<Type> element_type_ptr() const { return element_type_; }
private:
std::shared_ptr<Type> element_type_;
};
class FunctionType
{
public:
FunctionType(std::vector<std::shared_ptr<Type>> param_types,
std::shared_ptr<Type> return_type) : param_types_(std::move(param_types)),
return_type_(std::move(return_type)) {}
const std::vector<std::shared_ptr<Type>>& param_types() const
{
return param_types_;
}
const Type& return_type() const { return *return_type_; }
std::shared_ptr<Type> return_type_ptr() const { return return_type_; }
private:
std::vector<std::shared_ptr<Type>> param_types_;
std::shared_ptr<Type> return_type_;
};
class OptionalType
{
public:
explicit OptionalType(std::shared_ptr<Type> inner_type) : inner_type_(std::move(inner_type)) {}
const Type& inner_type() const { return *inner_type_; }
std::shared_ptr<Type> inner_type_ptr() const { return inner_type_; }
private:
std::shared_ptr<Type> inner_type_;
};
class VoidType
{
public:
bool operator==(const VoidType&) const { return true; }
};
class UnknownType
{
public:
bool operator==(const UnknownType&) const { return true; }
};
class ErrorType
{
public:
explicit ErrorType(std::string message = "") : message_(std::move(message)) {}
const std::string& message() const { return message_; }
bool operator==(const ErrorType&) const { return true; }
private:
std::string message_;
};
using TypeData = std::variant<
PrimitiveType,
ClassType,
ArrayType,
FunctionType,
OptionalType,
VoidType,
UnknownType,
ErrorType>;
class Type
{
public:
explicit Type(TypeData data) : data_(std::move(data)) {}
template<typename T>
bool Is() const
{
return std::holds_alternative<T>(data_);
}
template<typename T>
const T* As() const
{
return std::get_if<T>(&data_);
}
template<typename T>
T* As()
{
return std::get_if<T>(&data_);
}
TypeKind kind() const;
std::string ToString() const;
bool Equals(const Type& other) const;
const TypeData& data() const { return data_; }
private:
TypeData data_;
};
class TypeSystem
{
public:
TypeSystem();
std::shared_ptr<Type> GetIntType() const { return int_type_; }
std::shared_ptr<Type> GetFloatType() const { return float_type_; }
std::shared_ptr<Type> GetStringType() const { return string_type_; }
std::shared_ptr<Type> GetBoolType() const { return bool_type_; }
std::shared_ptr<Type> GetCharType() const { return char_type_; }
std::shared_ptr<Type> GetVoidType() const { return void_type_; }
std::shared_ptr<Type> GetUnknownType() const { return unknown_type_; }
std::shared_ptr<Type> GetErrorType() const { return error_type_; }
std::shared_ptr<Type> CreateClassType(symbol::SymbolId class_id);
std::shared_ptr<Type> CreateArrayType(std::shared_ptr<Type> element_type);
std::shared_ptr<Type> CreateFunctionType(
std::vector<std::shared_ptr<Type>> param_types,
std::shared_ptr<Type> return_type);
std::shared_ptr<Type> CreateOptionalType(std::shared_ptr<Type> inner_type);
void RegisterClassType(const std::string& type_name, symbol::SymbolId class_id);
std::shared_ptr<Type> GetTypeByName(const std::string& type_name) const;
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const;
void RegisterSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type);
TypeCompatibility CheckCompatibility(const Type& from, const Type& to) const;
bool IsAssignable(const Type& from, const Type& to) const;
bool RequiresExplicitCast(const Type& from, const Type& to) const;
std::shared_ptr<Type> InferBinaryExpressionType(
const Type& left,
const Type& right,
const std::string& op) const;
std::shared_ptr<Type> InferUnaryExpressionType(
const Type& operand,
const std::string& op) const;
std::shared_ptr<Type> InferLiteralType(const std::string& literal_value) const;
void SetInheritanceChecker(
std::function<bool(symbol::SymbolId, symbol::SymbolId)> checker)
{
is_subclass_of_ = std::move(checker);
}
private:
std::shared_ptr<Type> int_type_;
std::shared_ptr<Type> float_type_;
std::shared_ptr<Type> string_type_;
std::shared_ptr<Type> bool_type_;
std::shared_ptr<Type> char_type_;
std::shared_ptr<Type> void_type_;
std::shared_ptr<Type> unknown_type_;
std::shared_ptr<Type> error_type_;
std::unordered_map<std::string, std::shared_ptr<Type>> type_by_name_;
std::unordered_map<symbol::SymbolId, std::shared_ptr<Type>> symbol_types_;
std::function<bool(symbol::SymbolId derived, symbol::SymbolId base)>
is_subclass_of_;
TypeCompatibility CheckPrimitiveCompatibility(
const PrimitiveType& from,
const PrimitiveType& to) const;
TypeCompatibility CheckClassCompatibility(
const ClassType& from,
const ClassType& to) const;
TypeCompatibility CheckArrayCompatibility(
const ArrayType& from,
const ArrayType& to) const;
TypeCompatibility CheckFunctionCompatibility(
const FunctionType& from,
const FunctionType& to) const;
};
struct NameResolutionResult
{
symbol::SymbolId symbol_id = symbol::kInvalidSymbolId;
bool is_ambiguous = false;
std::vector<symbol::SymbolId> candidates;
bool IsResolved() const
{
return symbol_id != symbol::kInvalidSymbolId;
}
static NameResolutionResult Success(symbol::SymbolId id)
{
return { id, false, { id } };
}
static NameResolutionResult Ambiguous(std::vector<symbol::SymbolId> symbols)
{
return {
symbols.empty() ? symbol::kInvalidSymbolId : symbols[0],
true,
std::move(symbols)
};
}
static NameResolutionResult NotFound()
{
return { symbol::kInvalidSymbolId, false, {} };
}
};
struct OverloadCandidate
{
symbol::SymbolId symbol_id;
int match_score = 0;
std::vector<TypeCompatibility> arg_conversions;
bool operator<(const OverloadCandidate& other) const
{
return match_score > other.match_score;
}
};
class NameResolver
{
public:
explicit NameResolver(const symbol::SymbolTable& symbol_table,
const TypeSystem& type_system) : symbol_table_(symbol_table),
type_system_(type_system) {}
NameResolutionResult ResolveName(
const std::string& name,
symbol::ScopeId scope_id,
bool search_parent = true) const;
NameResolutionResult ResolveNameAtLocation(
const std::string& name,
const ast::Location& location) const;
NameResolutionResult ResolveMemberAccess(
symbol::SymbolId object_symbol_id,
const std::string& member_name) const;
NameResolutionResult ResolveClassMember(
symbol::SymbolId class_id,
const std::string& member_name,
bool static_only = false) const;
NameResolutionResult ResolveFunctionCall(
const std::string& function_name,
const std::vector<std::shared_ptr<Type>>& arg_types,
symbol::ScopeId scope_id) const;
NameResolutionResult ResolveMethodCall(
symbol::SymbolId object_symbol_id,
const std::string& method_name,
const std::vector<std::shared_ptr<Type>>& arg_types) const;
NameResolutionResult ResolveQualifiedName(
const std::string& qualifier,
const std::string& name,
symbol::ScopeId scope_id) const;
std::optional<symbol::ScopeId> GetSymbolScope(
[[maybe_unused]] symbol::SymbolId symbol_id) const;
bool IsSymbolVisibleInScope(
[[maybe_unused]] symbol::SymbolId symbol_id,
[[maybe_unused]] symbol::ScopeId scope_id) const;
private:
const symbol::SymbolTable& symbol_table_;
const TypeSystem& type_system_;
std::vector<symbol::SymbolId> SearchScopeChain(
const std::string& name,
symbol::ScopeId start_scope) const;
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner) const;
NameResolutionResult SelectBestOverload(
const std::vector<OverloadCandidate>& candidates) const;
OverloadCandidate CalculateOverloadScore(
symbol::SymbolId candidate_id,
const std::vector<std::shared_ptr<Type>>& arg_types) const;
std::vector<std::shared_ptr<Type>> GetParameterTypes(
symbol::SymbolId symbol_id) const;
std::optional<symbol::SymbolId> GetOwnerClassId(
[[maybe_unused]] symbol::SymbolId symbol_id) const;
bool CheckMemberAccessibility(
[[maybe_unused]] const symbol::Symbol& member,
[[maybe_unused]] symbol::ScopeId access_scope) const;
};
namespace graph
{
using symbol::SymbolId;
class Reference : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
const std::vector<semantic::Reference>& references(SymbolId id) const;
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const;
private:
std::unordered_map<SymbolId, std::vector<semantic::Reference>> references_;
};
class Call : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
const std::vector<semantic::Call>& callers(SymbolId id) const;
const std::vector<semantic::Call>& callees(SymbolId id) const;
private:
std::unordered_map<SymbolId, std::vector<semantic::Call>> callers_map_;
std::unordered_map<SymbolId, std::vector<semantic::Call>> callees_map_;
};
class Inheritance : public ISemanticGraph
{
public:
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
void AddInheritance(SymbolId derived, SymbolId base);
const std::vector<SymbolId>& base_classes(SymbolId id) const;
const std::vector<SymbolId>& derived_classes(SymbolId id) const;
bool IsSubclassOf(SymbolId derived, SymbolId base) const;
private:
std::unordered_map<SymbolId, std::vector<SymbolId>> base_classes_;
std::unordered_map<SymbolId, std::vector<SymbolId>> derived_classes_;
};
} // namespace graph
class SemanticModel
{
public:
struct UnitImportSet
{
std::vector<symbol::UnitImport> interface_imports;
std::vector<symbol::UnitImport> implementation_imports;
};
explicit SemanticModel(const symbol::SymbolTable& symbol_table);
void Clear();
void OnSymbolRemoved(symbol::SymbolId id);
void AddReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
void AddInheritance(symbol::SymbolId derived, symbol::SymbolId base);
void AddCall(symbol::SymbolId caller, symbol::SymbolId callee, const ast::Location& location);
graph::Reference& references() { return reference_graph_; }
graph::Inheritance& inheritance() { return inheritance_graph_; }
graph::Call& calls() { return call_graph_; }
const graph::Reference& references() const { return reference_graph_; }
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
const graph::Call& calls() const { return call_graph_; }
TypeSystem& type_system() { return type_system_; }
const TypeSystem& type_system() const { return type_system_; }
NameResolver& name_resolver() { return *name_resolver_; }
const NameResolver& name_resolver() const { return *name_resolver_; }
void RegisterUnitImports(const std::string& unit_name, UnitImportSet imports);
std::optional<UnitImportSet> GetUnitImports(const std::string& unit_name) const;
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const
{
return type_system_.GetSymbolType(symbol_id);
}
void SetSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type)
{
type_system_.RegisterSymbolType(symbol_id, std::move(type));
}
bool IsSubclassOf(symbol::SymbolId derived, symbol::SymbolId base) const
{
return inheritance_graph_.IsSubclassOf(derived, base);
}
private:
const symbol::SymbolTable& symbol_table_;
graph::Reference reference_graph_;
graph::Inheritance inheritance_graph_;
graph::Call call_graph_;
TypeSystem type_system_;
std::unique_ptr<NameResolver> name_resolver_;
std::unordered_map<std::string, UnitImportSet, utils::IHasher, utils::IEqualTo> unit_imports_;
};
class Analyzer : public ast::ASTVisitor
{
public:
explicit Analyzer(symbol::SymbolTable& symbol_table, SemanticModel& semantic_model);
using ExternalSymbolProvider = std::function<std::optional<symbol::Symbol>(const std::string&)>;
void SetExternalSymbolProvider(ExternalSymbolProvider provider);
void Analyze(ast::ASTNode& root);
void VisitProgram(ast::Program& node) override;
void VisitUnitDefinition(ast::UnitDefinition& node) override;
void VisitClassDefinition(ast::ClassDefinition& node) override;
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
void VisitVarDeclaration(ast::VarDeclaration& node) override;
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
void VisitClassMember(ast::ClassMember& node) override;
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
void VisitBlockStatement(ast::BlockStatement& node) override;
void VisitIfStatement(ast::IfStatement& node) override;
void VisitForInStatement(ast::ForInStatement& node) override;
void VisitForToStatement(ast::ForToStatement& node) override;
void VisitWhileStatement(ast::WhileStatement& node) override;
void VisitRepeatStatement(ast::RepeatStatement& node) override;
void VisitCaseStatement(ast::CaseStatement& node) override;
void VisitTryStatement(ast::TryStatement& node) override;
void VisitUsesStatement(ast::UsesStatement& node) override;
void VisitIdentifier(ast::Identifier& node) override;
void VisitCallExpression(ast::CallExpression& node) override;
void VisitAttributeExpression(ast::AttributeExpression& node) override;
void VisitAssignmentExpression(ast::AssignmentExpression& node) override;
void VisitLiteral(ast::Literal& node) override;
void VisitBinaryExpression(ast::BinaryExpression& node) override;
void VisitTernaryExpression(ast::TernaryExpression& node) override;
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
void VisitArrayExpression(ast::ArrayExpression& node) override;
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
void VisitNewExpression(ast::NewExpression& node) override;
void VisitEchoExpression(ast::EchoExpression& node) override;
void VisitRaiseExpression(ast::RaiseExpression& node) override;
void VisitInheritedExpression(ast::InheritedExpression& node) override;
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
void VisitBreakStatement(ast::BreakStatement& node) override;
void VisitContinueStatement(ast::ContinueStatement& node) override;
void VisitReturnStatement(ast::ReturnStatement& node) override;
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
void VisitColumnReference(ast::ColumnReference& node) override;
void VisitUnpackPattern(ast::UnpackPattern& node) override;
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
void VisitCompilerDirective(ast::CompilerDirective& node) override;
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
void VisitTSLXBlock(ast::TSLXBlock& node) override;
void VisitParameter(ast::Parameter& node) override;
private:
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
void VisitExpression(ast::Expression& expr);
void ProcessLValue(const ast::LValue& lvalue);
std::optional<symbol::SymbolId> ResolveParentClass(const ast::ClassDefinition::ParentClass& parent);
std::optional<symbol::ScopeId> ScopeAt(const ast::Location& location) const;
std::optional<symbol::SymbolId> ResolveByName(const std::string& name);
std::optional<symbol::SymbolId> FindMethodInClass(symbol::SymbolId class_id, const std::string& method_name) const;
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner_id) const;
std::optional<symbol::SymbolId> ResolveIdentifier(const std::string& name, const ast::Location& location);
std::optional<symbol::SymbolId> ResolveFromUses(const std::string& name);
void TrackReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_write = false);
void TrackCall(symbol::SymbolId callee, const ast::Location& location);
std::shared_ptr<Type> InferExpressionType(ast::Expression& expr);
std::shared_ptr<Type> GetDeclaredTypeForSymbol(symbol::SymbolId symbol_id);
std::optional<symbol::SymbolId> ResolveClassSymbol(const std::string& name, const ast::Location& location);
std::optional<symbol::SymbolId> ResolveLValueSymbol(const ast::LValue& lvalue);
void RegisterParameterTypes(symbol::SymbolId function_id, const std::vector<std::unique_ptr<ast::Parameter>>& parameters);
private:
struct UnitContext
{
std::string unit_name;
std::vector<symbol::UnitImport> interface_imports;
std::vector<symbol::UnitImport> implementation_imports;
};
symbol::SymbolTable& symbol_table_;
SemanticModel& semantic_model_;
std::optional<symbol::SymbolId> current_function_id_;
std::optional<symbol::SymbolId> current_class_id_;
std::optional<UnitContext> current_unit_context_;
std::optional<symbol::UnitVisibility> current_unit_section_;
ExternalSymbolProvider external_symbol_provider_;
std::unordered_map<std::string, symbol::SymbolId> imported_symbols_;
};
struct TokenInfo
{
ast::Location location;
std::string type;
std::string text;
bool is_named = false;
std::uint32_t depth = 0;
std::string parent_type;
std::uint32_t child_index = 0;
std::uint32_t sibling_count = 0;
bool is_error = false;
bool is_missing = false;
};
struct TokenCollectionOptions
{
bool include_anonymous = true;
bool include_comments = false;
bool include_whitespace = false;
bool include_errors = true;
bool include_missing = true;
std::uint32_t max_depth = UINT32_MAX_VALUE;
std::uint32_t min_depth = 0;
std::unordered_set<std::string> include_types;
std::unordered_set<std::string> exclude_types;
bool skip_empty_text = false;
std::uint32_t min_text_length = 0;
std::uint32_t max_text_length = UINT32_MAX_VALUE;
bool has_location_filter = false;
std::uint32_t filter_start_line = 0;
std::uint32_t filter_end_line = UINT32_MAX_VALUE;
std::function<bool(const TokenInfo&)> custom_filter;
static TokenCollectionOptions OnlyNamed()
{
TokenCollectionOptions opts;
opts.include_anonymous = false;
return opts;
}
static TokenCollectionOptions WithComments()
{
TokenCollectionOptions opts;
opts.include_comments = true;
return opts;
}
static TokenCollectionOptions OnlyTypes(const std::vector<std::string>& types)
{
TokenCollectionOptions opts;
opts.include_types = std::unordered_set<std::string>(types.begin(), types.end());
return opts;
}
static TokenCollectionOptions ExcludeTypes(const std::vector<std::string>& types)
{
TokenCollectionOptions opts;
opts.exclude_types = std::unordered_set<std::string>(types.begin(), types.end());
return opts;
}
static TokenCollectionOptions InRange(std::uint32_t start_line, std::uint32_t end_line)
{
TokenCollectionOptions opts;
opts.has_location_filter = true;
opts.filter_start_line = start_line;
opts.filter_end_line = end_line;
return opts;
}
};
class TokenCollector
{
public:
explicit TokenCollector(const TokenCollectionOptions& options = {});
~TokenCollector() = default;
TokenCollector(const TokenCollector&) = delete;
TokenCollector& operator=(const TokenCollector&) = delete;
TokenCollector(TokenCollector&&) = default;
TokenCollector& operator=(TokenCollector&&) = default;
std::vector<TokenInfo> Collect(TSNode root, const std::string& source);
std::vector<TokenInfo> CollectByType(TSNode root, const std::string& source, const std::vector<std::string>& types);
std::vector<TokenInfo> CollectInRange(TSNode root, const std::string& source, std::uint32_t start_line, std::uint32_t end_line);
std::vector<TokenInfo> CollectLeafNodes(TSNode root, const std::string& source);
std::vector<TokenInfo> FindTokensAtPosition(TSNode root, const std::string& source, std::uint32_t line, std::uint32_t column);
std::vector<TokenInfo> FindTokensByText(TSNode root, const std::string& source, const std::string& text, bool exact_match = true);
std::vector<TokenInfo> FindTokensBy(TSNode root, const std::string& source, std::function<bool(const TokenInfo&)> predicate);
std::uint32_t CountTokensByType(TSNode root, const std::string& source, const std::string& type);
std::vector<std::string> GetUniqueTypes(TSNode root, const std::string& source);
void SetOptions(const TokenCollectionOptions& options) { options_ = options; }
const TokenCollectionOptions& GetOptions() const { return options_; }
private:
void CollectRecursive(TSNode node, const std::string& source, std::vector<TokenInfo>& tokens, std::uint32_t depth, TSNode parent);
bool ShouldCollectNode(const TokenInfo& info) const;
void FillTokenInfo(TokenInfo& info, TSNode node, const std::string& source, std::uint32_t depth, TSNode parent) const;
bool IsCommentNode(const std::string& type) const;
bool IsWhitespaceNode(const std::string& type) const;
bool IsLocationInRange(const ast::Location& loc) const;
private:
TokenCollectionOptions options_;
};
inline std::vector<TokenInfo> CollectNamedTokens(TSNode root, const std::string& source)
{
return TokenCollector(TokenCollectionOptions::OnlyNamed()).Collect(root, source);
}
inline std::vector<TokenInfo> CollectTokensOfType(TSNode root, const std::string& source, const std::string& type)
{
return TokenCollector().CollectByType(root, source, { type });
}
} // namespace lsp::language::semantic

View File

@ -1,18 +0,0 @@
#pragma once
#include "../symbol/types.hpp"
namespace lsp::language::semantic
{
class ISemanticGraph
{
public:
virtual ~ISemanticGraph() = default;
virtual void OnSymbolRemoved(symbol::SymbolId id) = 0;
virtual void Clear() = 0;
};
}

View File

@ -1,6 +1,12 @@
#include "./name_resolver.hpp"
module;
#include <algorithm>
export module lsp.language.semantic:name_resolver;
import std;
import :interface;
import lsp.language.ast;
import lsp.language.symbol;
namespace lsp::language::semantic
{
@ -325,7 +331,7 @@ namespace lsp::language::semantic
}
int total_score = 0;
for (size_t i = 0; i < arg_types.size(); ++i)
for (std::size_t i = 0; i < arg_types.size(); ++i)
{
auto compat = type_system_.CheckCompatibility(*arg_types[i], *param_types[i]);
if (!compat.is_compatible)

View File

@ -1,131 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <vector>
#include "../symbol/table.hpp"
#include "./type_system.hpp"
namespace lsp::language::semantic
{
struct NameResolutionResult
{
symbol::SymbolId symbol_id = symbol::kInvalidSymbolId;
bool is_ambiguous = false;
std::vector<symbol::SymbolId> candidates;
bool IsResolved() const
{
return symbol_id != symbol::kInvalidSymbolId;
}
static NameResolutionResult Success(symbol::SymbolId id)
{
return { id, false, { id } };
}
static NameResolutionResult Ambiguous(std::vector<symbol::SymbolId> symbols)
{
return {
symbols.empty() ? symbol::kInvalidSymbolId : symbols[0],
true,
std::move(symbols)
};
}
static NameResolutionResult NotFound()
{
return { symbol::kInvalidSymbolId, false, {} };
}
};
struct OverloadCandidate
{
symbol::SymbolId symbol_id;
int match_score = 0;
std::vector<TypeCompatibility> arg_conversions;
bool operator<(const OverloadCandidate& other) const
{
return match_score > other.match_score;
}
};
class NameResolver
{
public:
explicit NameResolver(const symbol::SymbolTable& symbol_table,
const TypeSystem& type_system) : symbol_table_(symbol_table),
type_system_(type_system) {}
NameResolutionResult ResolveName(
const std::string& name,
symbol::ScopeId scope_id,
bool search_parent = true) const;
NameResolutionResult ResolveNameAtLocation(
const std::string& name,
const ast::Location& location) const;
NameResolutionResult ResolveMemberAccess(
symbol::SymbolId object_symbol_id,
const std::string& member_name) const;
NameResolutionResult ResolveClassMember(
symbol::SymbolId class_id,
const std::string& member_name,
bool static_only = false) const;
NameResolutionResult ResolveFunctionCall(
const std::string& function_name,
const std::vector<std::shared_ptr<Type>>& arg_types,
symbol::ScopeId scope_id) const;
NameResolutionResult ResolveMethodCall(
symbol::SymbolId object_symbol_id,
const std::string& method_name,
const std::vector<std::shared_ptr<Type>>& arg_types) const;
NameResolutionResult ResolveQualifiedName(
const std::string& qualifier,
const std::string& name,
symbol::ScopeId scope_id) const;
std::optional<symbol::ScopeId> GetSymbolScope(
[[maybe_unused]] symbol::SymbolId symbol_id) const;
bool IsSymbolVisibleInScope(
[[maybe_unused]] symbol::SymbolId symbol_id,
[[maybe_unused]] symbol::ScopeId scope_id) const;
private:
const symbol::SymbolTable& symbol_table_;
const TypeSystem& type_system_;
std::vector<symbol::SymbolId> SearchScopeChain(
const std::string& name,
symbol::ScopeId start_scope) const;
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner) const;
NameResolutionResult SelectBestOverload(
const std::vector<OverloadCandidate>& candidates) const;
OverloadCandidate CalculateOverloadScore(
symbol::SymbolId candidate_id,
const std::vector<std::shared_ptr<Type>>& arg_types) const;
std::vector<std::shared_ptr<Type>> GetParameterTypes(
symbol::SymbolId symbol_id) const;
std::optional<symbol::SymbolId> GetOwnerClassId(
[[maybe_unused]] symbol::SymbolId symbol_id) const;
bool CheckMemberAccessibility(
[[maybe_unused]] const symbol::Symbol& member,
[[maybe_unused]] symbol::ScopeId access_scope) const;
};
}

View File

@ -0,0 +1,16 @@
module;
export module lsp.language.semantic;
import std;
// 聚合导出语义模块的各个分区
export import :interface;
export import :type_system;
export import :name_resolver;
export import :semantic_model;
export import :analyzer;
export import :token_collector;
export import :graph.call;
export import :graph.inheritance;
export import :graph.reference;

View File

@ -1,6 +1,13 @@
#include <utility>
module;
#include "semantic_model.hpp"
export module lsp.language.semantic:semantic_model;
import std;
import :interface;
import lsp.language.ast;
import lsp.language.symbol;
import lsp.protocol.types;
namespace lsp::language::semantic
{

View File

@ -1,81 +0,0 @@
#pragma once
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
#include "../symbol/table.hpp"
#include "../../utils/string.hpp"
#include "./graph/call.hpp"
#include "./graph/inheritance.hpp"
#include "./graph/reference.hpp"
#include "./name_resolver.hpp"
#include "./type_system.hpp"
namespace lsp::language::semantic
{
class SemanticModel
{
public:
struct UnitImportSet
{
std::vector<symbol::UnitImport> interface_imports;
std::vector<symbol::UnitImport> implementation_imports;
};
explicit SemanticModel(const symbol::SymbolTable& symbol_table);
void Clear();
void OnSymbolRemoved(symbol::SymbolId id);
void AddReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
void AddInheritance(symbol::SymbolId derived, symbol::SymbolId base);
void AddCall(symbol::SymbolId caller, symbol::SymbolId callee, const ast::Location& location);
graph::Reference& references() { return reference_graph_; }
graph::Inheritance& inheritance() { return inheritance_graph_; }
graph::Call& calls() { return call_graph_; }
const graph::Reference& references() const { return reference_graph_; }
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
const graph::Call& calls() const { return call_graph_; }
TypeSystem& type_system() { return type_system_; }
const TypeSystem& type_system() const { return type_system_; }
NameResolver& name_resolver() { return *name_resolver_; }
const NameResolver& name_resolver() const { return *name_resolver_; }
void RegisterUnitImports(const std::string& unit_name, UnitImportSet imports);
std::optional<UnitImportSet> GetUnitImports(const std::string& unit_name) const;
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const
{
return type_system_.GetSymbolType(symbol_id);
}
void SetSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type)
{
type_system_.RegisterSymbolType(symbol_id, std::move(type));
}
bool IsSubclassOf(symbol::SymbolId derived, symbol::SymbolId base) const
{
return inheritance_graph_.IsSubclassOf(derived, base);
}
private:
const symbol::SymbolTable& symbol_table_;
graph::Reference reference_graph_;
graph::Inheritance inheritance_graph_;
graph::Call call_graph_;
TypeSystem type_system_;
std::unique_ptr<NameResolver> name_resolver_;
std::unordered_map<std::string, UnitImportSet, utils::IHasher, utils::IEqualTo> unit_imports_;
};
}

View File

@ -1,5 +1,13 @@
#include "./token_collector.hpp"
#include "../ast/tree_sitter_utils.hpp"
module;
export module lsp.language.semantic:token_collector;
import tree_sitter;
import std;
import :interface;
import lsp.language.ast;
namespace lsp::language::semantic
{
@ -201,7 +209,7 @@ namespace lsp::language::semantic
void TokenCollector::FillTokenInfo(TokenInfo& info, TSNode node, const std::string& source, uint32_t depth, TSNode parent) const
{
info.location = ast::ts::NodeLocation(node);
info.location = ast::ts_utils::NodeLocation(node);
info.type = ts_node_type(node);
info.is_named = ts_node_is_named(node);
info.depth = depth;

View File

@ -1,142 +0,0 @@
#pragma once
#include <functional>
#include <string>
#include <unordered_set>
#include <vector>
#include "../ast/types.hpp"
extern "C"
{
#include <tree_sitter/api.h>
}
namespace lsp::language::semantic
{
struct TokenInfo
{
ast::Location location;
std::string type; // 节点类型 (如 "identifier", "number")
std::string text; // Token 文本内容
bool is_named = false;
uint32_t depth = 0;
std::string parent_type; // 父节点类型
uint32_t child_index = 0; // 在父节点中的索引
uint32_t sibling_count = 0; // 兄弟节点总数
bool is_error = false; // 是否是错误节点
bool is_missing = false; // 是否是缺失节点
};
struct TokenCollectionOptions
{
bool include_anonymous = true;
bool include_comments = false;
bool include_whitespace = false;
bool include_errors = true;
bool include_missing = true;
uint32_t max_depth = UINT32_MAX;
uint32_t min_depth = 0;
std::unordered_set<std::string> include_types;
std::unordered_set<std::string> exclude_types;
bool skip_empty_text = false;
uint32_t min_text_length = 0;
uint32_t max_text_length = UINT32_MAX;
bool has_location_filter = false;
uint32_t filter_start_line = 0;
uint32_t filter_end_line = UINT32_MAX;
std::function<bool(const TokenInfo&)> custom_filter;
static TokenCollectionOptions OnlyNamed()
{
TokenCollectionOptions opts;
opts.include_anonymous = false;
return opts;
}
static TokenCollectionOptions WithComments()
{
TokenCollectionOptions opts;
opts.include_comments = true;
return opts;
}
static TokenCollectionOptions OnlyTypes(const std::vector<std::string>& types)
{
TokenCollectionOptions opts;
opts.include_types = std::unordered_set<std::string>(types.begin(), types.end());
return opts;
}
static TokenCollectionOptions ExcludeTypes(const std::vector<std::string>& types)
{
TokenCollectionOptions opts;
opts.exclude_types = std::unordered_set<std::string>(types.begin(), types.end());
return opts;
}
static TokenCollectionOptions InRange(uint32_t start_line, uint32_t end_line)
{
TokenCollectionOptions opts;
opts.has_location_filter = true;
opts.filter_start_line = start_line;
opts.filter_end_line = end_line;
return opts;
}
};
class TokenCollector
{
public:
explicit TokenCollector(const TokenCollectionOptions& options = {});
~TokenCollector() = default;
TokenCollector(const TokenCollector&) = delete;
TokenCollector& operator=(const TokenCollector&) = delete;
TokenCollector(TokenCollector&&) = default;
TokenCollector& operator=(TokenCollector&&) = default;
std::vector<TokenInfo> Collect(TSNode root, const std::string& source);
std::vector<TokenInfo> CollectByType(TSNode root, const std::string& source, const std::vector<std::string>& types);
std::vector<TokenInfo> CollectInRange(TSNode root, const std::string& source, uint32_t start_line, uint32_t end_line);
std::vector<TokenInfo> CollectLeafNodes(TSNode root, const std::string& source);
std::vector<TokenInfo> FindTokensAtPosition(TSNode root, const std::string& source, uint32_t line, uint32_t column);
std::vector<TokenInfo> FindTokensByText(TSNode root, const std::string& source, const std::string& text, bool exact_match = true);
std::vector<TokenInfo> FindTokensBy(TSNode root, const std::string& source, std::function<bool(const TokenInfo&)> predicate);
uint32_t CountTokensByType(TSNode root, const std::string& source, const std::string& type);
std::vector<std::string> GetUniqueTypes(TSNode root, const std::string& source);
void SetOptions(const TokenCollectionOptions& options) { options_ = options; }
const TokenCollectionOptions& GetOptions() const { return options_; }
private:
void CollectRecursive(TSNode node, const std::string& source, std::vector<TokenInfo>& tokens, uint32_t depth, TSNode parent);
bool ShouldCollectNode(const TokenInfo& info) const;
void FillTokenInfo(TokenInfo& info, TSNode node, const std::string& source, uint32_t depth, TSNode parent) const;
bool IsCommentNode(const std::string& type) const;
bool IsWhitespaceNode(const std::string& type) const;
bool IsLocationInRange(const ast::Location& loc) const;
private:
TokenCollectionOptions options_;
};
inline std::vector<TokenInfo> CollectNamedTokens(TSNode root, const std::string& source)
{
return TokenCollector(TokenCollectionOptions::OnlyNamed()).Collect(root, source);
}
inline std::vector<TokenInfo> CollectTokensOfType(TSNode root, const std::string& source, const std::string& type)
{
return TokenCollector().CollectByType(root, source, { type });
}
} // namespace lsp::language::semantic

View File

@ -1,8 +1,13 @@
#include "./type_system.hpp"
module;
#include <sstream>
export module lsp.language.semantic:type_system;
#include "../../utils/string.hpp"
import std;
import :interface;
import lsp.language.symbol;
import lsp.utils.string;
namespace lsp::language::semantic
{
@ -89,7 +94,7 @@ namespace lsp::language::semantic
{
std::string result = "function(";
const auto& params = type_data.param_types();
for (size_t i = 0; i < params.size(); ++i)
for (std::size_t i = 0; i < params.size(); ++i)
{
if (i > 0)
result += ", ";
@ -159,7 +164,7 @@ namespace lsp::language::semantic
{
return false;
}
for (size_t i = 0; i < params1.size(); ++i)
for (std::size_t i = 0; i < params1.size(); ++i)
{
if (!params1[i]->Equals(*params2[i]))
{
@ -375,7 +380,7 @@ namespace lsp::language::semantic
return TypeCompatibility::Incompatible();
}
for (size_t i = 0; i < from_params.size(); ++i)
for (std::size_t i = 0; i < from_params.size(); ++i)
{
if (!from_params[i]->Equals(*to_params[i]))
{

View File

@ -1,297 +0,0 @@
#pragma once
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
#include "../symbol/types.hpp"
namespace lsp::language::semantic
{
class Type;
class TypeSystem;
enum class TypeKind
{
kPrimitive,
kClass,
kArray,
kFunction,
kOptional,
kVoid,
kUnknown,
kError
};
enum class PrimitiveTypeKind
{
kInt,
kFloat,
kString,
kBool,
kChar
};
struct TypeCompatibility
{
bool is_compatible = false;
int conversion_cost = -1;
bool requires_cast = false;
static TypeCompatibility Exact()
{
return { true, 0, false };
}
static TypeCompatibility Implicit(int cost)
{
return { true, cost, false };
}
static TypeCompatibility ExplicitCast(int cost)
{
return { true, cost, true };
}
static TypeCompatibility Incompatible()
{
return { false, -1, false };
}
};
class PrimitiveType
{
public:
explicit PrimitiveType(PrimitiveTypeKind kind) : kind_(kind) {}
PrimitiveTypeKind kind() const { return kind_; }
std::string ToString() const;
bool operator==(const PrimitiveType& other) const
{
return kind_ == other.kind_;
}
private:
PrimitiveTypeKind kind_;
};
class ClassType
{
public:
explicit ClassType(symbol::SymbolId class_id) : class_id_(class_id) {}
symbol::SymbolId class_id() const { return class_id_; }
bool operator==(const ClassType& other) const
{
return class_id_ == other.class_id_;
}
private:
symbol::SymbolId class_id_;
};
class ArrayType
{
public:
explicit ArrayType(std::shared_ptr<Type> element_type) : element_type_(std::move(element_type)) {}
const Type& element_type() const { return *element_type_; }
std::shared_ptr<Type> element_type_ptr() const { return element_type_; }
private:
std::shared_ptr<Type> element_type_;
};
class FunctionType
{
public:
FunctionType(std::vector<std::shared_ptr<Type>> param_types,
std::shared_ptr<Type> return_type) : param_types_(std::move(param_types)),
return_type_(std::move(return_type)) {}
const std::vector<std::shared_ptr<Type>>& param_types() const
{
return param_types_;
}
const Type& return_type() const { return *return_type_; }
std::shared_ptr<Type> return_type_ptr() const { return return_type_; }
private:
std::vector<std::shared_ptr<Type>> param_types_;
std::shared_ptr<Type> return_type_;
};
class OptionalType
{
public:
explicit OptionalType(std::shared_ptr<Type> inner_type) : inner_type_(std::move(inner_type)) {}
const Type& inner_type() const { return *inner_type_; }
std::shared_ptr<Type> inner_type_ptr() const { return inner_type_; }
private:
std::shared_ptr<Type> inner_type_;
};
class VoidType
{
public:
bool operator==(const VoidType&) const { return true; }
};
class UnknownType
{
public:
bool operator==(const UnknownType&) const { return true; }
};
class ErrorType
{
public:
explicit ErrorType(std::string message = "") : message_(std::move(message)) {}
const std::string& message() const { return message_; }
bool operator==(const ErrorType&) const { return true; }
private:
std::string message_;
};
using TypeData = std::variant<
PrimitiveType,
ClassType,
ArrayType,
FunctionType,
OptionalType,
VoidType,
UnknownType,
ErrorType>;
class Type
{
public:
explicit Type(TypeData data) : data_(std::move(data)) {}
template<typename T>
bool Is() const
{
return std::holds_alternative<T>(data_);
}
template<typename T>
const T* As() const
{
return std::get_if<T>(&data_);
}
template<typename T>
T* As()
{
return std::get_if<T>(&data_);
}
TypeKind kind() const;
std::string ToString() const;
bool Equals(const Type& other) const;
const TypeData& data() const { return data_; }
private:
TypeData data_;
};
class TypeSystem
{
public:
TypeSystem();
std::shared_ptr<Type> GetIntType() const { return int_type_; }
std::shared_ptr<Type> GetFloatType() const { return float_type_; }
std::shared_ptr<Type> GetStringType() const { return string_type_; }
std::shared_ptr<Type> GetBoolType() const { return bool_type_; }
std::shared_ptr<Type> GetCharType() const { return char_type_; }
std::shared_ptr<Type> GetVoidType() const { return void_type_; }
std::shared_ptr<Type> GetUnknownType() const { return unknown_type_; }
std::shared_ptr<Type> GetErrorType() const { return error_type_; }
std::shared_ptr<Type> CreateClassType(symbol::SymbolId class_id);
std::shared_ptr<Type> CreateArrayType(std::shared_ptr<Type> element_type);
std::shared_ptr<Type> CreateFunctionType(
std::vector<std::shared_ptr<Type>> param_types,
std::shared_ptr<Type> return_type);
std::shared_ptr<Type> CreateOptionalType(std::shared_ptr<Type> inner_type);
void RegisterClassType(const std::string& type_name, symbol::SymbolId class_id);
std::shared_ptr<Type> GetTypeByName(const std::string& type_name) const;
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const;
void RegisterSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type);
TypeCompatibility CheckCompatibility(const Type& from, const Type& to) const;
bool IsAssignable(const Type& from, const Type& to) const;
bool RequiresExplicitCast(const Type& from, const Type& to) const;
std::shared_ptr<Type> InferBinaryExpressionType(
const Type& left,
const Type& right,
const std::string& op) const;
std::shared_ptr<Type> InferUnaryExpressionType(
const Type& operand,
const std::string& op) const;
std::shared_ptr<Type> InferLiteralType(const std::string& literal_value) const;
void SetInheritanceChecker(
std::function<bool(symbol::SymbolId, symbol::SymbolId)> checker)
{
is_subclass_of_ = std::move(checker);
}
private:
std::shared_ptr<Type> int_type_;
std::shared_ptr<Type> float_type_;
std::shared_ptr<Type> string_type_;
std::shared_ptr<Type> bool_type_;
std::shared_ptr<Type> char_type_;
std::shared_ptr<Type> void_type_;
std::shared_ptr<Type> unknown_type_;
std::shared_ptr<Type> error_type_;
std::unordered_map<std::string, std::shared_ptr<Type>> type_by_name_;
std::unordered_map<symbol::SymbolId, std::shared_ptr<Type>> symbol_types_;
std::function<bool(symbol::SymbolId derived, symbol::SymbolId base)>
is_subclass_of_;
TypeCompatibility CheckPrimitiveCompatibility(
const PrimitiveType& from,
const PrimitiveType& to) const;
TypeCompatibility CheckClassCompatibility(
const ClassType& from,
const ClassType& to) const;
TypeCompatibility CheckArrayCompatibility(
const ArrayType& from,
const ArrayType& to) const;
TypeCompatibility CheckFunctionCompatibility(
const FunctionType& from,
const FunctionType& to) const;
};
}

View File

@ -1,30 +0,0 @@
#pragma once
#include "../ast/types.hpp"
#include "../symbol/types.hpp"
namespace lsp::language::semantic
{
struct Reference
{
ast::Location location;
symbol::SymbolId symbol_id;
bool is_definition;
bool is_write;
};
struct Call
{
symbol::SymbolId caller;
symbol::SymbolId callee;
ast::Location call_site;
};
struct Inheritance
{
symbol::SymbolId derived;
symbol::SymbolId base;
};
}

View File

@ -1,175 +0,0 @@
#pragma once
#include "./table.hpp"
namespace lsp::language::symbol
{
class Builder : public ast::ASTVisitor
{
public:
explicit Builder(SymbolTable& table);
void Build(ast::ASTNode& root);
void VisitProgram(ast::Program& node) override;
void VisitUnitDefinition(ast::UnitDefinition& node) override;
void VisitClassDefinition(ast::ClassDefinition& node) override;
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
void VisitClassMember(ast::ClassMember& node) override;
void VisitVarDeclaration(ast::VarDeclaration& node) override;
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
void VisitBlockStatement(ast::BlockStatement& node) override;
void VisitIfStatement(ast::IfStatement& node) override;
void VisitForInStatement(ast::ForInStatement& node) override;
void VisitForToStatement(ast::ForToStatement& node) override;
void VisitWhileStatement(ast::WhileStatement& node) override;
void VisitRepeatStatement(ast::RepeatStatement& node) override;
void VisitCaseStatement(ast::CaseStatement& node) override;
void VisitTryStatement(ast::TryStatement& node) override;
void VisitUsesStatement(ast::UsesStatement& node) override;
void VisitIdentifier(ast::Identifier& node) override;
void VisitCallExpression(ast::CallExpression& node) override;
void VisitAttributeExpression(ast::AttributeExpression& node) override;
void VisitAssignmentExpression(ast::AssignmentExpression& node) override;
void VisitLiteral(ast::Literal& node) override;
void VisitBinaryExpression(ast::BinaryExpression& node) override;
void VisitTernaryExpression(ast::TernaryExpression& node) override;
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
void VisitArrayExpression(ast::ArrayExpression& node) override;
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
void VisitNewExpression(ast::NewExpression& node) override;
void VisitEchoExpression(ast::EchoExpression& node) override;
void VisitRaiseExpression(ast::RaiseExpression& node) override;
void VisitInheritedExpression(ast::InheritedExpression& node) override;
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
void VisitBreakStatement(ast::BreakStatement& node) override;
void VisitContinueStatement(ast::ContinueStatement& node) override;
void VisitReturnStatement(ast::ReturnStatement& node) override;
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
void VisitColumnReference(ast::ColumnReference& node) override;
void VisitUnpackPattern(ast::UnpackPattern& node) override;
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
void VisitCompilerDirective(ast::CompilerDirective& node) override;
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
void VisitTSLXBlock(ast::TSLXBlock& node) override;
void VisitParameter(ast::Parameter& node) override;
private:
class ScopeGuard
{
public:
ScopeGuard(Builder& builder, ScopeId scope_id);
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& other) noexcept;
ScopeGuard& operator=(ScopeGuard&& other) noexcept;
~ScopeGuard();
private:
Builder* builder_ = nullptr;
ScopeId scope_id_ = kInvalidScopeId;
};
SymbolId CreateFunctionSymbol(
const std::string& name,
const ast::Location& location,
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
const std::optional<ast::TypeAnnotation>& return_type);
SymbolId CreateMethodSymbol(
const std::string& name,
const ast::Location& location,
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
const std::optional<ast::TypeAnnotation>& return_type,
ast::AccessModifier access = ast::AccessModifier::kPublic,
ast::MethodKind method_kind = ast::MethodKind::kOrdinary,
std::optional<ast::MethodModifier> method_modifier = ast::MethodModifier::kNone,
bool is_static = false);
SymbolId CreateSymbol(
const std::string& name,
protocol::SymbolKind kind,
const ast::Location& location,
const std::optional<std::string>& type_hint = std::nullopt);
SymbolId CreateSymbol(
const std::string& name,
protocol::SymbolKind kind,
const ast::ASTNode& node,
const std::optional<std::string>& type_hint = std::nullopt);
SymbolId CreateFieldSymbol(
const std::string& name,
const ast::Location& location,
const std::optional<ast::TypeAnnotation>& type,
std::optional<ast::ReferenceModifier> reference_modifier,
bool is_static);
ScopeGuard EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range);
ScopeGuard EnterScope(ScopeKind kind, const ast::Location& range);
void ExitScope();
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
void VisitExpression(ast::Expression& expr);
// Helper for visiting unary expressions
template<typename T>
void VisitUnaryExpressionNode(T& node)
{
if (node.argument)
{
VisitExpression(*node.argument);
}
}
std::optional<std::string> ExtractTypeName(const std::optional<ast::TypeAnnotation>& type) const;
std::vector<language::symbol::Parameter> BuildParameters(const std::vector<std::unique_ptr<ast::Parameter>>& parameters) const;
void ProcessLValue(const ast::LValue& lvalue, bool is_write);
std::optional<ScopeId> FindScopeOwnedBy(SymbolId owner) const;
private:
SymbolTable& table_;
ScopeId current_scope_id_;
std::optional<SymbolId> current_parent_symbol_id_;
std::optional<SymbolId> current_function_id_;
bool in_interface_section_;
ast::AccessModifier current_access_modifier_ = ast::AccessModifier::kPublic;
};
}

View File

@ -1,4 +1,10 @@
#include "./coordinator.hpp"
module;
export module lsp.language.symbol:index.coordinator;
import std;
import :types;
namespace lsp::language::symbol::index
{

View File

@ -1,33 +0,0 @@
#pragma once
#include <memory>
#include "../interface.hpp"
#include "./dispatcher.hpp"
#include "./location.hpp"
#include "./scope.hpp"
namespace lsp::language::symbol::index
{
class Coordinator
{
public:
Coordinator();
void Register(std::shared_ptr<ISymbolIndex> index);
void NotifyAdded(const Symbol& symbol);
void NotifyRemoved(SymbolId id);
void Clear();
Location& locations();
const Location& locations() const;
Scope& scopes();
const Scope& scopes() const;
private:
Dispatcher dispatcher_;
std::shared_ptr<Location> location_index_;
std::shared_ptr<Scope> scope_index_;
};
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <memory>
#include <utility>
#include <vector>
#include "../interface.hpp"
namespace lsp::language::symbol::index
{
class Dispatcher
{
public:
void RegisterIndex(std::shared_ptr<ISymbolIndex> index)
{
indexes_.push_back(std::move(index));
}
void NotifySymbolAdded(const Symbol& symbol) const
{
for (const auto& index : indexes_)
index->OnSymbolAdded(symbol);
}
void NotifySymbolRemoved(SymbolId id) const
{
for (const auto& index : indexes_)
index->OnSymbolRemoved(id);
}
void Clear() const
{
for (const auto& index : indexes_)
index->Clear();
}
private:
std::vector<std::shared_ptr<ISymbolIndex>> indexes_;
};
}

View File

@ -1,6 +1,11 @@
#include "location.hpp"
module;
#include <algorithm>
export module lsp.language.symbol:index.location;
import std;
import :types;
import lsp.language.ast;
namespace lsp::language::symbol::index
{
@ -29,10 +34,10 @@ namespace lsp::language::symbol::index
const ast::Location& location) const
{
EnsureSorted();
uint32_t pos = location.start_offset;
std::uint32_t pos = location.start_offset;
auto it =
std::lower_bound(entries_.begin(), entries_.end(), pos, [](const Entry& e, uint32_t p) { return e.start < p; });
std::lower_bound(entries_.begin(), entries_.end(), pos, [](const Entry& e, std::uint32_t p) { return e.start < p; });
if (it != entries_.begin())
{
@ -40,13 +45,13 @@ namespace lsp::language::symbol::index
}
std::optional<SymbolId> result;
uint32_t min_span = UINT32_MAX;
std::uint32_t min_span = std::numeric_limits<std::uint32_t>::max();
for (; it != entries_.end() && it->start <= pos; ++it)
{
if (pos >= it->start && pos < it->end)
{
uint32_t span = it->end - it->start;
std::uint32_t span = it->end - it->start;
if (span < min_span)
{
min_span = span;

View File

@ -1,37 +0,0 @@
#pragma once
#include <optional>
#include <vector>
#include "../interface.hpp"
#include "../types.hpp"
namespace lsp::language::symbol::index
{
class Location : public ISymbolIndex
{
public:
void OnSymbolAdded(const Symbol& symbol) override;
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
private:
struct Entry
{
uint32_t start;
uint32_t end;
SymbolId symbol_id;
bool operator<(const Entry& other) const;
};
void EnsureSorted() const;
mutable std::vector<Entry> entries_;
mutable bool needs_sort_ = false;
};
}

View File

@ -1,7 +1,12 @@
#include "scope.hpp"
module;
#include <algorithm>
#include <limits>
export module lsp.language.symbol:index.scope;
import std;
import :types;
import lsp.language.ast;
import lsp.utils.string;
namespace lsp::language::symbol::index
{
@ -85,7 +90,7 @@ namespace lsp::language::symbol::index
auto sym_it = it->second.symbols.find(name);
if (sym_it != it->second.symbols.end() && !sym_it->second.empty())
{
return sym_it->second.front(); // Return first symbol for overload resolution
return sym_it->second.front(); // Return first symbol for overload resolution
}
return std::nullopt;
}
@ -122,7 +127,7 @@ namespace lsp::language::symbol::index
auto sym_it = it->second.symbols.find(name);
if (sym_it != it->second.symbols.end())
{
result = sym_it->second; // Return all symbols with this name
result = sym_it->second; // Return all symbols with this name
}
return result;
@ -141,11 +146,11 @@ namespace lsp::language::symbol::index
std::optional<ScopeId> Scope::FindScopeAt(const ast::Location& location) const
{
EnsureSorted();
uint32_t pos = location.start_offset;
std::uint32_t pos = location.start_offset;
// Binary search for the first entry that might contain this position
auto it = std::lower_bound(scope_entries_.begin(), scope_entries_.end(), pos,
[](const ScopeEntry& e, uint32_t p) { return e.start < p; });
auto it = std::lower_bound(
scope_entries_.begin(), scope_entries_.end(), pos, [](const ScopeEntry& e, std::uint32_t p) { return e.start < p; });
if (it != scope_entries_.begin())
{
@ -154,13 +159,13 @@ namespace lsp::language::symbol::index
// Find the smallest containing scope
std::optional<ScopeId> result;
uint32_t smallest_span = std::numeric_limits<uint32_t>::max();
std::uint32_t smallest_span = std::numeric_limits<std::uint32_t>::max();
for (; it != scope_entries_.end() && it->start <= pos; ++it)
{
if (pos >= it->start && pos < it->end)
{
uint32_t span = it->end - it->start;
std::uint32_t span = it->end - it->start;
if (span < smallest_span)
{
smallest_span = span;

View File

@ -1,79 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "../interface.hpp"
#include "../types.hpp"
#include "../../../utils/string.hpp"
namespace lsp::language::symbol
{
struct ScopeInfo
{
ScopeId id;
ScopeKind kind;
ast::Location range;
std::optional<ScopeId> parent;
std::optional<SymbolId> owner;
std::unordered_map<std::string, std::vector<SymbolId>, utils::IHasher, utils::IEqualTo> symbols;
};
}
namespace lsp::language::symbol::index
{
class Scope : public ISymbolIndex
{
public:
void OnSymbolAdded(const Symbol&) override;
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt);
// Adds a symbol to the scope. Multiple symbols with the same name are allowed (for overloading).
// Returns true if this is the first symbol with this name, false if there are existing symbols.
bool AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
std::optional<SymbolId> FindSymbolInScope(ScopeId scope_id, const std::string& name) const;
std::optional<SymbolId> FindSymbolInScopeChain(ScopeId scope_id, const std::string& name) const;
std::vector<SymbolId> FindSymbols(ScopeId scope_id, const std::string& name) const;
std::optional<ScopeId> GetParent(ScopeId scope_id) const;
std::optional<ScopeId> FindScopeAt(const ast::Location& location) const;
const symbol::ScopeInfo* scope(ScopeId id) const;
ScopeId global_scope() const;
const std::unordered_map<ScopeId, symbol::ScopeInfo>& all_scopes() const;
private:
struct ScopeEntry
{
uint32_t start;
uint32_t end;
ScopeId scope_id;
bool operator<(const ScopeEntry& other) const
{
if (start != other.start)
{
return start < other.start;
}
return end > other.end; // Smaller scopes first for nested scopes
}
};
void EnsureSorted() const;
ScopeId next_scope_id_ = 1;
ScopeId global_scope_ = kInvalidScopeId;
std::unordered_map<ScopeId, symbol::ScopeInfo> scopes_;
mutable std::vector<ScopeEntry> scope_entries_;
mutable bool needs_sort_ = false;
};
}

View File

@ -1,18 +0,0 @@
#pragma once
#include "./types.hpp"
namespace lsp::language::symbol
{
class ISymbolIndex
{
public:
virtual ~ISymbolIndex() = default;
virtual void OnSymbolAdded(const Symbol& symbol) = 0;
virtual void OnSymbolRemoved(SymbolId id) = 0;
virtual void Clear() = 0;
};
}

View File

@ -1,6 +1,12 @@
#include <utility>
module;
#include "./builder.hpp"
import std;
module lsp.language.symbol:internal.builder;
import :types;
import lsp.language.ast;
import lsp.protocol.types;
namespace lsp::language::symbol
{
@ -440,7 +446,7 @@ namespace lsp::language::symbol
ast::ExternalMethodDefinition& node)
{
std::string method_name = node.name;
size_t dot_pos = method_name.find_last_of('.');
std::size_t dot_pos = method_name.find_last_of('.');
if (dot_pos != std::string::npos)
{
method_name = method_name.substr(dot_pos + 1);
@ -633,7 +639,7 @@ namespace lsp::language::symbol
imports.reserve(node.units.size());
for (const auto& unit : node.units)
{
imports.push_back({unit.name, unit.location});
imports.push_back({ unit.name, unit.location });
}
// Add imports to the appropriate symbol type

View File

@ -1,6 +1,11 @@
#include <algorithm>
module;
#include "./store.hpp"
import std;
module lsp.language.symbol:internal.store;
import :types;
import lsp.utils.string;
namespace lsp::language::symbol
{
@ -62,12 +67,10 @@ namespace lsp::language::symbol
result.push_back(std::cref(def));
}
std::sort(result.begin(), result.end(),
[](const std::reference_wrapper<const Symbol>& a,
const std::reference_wrapper<const Symbol>& b) {
return a.get().selection_range().start_offset <
b.get().selection_range().start_offset;
});
std::sort(result.begin(), result.end(), [](const std::reference_wrapper<const Symbol>& a, const std::reference_wrapper<const Symbol>& b) {
return a.get().selection_range().start_offset <
b.get().selection_range().start_offset;
});
return result;
}

View File

@ -1,4 +1,10 @@
#include "./table.hpp"
module;
import std;
module lsp.language.symbol:internal.table;
import :types;
namespace lsp::language::symbol
{

View File

@ -1,31 +0,0 @@
#pragma once
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#include "./types.hpp"
#include "../../utils/string.hpp"
namespace lsp::language::symbol
{
class SymbolStore
{
public:
SymbolId Add(Symbol def);
bool Remove(SymbolId id);
void Clear();
const Symbol* Get(SymbolId id) const;
std::vector<std::reference_wrapper<const Symbol>> GetAll() const;
std::vector<SymbolId> FindByName(const std::string& name) const;
private:
SymbolId next_id_ = 1;
std::unordered_map<SymbolId, Symbol> symbols_;
std::unordered_map<std::string, std::vector<SymbolId>, utils::IHasher, utils::IEqualTo> symbols_by_name_;
};
}

View File

@ -0,0 +1,8 @@
module;
export module lsp.language.symbol;
export import :types;
export import :index.coordinator;
export import :index.location;
export import :index.scope;

View File

@ -1,45 +0,0 @@
#pragma once
#include <functional>
#include <memory>
#include <optional>
#include <vector>
#include "./index/coordinator.hpp"
#include "./store.hpp"
namespace lsp::language::symbol
{
class SymbolTable
{
public:
SymbolTable();
~SymbolTable();
SymbolId CreateSymbol(Symbol symbol);
bool RemoveSymbol(SymbolId id);
void Clear();
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt);
bool AddSymbolToScope(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
void RegisterIndex(std::shared_ptr<ISymbolIndex> index);
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const;
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
const Symbol* definition(SymbolId id) const;
std::vector<std::reference_wrapper<const Symbol>> all_definitions() const;
index::Location& locations();
const index::Location& locations() const;
index::Scope& scopes();
const index::Scope& scopes() const;
private:
SymbolStore store_;
index::Coordinator index_coordinator_;
};
}

View File

@ -0,0 +1,614 @@
module;
export module lsp.language.symbol:types;
import std;
import lsp.language.ast;
import lsp.protocol;
import lsp.utils.string;
export namespace lsp::language::symbol
{
using SymbolId = std::uint64_t;
constexpr SymbolId kInvalidSymbolId = 0;
using ScopeId = std::uint64_t;
constexpr ScopeId kInvalidScopeId = 0;
enum class ScopeKind
{
kGlobal,
kUnit,
kClass,
kFunction,
kAnonymousFunction,
kBlock
};
enum class VariableScope
{
kAutomatic,
kStatic,
kGlobal,
kParameter,
kField
};
enum class UnitVisibility
{
kInterface,
kImplementation
};
struct Parameter
{
std::string name;
std::optional<std::string> type;
std::optional<std::string> default_value;
};
struct UnitImport
{
std::string unit_name;
ast::Location location;
};
struct Function
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Function;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::Location declaration_range;
std::optional<ast::Location> implementation_range;
std::vector<Parameter> parameters;
std::optional<std::string> return_type;
std::vector<UnitImport> imports;
std::optional<UnitVisibility> unit_visibility;
bool HasImplementation() const { return implementation_range.has_value(); }
};
struct Class
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Class;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<UnitVisibility> unit_visibility;
std::vector<SymbolId> base_classes;
std::vector<SymbolId> members;
std::vector<UnitImport> imports;
};
struct Method
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Method;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::Location declaration_range;
std::optional<ast::Location> implementation_range;
ast::MethodKind method_kind = ast::MethodKind::kOrdinary;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<ast::MethodModifier> method_modifier = ast::MethodModifier::kNone;
bool is_static = false;
std::vector<Parameter> parameters;
std::optional<std::string> return_type;
std::vector<UnitImport> imports;
bool HasImplementation() const { return implementation_range.has_value(); }
};
struct Property
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Property;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<std::string> type;
std::optional<SymbolId> getter;
std::optional<SymbolId> setter;
};
struct Field
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Field;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<ast::ReferenceModifier> reference_modifier;
std::optional<std::string> type;
bool is_static = false;
};
struct Variable
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Variable;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<std::string> type;
std::optional<ast::ReferenceModifier> reference_modifier;
VariableScope storage = VariableScope::kAutomatic;
std::optional<UnitVisibility> unit_visibility;
bool has_initializer = false;
};
struct Constant
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Constant;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<std::string> type;
std::string value;
};
struct Unit
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Module;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::vector<UnitImport> interface_imports;
std::vector<UnitImport> implementation_imports;
};
using SymbolData = std::variant<Function, Class, Method, Property, Field, Variable, Constant, Unit>;
class Symbol
{
public:
explicit Symbol(SymbolData data) : data_(std::move(data)) {}
template<typename T>
bool Is() const
{
return std::holds_alternative<T>(data_);
}
template<typename T>
const T* As() const
{
return std::get_if<T>(&data_);
}
template<typename T>
T* As()
{
return std::get_if<T>(&data_);
}
const SymbolData& data() const { return data_; }
SymbolData& mutable_data() { return data_; }
SymbolId id() const
{
return std::visit([](const auto& s) { return s.id; }, data_);
}
const std::string& name() const
{
return std::visit([](const auto& s) -> const auto& { return s.name; }, data_);
}
ast::Location selection_range() const
{
return std::visit([](const auto& s) { return s.selection_range; }, data_);
}
ast::Location range() const
{
return std::visit([](const auto& s) { return s.range; }, data_);
}
protocol::SymbolKind kind() const
{
return std::visit([](const auto& s) { return s.kind; }, data_);
}
private:
SymbolData data_;
};
class ISymbolIndex
{
public:
virtual ~ISymbolIndex() = default;
virtual void OnSymbolAdded(const Symbol& symbol) = 0;
virtual void OnSymbolRemoved(SymbolId id) = 0;
virtual void Clear() = 0;
};
class SymbolStore
{
public:
SymbolId Add(Symbol def);
bool Remove(SymbolId id);
void Clear();
const Symbol* Get(SymbolId id) const;
std::vector<std::reference_wrapper<const Symbol>> GetAll() const;
std::vector<SymbolId> FindByName(const std::string& name) const;
private:
SymbolId next_id_ = 1;
std::unordered_map<SymbolId, Symbol> symbols_;
std::unordered_map<std::string, std::vector<SymbolId>, utils::IHasher, utils::IEqualTo> symbols_by_name_;
};
struct ScopeInfo
{
ScopeId id;
ScopeKind kind;
ast::Location range;
std::optional<ScopeId> parent;
std::optional<SymbolId> owner;
std::unordered_map<std::string, std::vector<SymbolId>, utils::IHasher, utils::IEqualTo> symbols;
};
namespace index
{
class Dispatcher
{
public:
void RegisterIndex(std::shared_ptr<ISymbolIndex> index)
{
indexes_.push_back(std::move(index));
}
void NotifySymbolAdded(const Symbol& symbol) const
{
for (const auto& index : indexes_)
index->OnSymbolAdded(symbol);
}
void NotifySymbolRemoved(SymbolId id) const
{
for (const auto& index : indexes_)
index->OnSymbolRemoved(id);
}
void Clear() const
{
for (const auto& index : indexes_)
index->Clear();
}
private:
std::vector<std::shared_ptr<ISymbolIndex>> indexes_;
};
class Location : public ISymbolIndex
{
public:
void OnSymbolAdded(const Symbol& symbol) override;
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
private:
struct Entry
{
std::uint32_t start;
std::uint32_t end;
SymbolId symbol_id;
bool operator<(const Entry& other) const;
};
void EnsureSorted() const;
mutable std::vector<Entry> entries_;
mutable bool needs_sort_ = false;
};
class Scope : public ISymbolIndex
{
public:
void OnSymbolAdded(const Symbol&) override;
void OnSymbolRemoved(SymbolId id) override;
void Clear() override;
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt);
// Adds a symbol to the scope. Multiple symbols with the same name are allowed (for overloading).
// Returns true if this is the first symbol with this name, false if there are existing symbols.
bool AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
std::optional<SymbolId> FindSymbolInScope(ScopeId scope_id, const std::string& name) const;
std::optional<SymbolId> FindSymbolInScopeChain(ScopeId scope_id, const std::string& name) const;
std::vector<SymbolId> FindSymbols(ScopeId scope_id, const std::string& name) const;
std::optional<ScopeId> GetParent(ScopeId scope_id) const;
std::optional<ScopeId> FindScopeAt(const ast::Location& location) const;
const ScopeInfo* scope(ScopeId id) const;
ScopeId global_scope() const;
const std::unordered_map<ScopeId, ScopeInfo>& all_scopes() const;
private:
struct ScopeEntry
{
std::uint32_t start;
std::uint32_t end;
ScopeId scope_id;
bool operator<(const ScopeEntry& other) const
{
if (start != other.start)
{
return start < other.start;
}
return end > other.end; // Smaller scopes first for nested scopes
}
};
void EnsureSorted() const;
ScopeId next_scope_id_ = 1;
ScopeId global_scope_ = kInvalidScopeId;
std::unordered_map<ScopeId, ScopeInfo> scopes_;
mutable std::vector<ScopeEntry> scope_entries_;
mutable bool needs_sort_ = false;
};
class Coordinator
{
public:
Coordinator();
void Register(std::shared_ptr<ISymbolIndex> index);
void NotifyAdded(const Symbol& symbol);
void NotifyRemoved(SymbolId id);
void Clear();
Location& locations();
const Location& locations() const;
Scope& scopes();
const Scope& scopes() const;
private:
Dispatcher dispatcher_;
std::shared_ptr<Location> location_index_;
std::shared_ptr<Scope> scope_index_;
};
}
class SymbolTable
{
public:
SymbolTable();
~SymbolTable();
SymbolId CreateSymbol(Symbol symbol);
bool RemoveSymbol(SymbolId id);
void Clear();
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt);
bool AddSymbolToScope(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
void RegisterIndex(std::shared_ptr<ISymbolIndex> index);
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const;
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
const Symbol* definition(SymbolId id) const;
std::vector<std::reference_wrapper<const Symbol>> all_definitions() const;
index::Location& locations();
const index::Location& locations() const;
index::Scope& scopes();
const index::Scope& scopes() const;
private:
SymbolStore store_;
index::Coordinator index_coordinator_;
};
class Builder : public ast::ASTVisitor
{
public:
explicit Builder(SymbolTable& table);
void Build(ast::ASTNode& root);
void VisitProgram(ast::Program& node) override;
void VisitUnitDefinition(ast::UnitDefinition& node) override;
void VisitClassDefinition(ast::ClassDefinition& node) override;
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
void VisitClassMember(ast::ClassMember& node) override;
void VisitVarDeclaration(ast::VarDeclaration& node) override;
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
void VisitBlockStatement(ast::BlockStatement& node) override;
void VisitIfStatement(ast::IfStatement& node) override;
void VisitForInStatement(ast::ForInStatement& node) override;
void VisitForToStatement(ast::ForToStatement& node) override;
void VisitWhileStatement(ast::WhileStatement& node) override;
void VisitRepeatStatement(ast::RepeatStatement& node) override;
void VisitCaseStatement(ast::CaseStatement& node) override;
void VisitTryStatement(ast::TryStatement& node) override;
void VisitUsesStatement(ast::UsesStatement& node) override;
void VisitIdentifier(ast::Identifier& node) override;
void VisitCallExpression(ast::CallExpression& node) override;
void VisitAttributeExpression(ast::AttributeExpression& node) override;
void VisitAssignmentExpression(ast::AssignmentExpression& node) override;
void VisitLiteral(ast::Literal& node) override;
void VisitBinaryExpression(ast::BinaryExpression& node) override;
void VisitTernaryExpression(ast::TernaryExpression& node) override;
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
void VisitArrayExpression(ast::ArrayExpression& node) override;
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
void VisitNewExpression(ast::NewExpression& node) override;
void VisitEchoExpression(ast::EchoExpression& node) override;
void VisitRaiseExpression(ast::RaiseExpression& node) override;
void VisitInheritedExpression(ast::InheritedExpression& node) override;
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
void VisitBreakStatement(ast::BreakStatement& node) override;
void VisitContinueStatement(ast::ContinueStatement& node) override;
void VisitReturnStatement(ast::ReturnStatement& node) override;
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
void VisitColumnReference(ast::ColumnReference& node) override;
void VisitUnpackPattern(ast::UnpackPattern& node) override;
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
void VisitCompilerDirective(ast::CompilerDirective& node) override;
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
void VisitTSLXBlock(ast::TSLXBlock& node) override;
void VisitParameter(ast::Parameter& node) override;
private:
class ScopeGuard
{
public:
ScopeGuard(Builder& builder, ScopeId scope_id);
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& other) noexcept;
ScopeGuard& operator=(ScopeGuard&& other) noexcept;
~ScopeGuard();
private:
Builder* builder_ = nullptr;
ScopeId scope_id_ = kInvalidScopeId;
};
SymbolId CreateFunctionSymbol(
const std::string& name,
const ast::Location& location,
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
const std::optional<ast::TypeAnnotation>& return_type);
SymbolId CreateMethodSymbol(
const std::string& name,
const ast::Location& location,
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
const std::optional<ast::TypeAnnotation>& return_type,
ast::AccessModifier access = ast::AccessModifier::kPublic,
ast::MethodKind method_kind = ast::MethodKind::kOrdinary,
std::optional<ast::MethodModifier> method_modifier = ast::MethodModifier::kNone,
bool is_static = false);
SymbolId CreateSymbol(
const std::string& name,
protocol::SymbolKind kind,
const ast::Location& location,
const std::optional<std::string>& type_hint = std::nullopt);
SymbolId CreateSymbol(
const std::string& name,
protocol::SymbolKind kind,
const ast::ASTNode& node,
const std::optional<std::string>& type_hint = std::nullopt);
SymbolId CreateFieldSymbol(
const std::string& name,
const ast::Location& location,
const std::optional<ast::TypeAnnotation>& type,
std::optional<ast::ReferenceModifier> reference_modifier,
bool is_static);
ScopeGuard EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range);
ScopeGuard EnterScope(ScopeKind kind, const ast::Location& range);
void ExitScope();
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
void VisitExpression(ast::Expression& expr);
template<typename T>
void VisitUnaryExpressionNode(T& node)
{
if (node.argument)
{
VisitExpression(*node.argument);
}
}
std::optional<std::string> ExtractTypeName(const std::optional<ast::TypeAnnotation>& type) const;
std::vector<language::symbol::Parameter> BuildParameters(const std::vector<std::unique_ptr<ast::Parameter>>& parameters) const;
void ProcessLValue(const ast::LValue& lvalue, bool is_write);
std::optional<ScopeId> FindScopeOwnedBy(SymbolId owner) const;
private:
SymbolTable& table_;
ScopeId current_scope_id_;
std::optional<SymbolId> current_parent_symbol_id_;
std::optional<SymbolId> current_function_id_;
bool in_interface_section_;
ast::AccessModifier current_access_modifier_ = ast::AccessModifier::kPublic;
};
}

View File

@ -1,249 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "../../protocol/protocol.hpp"
#include "../ast/types.hpp"
namespace lsp::language::symbol
{
using SymbolId = uint64_t;
constexpr SymbolId kInvalidSymbolId = 0;
using ScopeId = uint64_t;
constexpr ScopeId kInvalidScopeId = 0;
enum class ScopeKind
{
kGlobal,
kUnit,
kClass,
kFunction,
kAnonymousFunction,
kBlock
};
enum class VariableScope
{
kAutomatic,
kStatic,
kGlobal,
kParameter,
kField
};
enum class UnitVisibility
{
kInterface,
kImplementation
};
struct Parameter
{
std::string name;
std::optional<std::string> type;
std::optional<std::string> default_value;
};
struct UnitImport
{
std::string unit_name;
ast::Location location;
};
struct Function
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Function;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::Location declaration_range;
std::optional<ast::Location> implementation_range;
std::vector<Parameter> parameters;
std::optional<std::string> return_type;
std::vector<UnitImport> imports;
std::optional<UnitVisibility> unit_visibility;
bool HasImplementation() const { return implementation_range.has_value(); }
};
struct Class
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Class;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<UnitVisibility> unit_visibility;
std::vector<SymbolId> base_classes;
std::vector<SymbolId> members;
std::vector<UnitImport> imports;
};
struct Method
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Method;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::Location declaration_range;
std::optional<ast::Location> implementation_range;
ast::MethodKind method_kind = ast::MethodKind::kOrdinary;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<ast::MethodModifier> method_modifier = ast::MethodModifier::kNone;
bool is_static = false;
std::vector<Parameter> parameters;
std::optional<std::string> return_type;
std::vector<UnitImport> imports;
bool HasImplementation() const { return implementation_range.has_value(); }
};
struct Property
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Property;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<std::string> type;
std::optional<SymbolId> getter;
std::optional<SymbolId> setter;
};
struct Field
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Field;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
ast::AccessModifier access = ast::AccessModifier::kPublic;
std::optional<ast::ReferenceModifier> reference_modifier;
std::optional<std::string> type;
bool is_static = false;
};
struct Variable
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Variable;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<std::string> type;
std::optional<ast::ReferenceModifier> reference_modifier;
VariableScope storage = VariableScope::kAutomatic;
std::optional<UnitVisibility> unit_visibility;
bool has_initializer = false;
};
struct Constant
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Constant;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::optional<std::string> type;
std::string value;
};
struct Unit
{
static constexpr protocol::SymbolKind kind = protocol::SymbolKind::Module;
SymbolId id = kInvalidSymbolId;
std::string name;
ast::Location selection_range;
ast::Location range;
std::vector<UnitImport> interface_imports;
std::vector<UnitImport> implementation_imports;
};
using SymbolData = std::variant<Function, Class, Method, Property, Field, Variable, Constant, Unit>;
class Symbol
{
public:
explicit Symbol(SymbolData data) : data_(std::move(data)) {}
template<typename T>
bool Is() const
{
return std::holds_alternative<T>(data_);
}
template<typename T>
const T* As() const
{
return std::get_if<T>(&data_);
}
template<typename T>
T* As()
{
return std::get_if<T>(&data_);
}
const SymbolData& data() const { return data_; }
SymbolData& mutable_data() { return data_; }
SymbolId id() const
{
return std::visit([](const auto& s) { return s.id; }, data_);
}
const std::string& name() const
{
return std::visit([](const auto& s) -> const auto& { return s.name; },
data_);
}
ast::Location selection_range() const
{
return std::visit([](const auto& s) { return s.selection_range; }, data_);
}
ast::Location range() const
{
return std::visit([](const auto& s) { return s.range; }, data_);
}
protocol::SymbolKind kind() const
{
return std::visit([](const auto& s) { return s.kind; }, data_);
}
private:
SymbolData data_;
};
}

View File

@ -1,10 +1,12 @@
#include <iostream>
#include <exception>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include "./core/server.hpp"
#include "./utils/args_parser.hpp"
module;
export module lsp.main;
import spdlog;
import std;
import lsp.core.server;
import lsp.utils.args_parser;
int main(int argc, char* argv[])
{

View File

@ -1,6 +1,22 @@
#include "bootstrap.hpp"
#include <spdlog/spdlog.h>
#include "../utils/args_parser.hpp"
module;
#include <format>
export module lsp.manager.bootstrap;
import spdlog;
import std;
import lsp.manager.manager_hub;
import lsp.scheduler.async_executor;
import lsp.utils.args_parser;
export namespace lsp::manager::bootstrap
{
void InitializeManagerHub(
ManagerHub& hub,
scheduler::AsyncExecutor& async_executor,
const std::vector<std::string>& system_lib_paths);
}
namespace lsp::manager::bootstrap
{
@ -13,7 +29,7 @@ namespace lsp::manager::bootstrap
for (const auto& path : system_lib_paths)
{
std::string task_name = fmt::format("Load system library: {}", path);
std::string task_name = std::format("Load system library: {}", path);
async_executor.Submit(
task_name,
@ -21,7 +37,7 @@ namespace lsp::manager::bootstrap
try
{
hub.symbols().LoadSystemLibrary(path);
return fmt::format("Loaded system library: {}", path);
return std::format("Loaded system library: {}", path);
}
catch (const std::exception& e)
{

View File

@ -1,11 +0,0 @@
#pragma once
#include "../manager/manager_hub.hpp"
#include "../scheduler/async_executor.hpp"
namespace lsp::manager::bootstrap
{
void InitializeManagerHub(
ManagerHub& hub,
scheduler::AsyncExecutor& async_executor,
const std::vector<std::string>& system_lib_paths);
}

View File

@ -1,6 +1,34 @@
#include "text_document.hpp"
#include <spdlog/spdlog.h>
#include "../../utils/text_coordinates.hpp"
module;
export module lsp.manager.detail.text_document;
import tree_sitter;
import spdlog;
import std;
import lsp.protocol;
import lsp.utils.text_coordinates;
export namespace lsp::manager::detail
{
class TextDocument
{
public:
explicit TextDocument(const protocol::TextDocumentItem& item);
void ApplyChange(const protocol::TextDocumentContentChangeEvent& change);
void SetVersion(protocol::integer version);
const protocol::string& GetContent() const;
const protocol::DocumentUri& GetUri() const;
protocol::integer GetVersion() const;
private:
static bool IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content);
protocol::TextDocumentItem item_;
};
}
namespace lsp::manager::detail
{
@ -47,7 +75,7 @@ namespace lsp::manager::detail
if (change.range.start.line == 0 && change.range.start.character == 0)
{
protocol::uinteger line_count = 0;
for (size_t i = 0; i < current_content.length(); ++i)
for (std::size_t i = 0; i < current_content.length(); ++i)
{
if (current_content[i] == '\n')
line_count++;

View File

@ -1,27 +0,0 @@
#pragma once
#include "../../protocol/protocol.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
namespace lsp::manager::detail
{
class TextDocument
{
public:
explicit TextDocument(const protocol::TextDocumentItem& item);
void ApplyChange(const protocol::TextDocumentContentChangeEvent& change);
void SetVersion(protocol::integer version);
const protocol::string& GetContent() const;
const protocol::DocumentUri& GetUri() const;
protocol::integer GetVersion() const;
private:
static bool IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content);
protocol::TextDocumentItem item_;
};
}

View File

@ -1,6 +1,39 @@
#include "document.hpp"
#include <spdlog/spdlog.h>
#include "./events.hpp"
module;
export module lsp.manager.document;
import spdlog;
import std;
import lsp.protocol;
import lsp.manager.event_bus;
import lsp.manager.detail.text_document;
import lsp.manager.events;
export namespace lsp::manager
{
class Document
{
public:
explicit Document(EventBus& event_bus);
~Document();
void OpenDocument(const protocol::DidOpenTextDocumentParams& params);
void UpdateDocument(const protocol::DidChangeTextDocumentParams& params);
void CloseDocument(const protocol::DidCloseTextDocumentParams& params);
std::optional<protocol::string> GetContent(const protocol::DocumentUri& uri) const;
std::optional<protocol::integer> GetVersion(const protocol::DocumentUri& uri) const;
std::vector<protocol::DocumentUri> GetAllDocumentUris() const;
void Clear();
private:
mutable std::shared_mutex mutex_;
std::unordered_map<protocol::DocumentUri, detail::TextDocument> documents_;
EventBus& event_bus_;
};
}
namespace lsp::manager
{

View File

@ -1,33 +0,0 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <shared_mutex>
#include "./event_bus.hpp"
#include "./detail/text_document.hpp"
#include "../protocol/protocol.hpp"
namespace lsp::manager
{
class Document
{
public:
explicit Document(EventBus& event_bus);
~Document();
void OpenDocument(const protocol::DidOpenTextDocumentParams& params);
void UpdateDocument(const protocol::DidChangeTextDocumentParams& params);
void CloseDocument(const protocol::DidCloseTextDocumentParams& params);
std::optional<protocol::string> GetContent(const protocol::DocumentUri& uri) const;
std::optional<protocol::integer> GetVersion(const protocol::DocumentUri& uri) const;
std::vector<protocol::DocumentUri> GetAllDocumentUris() const;
void Clear();
private:
mutable std::shared_mutex mutex_;
std::unordered_map<protocol::DocumentUri, detail::TextDocument> documents_;
EventBus& event_bus_;
};
}

View File

@ -1,13 +1,10 @@
#pragma once
module;
#include <functional>
#include <mutex>
#include <shared_mutex>
#include <typeindex>
#include <unordered_map>
#include <vector>
export module lsp.manager.event_bus;
namespace lsp::manager
import std;
export namespace lsp::manager
{
class EventBus
{
@ -46,10 +43,10 @@ namespace lsp::manager
handlers_.erase(type);
}
size_t GetSubscriberCount() const
std::size_t GetSubscriberCount() const
{
std::shared_lock<std::shared_mutex> lock(mutex_);
size_t count = 0;
std::size_t count = 0;
for (const auto& [type, handlers] : handlers_)
count += handlers.size();
return count;

View File

@ -1,12 +1,14 @@
#pragma once
module;
#include "../protocol/protocol.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
export module lsp.manager.events;
import tree_sitter;
namespace lsp::manager::events
import std;
import lsp.protocol;
export namespace lsp::manager::events
{
using DocumentOpened = protocol::DidOpenTextDocumentParams;
using DocumentClosed = protocol::DidCloseTextDocumentParams;

View File

@ -1,29 +0,0 @@
#include "manager_hub.hpp"
#include <algorithm>
#include "../utils/text_coordinates.hpp"
namespace lsp::manager
{
ManagerHub::ManagerHub() : event_bus_(),
documents_(event_bus_),
parser_(event_bus_),
symbols_(event_bus_)
{
}
ManagerHub::~ManagerHub() = default;
void ManagerHub::Initialize()
{
documents_.Clear();
parser_.Clear();
}
void ManagerHub::Shutdown()
{
documents_.Clear();
parser_.Clear();
}
}

View File

@ -0,0 +1,60 @@
module;
export module lsp.manager.manager_hub;
import std;
import lsp.protocol;
import lsp.manager.event_bus;
import lsp.manager.document;
import lsp.manager.parser;
import lsp.manager.symbol;
import lsp.utils.text_coordinates;
export namespace lsp::manager
{
class ManagerHub
{
public:
ManagerHub();
~ManagerHub();
void Initialize();
void Shutdown();
Document& documents() { return documents_; }
Parser& parser() { return parser_; }
Symbol& symbols() { return symbols_; }
private:
EventBus event_bus_;
Document documents_;
Parser parser_;
Symbol symbols_;
};
}
namespace lsp::manager
{
ManagerHub::ManagerHub() : event_bus_(),
documents_(event_bus_),
parser_(event_bus_),
symbols_(event_bus_)
{
}
ManagerHub::~ManagerHub() = default;
void ManagerHub::Initialize()
{
documents_.Clear();
parser_.Clear();
}
void ManagerHub::Shutdown()
{
documents_.Clear();
parser_.Clear();
}
}

View File

@ -1,34 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include "../manager/event_bus.hpp"
#include "../manager/document.hpp"
#include "../manager/parser.hpp"
#include "../manager/symbol.hpp"
#include "../protocol/protocol.hpp"
namespace lsp::manager
{
class ManagerHub
{
public:
ManagerHub();
~ManagerHub();
void Initialize();
void Shutdown();
Document& documents() { return documents_; }
Parser& parser() { return parser_; }
Symbol& symbols() { return symbols_; }
private:
EventBus event_bus_;
Document documents_;
Parser parser_;
Symbol symbols_;
};
}

View File

@ -1,6 +1,72 @@
#include "parser.hpp"
#include <spdlog/spdlog.h>
#include "../utils/text_coordinates.hpp"
module;
export module lsp.manager.parser;
import tree_sitter;
import spdlog;
import std;
import lsp.protocol;
import lsp.manager.events;
import lsp.manager.event_bus;
import lsp.utils.text_coordinates;
extern "C" const TSLanguage* tree_sitter_tsf(void);
export namespace lsp::manager
{
class TreeSitter
{
public:
TreeSitter();
TreeSitter(const TreeSitter&) = delete;
TreeSitter& operator=(const TreeSitter&) = delete;
TreeSitter(TreeSitter&& other) noexcept;
~TreeSitter();
bool SetLanguage(const TSLanguage* language);
TSTree* Parse(const char* content, std::size_t length, TSTree* old_tree = nullptr);
TSParser* GetRawParser() const;
private:
TSParser* parser_;
};
class SyntaxTree
{
public:
explicit SyntaxTree(TSTree* tree);
~SyntaxTree();
void ApplyEdit(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& content);
TSTree* Get() const;
TSNode GetRootNode() const;
private:
std::unique_ptr<TSTree, void (*)(TSTree*)> tree_;
};
class Parser
{
public:
explicit Parser(EventBus& event_bus);
~Parser();
TSTree* GetTree(const protocol::DocumentUri& uri) const;
TSParser* GetRawParser() const;
void Clear();
private:
void OnDocumentOpened(const events::DocumentOpened& event);
void OnDocumentChanged(const events::DocumentChanged& event);
void OnDocumentClosed(const events::DocumentClosed& event);
TreeSitter parser_;
std::unordered_map<protocol::DocumentUri, std::unique_ptr<SyntaxTree>> trees_;
EventBus& event_bus_;
mutable std::shared_mutex mutex_;
};
}
namespace lsp::manager
{
@ -29,7 +95,7 @@ namespace lsp::manager
return ts_parser_set_language(parser_, language);
}
TSTree* TreeSitter::Parse(const char* content, size_t length, TSTree* old_tree)
TSTree* TreeSitter::Parse(const char* content, std::size_t length, TSTree* old_tree)
{
if (!parser_)
return nullptr;

View File

@ -1,70 +0,0 @@
#pragma once
#include <memory>
#include <optional>
#include <shared_mutex>
#include <unordered_map>
#include "./events.hpp"
#include "./event_bus.hpp"
#include "../protocol/protocol.hpp"
extern "C" {
#include <tree_sitter/api.h>
}
extern "C" const TSLanguage* tree_sitter_tsf(void);
namespace lsp::manager
{
class TreeSitter
{
public:
TreeSitter();
TreeSitter(const TreeSitter&) = delete;
TreeSitter& operator=(const TreeSitter&) = delete;
TreeSitter(TreeSitter&& other) noexcept;
~TreeSitter();
bool SetLanguage(const TSLanguage* language);
TSTree* Parse(const char* content, size_t length, TSTree* old_tree = nullptr);
TSParser* GetRawParser() const;
private:
TSParser* parser_;
};
class SyntaxTree
{
public:
explicit SyntaxTree(TSTree* tree);
~SyntaxTree();
void ApplyEdit(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& content);
TSTree* Get() const;
TSNode GetRootNode() const;
private:
std::unique_ptr<TSTree, void (*)(TSTree*)> tree_;
};
class Parser
{
public:
explicit Parser(EventBus& event_bus);
~Parser();
TSTree* GetTree(const protocol::DocumentUri& uri) const;
TSParser* GetRawParser() const;
void Clear();
private:
void OnDocumentOpened(const events::DocumentOpened& event);
void OnDocumentChanged(const events::DocumentChanged& event);
void OnDocumentClosed(const events::DocumentClosed& event);
TreeSitter parser_;
std::unordered_map<protocol::DocumentUri, std::unique_ptr<SyntaxTree>> trees_;
EventBus& event_bus_;
mutable std::shared_mutex mutex_;
};
}

View File

@ -1,15 +1,88 @@
#include "symbol.hpp"
#include <spdlog/spdlog.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <cctype>
#include "../utils/string.hpp"
#include "../language/ast/deserializer.hpp"
#include "../language/symbol/builder.hpp"
module;
export module lsp.manager.symbol;
import tree_sitter;
import spdlog;
import std;
import lsp.protocol;
import lsp.manager.event_bus;
import lsp.manager.events;
import lsp.language.ast;
import lsp.language.symbol;
import lsp.language.semantic;
import lsp.utils.string;
export namespace lsp::manager
{
class Symbol
{
public:
enum class SymbolSource
{
kEditing,
kWorkspace,
kSystem
};
struct IndexedSymbol
{
protocol::DocumentUri uri;
std::string name;
protocol::SymbolKind kind;
SymbolSource source;
language::symbol::SymbolId id;
};
explicit Symbol(EventBus& event_bus);
~Symbol();
void LoadSystemLibrary(const std::string& lib_path);
void LoadWorkspace(const protocol::DocumentUri& workspace_uri);
const language::symbol::SymbolTable* GetSymbolTable(const protocol::DocumentUri& uri) const;
const language::semantic::SemanticModel* GetSemanticModel(const protocol::DocumentUri& uri) const;
std::vector<const language::symbol::SymbolTable*> GetWorkspaceSymbolTables() const;
std::vector<const language::symbol::SymbolTable*> GetSystemSymbolTables() const;
std::vector<IndexedSymbol> QueryIndexedSymbols(protocol::SymbolKind kind, std::optional<SymbolSource> source = std::nullopt) const;
private:
void OnDocumentParsed(const events::DocumentParsed& event);
void OnDocumentReparsed(const events::DocumentReparsed& event);
void OnDocumentClosed(const events::DocumentClosed& event);
struct DocumentAnalysis
{
protocol::DocumentUri uri;
protocol::integer version;
std::unique_ptr<language::ast::Deserializer> deserializer;
std::unique_ptr<language::ast::Program> ast;
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
};
struct StoredSymbolEntry
{
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
};
void RebuildIndex();
void AddTableToIndex(const language::symbol::SymbolTable& table, const protocol::DocumentUri& uri, SymbolSource source);
bool IsTopLevelSymbol(const language::symbol::SymbolTable& table, language::symbol::SymbolId id) const;
std::unordered_map<std::string, StoredSymbolEntry> system_symbols_;
std::unordered_map<std::string, StoredSymbolEntry> workspace_symbols_;
std::unordered_map<protocol::DocumentUri, DocumentAnalysis> editing_symbols_;
std::unordered_map<std::string, std::vector<IndexedSymbol>, utils::IHasher, utils::IEqualTo> index_by_name_;
EventBus& event_bus_;
mutable std::shared_mutex mutex_;
};
}
namespace lsp::manager
{
@ -68,7 +141,11 @@ namespace lsp::manager
if (!path.has_extension())
return false;
std::string ext = path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
std::transform(
ext.begin(),
ext.end(),
ext.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return ext == ".tsf" || ext == ".tsl";
}
@ -510,7 +587,8 @@ namespace lsp::manager
.name = symbol.name(),
.kind = symbol.kind(),
.source = source,
.id = symbol.id() };
.id = symbol.id()
};
auto key = utils::ToLower(symbol.name());
index_by_name_[key].push_back(std::move(item));

View File

@ -1,83 +0,0 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <shared_mutex>
#include <vector>
#include "../protocol/protocol.hpp"
#include "../language/symbol/table.hpp"
#include "../language/ast/deserializer.hpp"
#include "../language/semantic/semantic_model.hpp"
#include "./event_bus.hpp"
#include "./events.hpp"
#include "../utils/string.hpp"
namespace lsp::manager
{
class Symbol
{
public:
enum class SymbolSource
{
kEditing,
kWorkspace,
kSystem
};
struct IndexedSymbol
{
protocol::DocumentUri uri;
std::string name;
protocol::SymbolKind kind;
SymbolSource source;
language::symbol::SymbolId id;
};
explicit Symbol(EventBus& event_bus);
~Symbol();
void LoadSystemLibrary(const std::string& lib_path);
void LoadWorkspace(const protocol::DocumentUri& workspace_uri);
const language::symbol::SymbolTable* GetSymbolTable(const protocol::DocumentUri& uri) const;
const language::semantic::SemanticModel* GetSemanticModel(const protocol::DocumentUri& uri) const;
std::vector<const language::symbol::SymbolTable*> GetWorkspaceSymbolTables() const;
std::vector<const language::symbol::SymbolTable*> GetSystemSymbolTables() const;
std::vector<IndexedSymbol> QueryIndexedSymbols(protocol::SymbolKind kind, std::optional<SymbolSource> source = std::nullopt) const;
private:
void OnDocumentParsed(const events::DocumentParsed& event);
void OnDocumentReparsed(const events::DocumentReparsed& event);
void OnDocumentClosed(const events::DocumentClosed& event);
struct DocumentAnalysis
{
protocol::DocumentUri uri;
protocol::integer version;
std::unique_ptr<language::ast::Deserializer> deserializer;
std::unique_ptr<language::ast::Program> ast;
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
};
struct StoredSymbolEntry
{
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
};
void RebuildIndex();
void AddTableToIndex(const language::symbol::SymbolTable& table, const protocol::DocumentUri& uri, SymbolSource source);
bool IsTopLevelSymbol(const language::symbol::SymbolTable& table, language::symbol::SymbolId id) const;
std::unordered_map<std::string, StoredSymbolEntry> system_symbols_;
std::unordered_map<std::string, StoredSymbolEntry> workspace_symbols_;
std::unordered_map<protocol::DocumentUri, DocumentAnalysis> editing_symbols_;
std::unordered_map<std::string, std::vector<IndexedSymbol>, utils::IHasher, utils::IEqualTo> index_by_name_;
EventBus& event_bus_;
mutable std::shared_mutex mutex_;
};
}

View File

@ -0,0 +1,250 @@
module;
import glaze;
export module lsp.protocol.common.basic_types;
import std;
export namespace lsp::protocol
{
using integer = std::int32_t;
using uinteger = std::uint32_t;
using decimal = double;
using boolean = bool;
using string = std::string;
using URI = string;
using DocumentUri = string;
using ProgressToken = std::variant<string, integer>;
struct LSPAny;
using LSPObject = std::map<string, LSPAny>;
using LSPArray = std::vector<LSPAny>;
struct LSPAny
{
using LSPAnyVariant = std::variant<
LSPObject,
LSPArray,
string,
integer,
uinteger,
decimal,
boolean,
std::nullptr_t>;
LSPAnyVariant value;
LSPAny() : value(std::nullptr_t{}) {}
LSPAny(const LSPAny&) = default;
LSPAny(LSPAny&&) noexcept = default;
LSPAny& operator=(const LSPAny&) = default;
LSPAny& operator=(LSPAny&&) noexcept = default;
template<typename T>
LSPAny(const T& val)
{
*this = val;
}
template<typename T>
LSPAny& operator=(const T& val);
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);
};
template<typename T>
LSPAny& LSPAny::operator=(const T& val)
{
if constexpr (std::is_same_v<T, LSPObject> ||
std::is_same_v<T, LSPArray> ||
std::is_same_v<T, string> ||
std::is_same_v<T, integer> ||
std::is_same_v<T, uinteger> ||
std::is_same_v<T, decimal> ||
std::is_same_v<T, boolean> ||
std::is_same_v<T, std::nullptr_t>)
{
value = val;
}
else if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
if constexpr (std::is_unsigned_v<T>)
{
if (val <= static_cast<std::make_unsigned_t<uinteger>>(std::numeric_limits<uinteger>::max()))
value = static_cast<uinteger>(val);
else
value = static_cast<decimal>(val);
}
else
{
if (val >= std::numeric_limits<integer>::min() && val <= std::numeric_limits<integer>::max())
value = static_cast<integer>(val);
else
value = static_cast<decimal>(val);
}
}
else if constexpr (std::is_floating_point_v<T>)
{
value = static_cast<decimal>(val);
}
else if constexpr (std::is_same_v<T, const char*> ||
(std::is_array_v<T> && std::is_same_v<std::remove_extent_t<T>, char>))
{
value = string(val);
}
else
{
static_assert(std::is_same_v<T, void>, "Unsupported type for LSPAny");
}
return *this;
}
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);
}
// 额外基础类型
struct Position
{
uinteger line;
uinteger character;
};
struct Range
{
Position start;
Position end;
};
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 Location
{
string uri;
Range range;
};
struct Command
{
string title;
string command;
std::optional<std::vector<LSPAny>> arguments;
};
struct TextEdit
{
Range range;
string newText;
};
struct AnnotatedTextEdit
{
Range range;
string newText;
string annotationId;
};
struct SnippetTextEdit
{
Range range;
string snippet;
};
struct ChangeAnnotation
{
string label;
std::optional<string> description;
std::optional<boolean> needsResolve;
};
struct StringValue
{
string kind;
string value;
};
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;
};
}
namespace glz
{
// 为 LSPAny 提供 glaze 支持
template<>
struct meta<lsp::protocol::LSPAny>
{
static constexpr std::string_view name = "LSPAny";
using T = lsp::protocol::LSPAny;
static constexpr auto value = &T::value;
};
}

View File

@ -1,8 +1,14 @@
#pragma once
#include "./basic_types.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.common.message;
import std;
import lsp.protocol.common.basic_types;
export namespace lsp::protocol
{
// LSP Error Codes
enum class ErrorCodes : int
{
ParseError = -32700,
@ -23,42 +29,41 @@ namespace lsp::protocol
LspReservedErrorRangeEnd = -32800
};
struct Message
struct RequestMessage
{
string jsonrpc = "2.0";
};
struct RequestMessage: Message
{
std::variant<integer, string> id;
string method;
std::optional<std::variant<LSPArray, LSPObject>> params;
};
struct ResponseError: Message
{
ErrorCodes 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 id;
string method;
std::optional<LSPAny> params;
};
struct CancelParams
struct ResponseError
{
std::variant<integer, string> id;
string jsonrpc = "2.0";
integer code;
string message;
std::optional<LSPAny> data;
};
}
struct ResponseMessage
{
string jsonrpc = "2.0";
std::optional<string> id;
std::optional<LSPAny> result;
std::optional<ResponseError> error;
};
struct NotificationMessage
{
string jsonrpc = "2.0";
string method;
std::optional<LSPAny> params;
};
// $/cancelRequest notification params
struct CancelParams
{
// The request id to cancel.
std::variant<int, std::string> id; // integer | string
};
}

View File

@ -1,8 +1,18 @@
#pragma once
#include "./basic_types.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.common.registration;
import std;
import lsp.protocol.common.basic_types;
export namespace lsp::protocol
{
struct StaticRegistrationOptions
{
std::optional<string> id;
};
struct Registration
{
string id;
@ -15,16 +25,6 @@ namespace lsp::protocol
std::vector<Registration> registrations;
};
struct StaticRegistrationOptions
{
std::optional<string> id;
};
struct TextDocumentRegistrationOptions
{
std::optional<DocumentSelector> documentSelector;
};
struct Unregistration
{
string id;
@ -33,7 +33,6 @@ namespace lsp::protocol
struct UnregistrationParams
{
std::vector<Unregistration> unregistration;
std::vector<Unregistration> unregistrations;
};
} // namespace lsp::protocol
}

View File

@ -1,315 +0,0 @@
#pragma once
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include <map>
#include <type_traits>
#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序列化
// export type LSPAny = LSPObject | LSPArray | string | integer | uinteger | decimal | boolean | null;
struct LSPAny;
using LSPObject = std::map<string, LSPAny>;
using LSPArray = std::vector<LSPAny>;
struct LSPAny
{
using LSPAnyVariant = std::variant<
LSPObject,
LSPArray,
string,
integer,
uinteger,
decimal,
boolean,
std::nullptr_t>;
LSPAnyVariant value;
// 默认构造函数
LSPAny();
// 拷贝和移动构造函数
LSPAny(const LSPAny& other);
LSPAny(LSPAny&& other) noexcept;
// 容器类型的构造函数
LSPAny(const LSPObject& val);
LSPAny(LSPObject&& val);
LSPAny(const LSPArray& val);
LSPAny(LSPArray&& val);
// 字符串类型的构造函数
LSPAny(const string& val);
LSPAny(string&& val);
LSPAny(const char* val);
// 浮点类型的构造函数 -- decimal
LSPAny(float val);
LSPAny(double val);
LSPAny(long double val);
// 布尔和空值
LSPAny(boolean val);
LSPAny(std::nullptr_t);
// 单个模板处理所有整数类型
template<typename T, typename = std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<std::decay_t<T>, char>>>
LSPAny(T val);
// 赋值操作符
LSPAny& operator=(const LSPAny& other);
LSPAny& operator=(LSPAny&& other) noexcept;
LSPAny& operator=(const LSPObject& val);
LSPAny& operator=(LSPObject&& val);
LSPAny& operator=(const LSPArray& val);
LSPAny& operator=(LSPArray&& val);
LSPAny& operator=(const string& val);
LSPAny& operator=(string&& val);
LSPAny& operator=(const char* 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, typename = std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<std::decay_t<T>, char>>>
LSPAny& operator=(T val);
// 类型检查辅助函数
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;
};
struct StringValue
{
string kind;
string value;
};
struct SnippetTextEdit
{
Range range;
StringValue snippet;
std::optional<ChangeAnnotationIdentifier> annotationId;
};
enum class ApplyKind
{
Replace = 1,
Merge = 2
};
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, typename>
LSPAny::LSPAny(T val)
{
if constexpr (std::is_signed_v<T>)
{
// 有符号整数
if (val >= std::numeric_limits<integer>::min() && val <= std::numeric_limits<integer>::max())
value = static_cast<integer>(val);
else
value = static_cast<decimal>(val); // 超出 int32 范围,转为 decimal可能丢失精度
}
else
{
// 无符号整数
if (val <= std::numeric_limits<uinteger>::max())
value = static_cast<uinteger>(val);
else
value = static_cast<decimal>(val); // 超出 uint32 范围,转为 decimal可能丢失精度
}
}
template<typename T, typename>
LSPAny& LSPAny::operator=(T val)
{
if constexpr (std::is_signed_v<T>)
{
// 有符号整数
if (val >= std::numeric_limits<integer>::min() && val <= std::numeric_limits<integer>::max())
value = static_cast<integer>(val);
else
value = static_cast<decimal>(val);
}
else
{
if (val <= std::numeric_limits<uinteger>::max())
value = static_cast<uinteger>(val);
else
value = static_cast<decimal>(val);
}
return *this;
}
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

@ -1,137 +0,0 @@
#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 LSPObject& val) :
value(val) {}
inline LSPAny::LSPAny(LSPObject&& val) :
value(std::move(val)) {}
inline LSPAny::LSPAny(const LSPArray& val) :
value(val) {}
inline LSPAny::LSPAny(LSPArray&& 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(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 LSPObject& val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(LSPObject&& val)
{
value = std::move(val);
return *this;
}
inline LSPAny& LSPAny::operator=(const LSPArray& val)
{
value = val;
return *this;
}
inline LSPAny& LSPAny::operator=(LSPArray&& 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;
}
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

@ -1,24 +1,29 @@
#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"
module;
namespace lsp::protocol
export module lsp.protocol.initialize.capabilities;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.window.progress;
import lsp.protocol.text_document.diagnostics;
import lsp.protocol.workspace.workspace;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.text_document.completion;
import lsp.protocol.text_document.document_features;
import lsp.protocol.text_document.symbols;
import lsp.protocol.text_document.navigation;
import lsp.protocol.text_document.code_actions;
import lsp.protocol.text_document.formatting;
import lsp.protocol.text_document.rename;
import lsp.protocol.text_document.semantic_tokens;
import lsp.protocol.text_document.inline_features;
import lsp.protocol.text_document.signature_help;
import lsp.protocol.workspace.notebook;
import lsp.protocol.workspace.file_operations;
import lsp.protocol.initialize.configuration;
export namespace lsp::protocol
{
struct TextDocumentClientCapabilities
{

View File

@ -1,10 +1,14 @@
#pragma once
#include "./basic_types.hpp"
#include "./progress.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.initialize.configuration;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.window.progress;
export namespace lsp::protocol
{
// Configuration
struct ConfigurationItem
{
std::optional<URI> scopeUri;
@ -13,7 +17,7 @@ namespace lsp::protocol
struct ConfigurationParams
{
std::vector<ConfigurationParams> items;
std::vector<ConfigurationItem> items;
};
struct DidChangeConfigurationClientCapabilities
@ -26,7 +30,6 @@ namespace lsp::protocol
LSPAny settings;
};
// Command Execution
struct ExecuteCommandClientCapabilities
{
std::optional<boolean> dynamicRegistration;
@ -47,7 +50,6 @@ namespace lsp::protocol
std::optional<std::vector<LSPAny>> arguments;
};
// Message
enum class MessageType
{
Error = 1,
@ -107,5 +109,4 @@ namespace lsp::protocol
MessageType type;
string message;
};
}

View File

@ -0,0 +1,629 @@
module;
import glaze;
export module lsp.protocol;
import std;
// Aggregated LSP protocol module (facade with serialization metadata)
// This module re-exports lsp.protocol.types and all protocol submodules
// Note: transform.* modules are NOT re-exported to avoid circular dependencies
export import lsp.protocol.types;
// Import all protocol submodules for glaze metadata
import lsp.protocol.window.progress;
import lsp.protocol.initialize.configuration;
import lsp.protocol.initialize.capabilities;
import lsp.protocol.workspace.workspace;
import lsp.protocol.workspace.file_operations;
import lsp.protocol.workspace.notebook;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.text_document.completion;
import lsp.protocol.text_document.code_actions;
import lsp.protocol.text_document.diagnostics;
import lsp.protocol.text_document.document_features;
import lsp.protocol.text_document.formatting;
import lsp.protocol.text_document.inline_features;
import lsp.protocol.text_document.navigation;
import lsp.protocol.text_document.rename;
import lsp.protocol.text_document.semantic_tokens;
import lsp.protocol.text_document.signature_help;
import lsp.protocol.text_document.symbols;
// Glaze meta specializations (from legacy protocol.hpp)
namespace glz
{
template<>
struct meta<lsp::protocol::RequestMessage>
{
using T = lsp::protocol::RequestMessage;
static constexpr auto value = glz::object(
&T::jsonrpc,
&T::id,
&T::method,
&T::params);
};
template<>
struct meta<lsp::protocol::ResponseError>
{
using T = lsp::protocol::ResponseError;
static constexpr auto value = glz::object(&T::jsonrpc, &T::code, &T::message, &T::data);
};
template<>
struct meta<lsp::protocol::ResponseMessage>
{
using T = lsp::protocol::ResponseMessage;
static constexpr auto value = glz::object(&T::jsonrpc, &T::id, &T::result, &T::error);
};
template<>
struct meta<lsp::protocol::NotificationMessage>
{
using T = lsp::protocol::NotificationMessage;
static constexpr auto value = glz::object(&T::jsonrpc, &T::method, &T::params);
};
template<>
struct meta<lsp::protocol::CancelParams>
{
using T = lsp::protocol::CancelParams;
static constexpr auto value = glz::object(&T::id);
};
template<>
struct meta<lsp::protocol::AnnotatedTextEdit>
{
using T = lsp::protocol::AnnotatedTextEdit;
static constexpr auto value = glz::object(&T::range, &T::newText, &T::annotationId);
};
template<>
struct meta<lsp::protocol::InitializeParams>
{
using T = lsp::protocol::InitializeParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::processId, &T::clientInfo, &T::locale, &T::rootPath, &T::rootUri, &T::initializationOptions, &T::capabilities, &T::trace, &T::workspaceFolders);
};
template<>
struct meta<lsp::protocol::CodeActionOptions>
{
using T = lsp::protocol::CodeActionOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::codeActionKinds, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::CodeActionRegistrationOptions>
{
using T = lsp::protocol::CodeActionRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::codeActionKinds, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::CodeActionParams>
{
using T = lsp::protocol::CodeActionParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::range, &T::context);
};
template<>
struct meta<lsp::protocol::DocumentColorOptions>
{
using T = lsp::protocol::DocumentColorOptions;
static constexpr auto value = glz::object(&T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::DocumentColorRegistrationOptions>
{
using T = lsp::protocol::DocumentColorRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::DocumentColorParams>
{
using T = lsp::protocol::DocumentColorParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument);
};
template<>
struct meta<lsp::protocol::ColorPresentationParams>
{
using T = lsp::protocol::ColorPresentationParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::color, &T::range);
};
template<>
struct meta<lsp::protocol::CompletionOptions>
{
using T = lsp::protocol::CompletionOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::triggerCharacters, &T::allCommitCharacters, &T::resolveProvider, &T::completionItem);
};
template<>
struct meta<lsp::protocol::CompletionRegistrationOptions>
{
using T = lsp::protocol::CompletionRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::triggerCharacters, &T::allCommitCharacters, &T::resolveProvider, &T::completionItem);
};
template<>
struct meta<lsp::protocol::CompletionParams>
{
using T = lsp::protocol::CompletionParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::position, &T::context);
};
template<>
struct meta<lsp::protocol::ExecuteCommandOptions>
{
using T = lsp::protocol::ExecuteCommandOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::commands);
};
template<>
struct meta<lsp::protocol::ExecuteCommandRegistrationOptions>
{
using T = lsp::protocol::ExecuteCommandRegistrationOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::commands);
};
template<>
struct meta<lsp::protocol::ExecuteCommandParams>
{
using T = lsp::protocol::ExecuteCommandParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::arguments);
};
template<>
struct meta<lsp::protocol::DiagnosticOptions>
{
using T = lsp::protocol::DiagnosticOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::interFileDependencies, &T::workspaceDiagnostics);
};
template<>
struct meta<lsp::protocol::DiagnosticRegistrationOptions>
{
using T = lsp::protocol::DiagnosticRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id, &T::workDoneProgress, &T::interFileDependencies, &T::workspaceDiagnostics);
};
template<>
struct meta<lsp::protocol::DiagnosticParams>
{
using T = lsp::protocol::DiagnosticParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::identifier, &T::previousResultId);
};
template<>
struct meta<lsp::protocol::PublishDiagnosticsClientCapabilities>
{
using T = lsp::protocol::PublishDiagnosticsClientCapabilities;
static constexpr auto value = glz::object(&T::dynamicRegistration, &T::relatedDocumentSupport, &T::relatedInformation, &T::tagSupport, &T::codeDescriptionSupport, &T::markupMessageSupport, &T::dataSupport, &T::versionSupport);
};
template<>
struct meta<lsp::protocol::RelatedFullDocumentDiagnosticReport>
{
using T = lsp::protocol::RelatedFullDocumentDiagnosticReport;
static constexpr auto value = glz::object(&T::kind, &T::resultId, &T::items, &T::relatedDocuments);
};
template<>
struct meta<lsp::protocol::RelatedUnchangedDocumentDiagnosticReport>
{
using T = lsp::protocol::RelatedUnchangedDocumentDiagnosticReport;
static constexpr auto value = glz::object(&T::kind, &T::resultId, &T::relatedDocuments);
};
template<>
struct meta<lsp::protocol::WorkspaceDiagnosticParams>
{
using T = lsp::protocol::WorkspaceDiagnosticParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::identifier, &T::previousResultIds);
};
template<>
struct meta<lsp::protocol::WorkspaceFullDocumentDiagnosticReport>
{
using T = lsp::protocol::WorkspaceFullDocumentDiagnosticReport;
static constexpr auto value = glz::object(&T::kind, &T::resultId, &T::items, &T::uri, &T::version);
};
template<>
struct meta<lsp::protocol::WorkspaceUnchangedDocumentDiagnosticReport>
{
using T = lsp::protocol::WorkspaceUnchangedDocumentDiagnosticReport;
static constexpr auto value = glz::object(&T::kind, &T::resultId, &T::uri, &T::version);
};
template<>
struct meta<lsp::protocol::DocumentHighlightOptions>
{
using T = lsp::protocol::DocumentHighlightOptions;
static constexpr auto value = glz::object(&T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::DocumentHighlightRegistrationOptions>
{
using T = lsp::protocol::DocumentHighlightRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::DocumentHighlightParams>
{
using T = lsp::protocol::DocumentHighlightParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::position);
};
template<>
struct meta<lsp::protocol::DocumentLinkOptions>
{
using T = lsp::protocol::DocumentLinkOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::DocumentLinkRegistrationOptions>
{
using T = lsp::protocol::DocumentLinkRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::DocumentLinkParams>
{
using T = lsp::protocol::DocumentLinkParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument);
};
template<>
struct meta<lsp::protocol::HoverOptions>
{
using T = lsp::protocol::HoverOptions;
static constexpr auto value = glz::object(&T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::HoverRegistrationOptions>
{
using T = lsp::protocol::HoverRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::HoverParams>
{
using T = lsp::protocol::HoverParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::textDocument, &T::position);
};
template<>
struct meta<lsp::protocol::CodeLensOptions>
{
using T = lsp::protocol::CodeLensOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::CodeLensRegistrationOptions>
{
using T = lsp::protocol::CodeLensRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::CodeLensParams>
{
using T = lsp::protocol::CodeLensParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument);
};
template<>
struct meta<lsp::protocol::FoldingRangeOptions>
{
using T = lsp::protocol::FoldingRangeOptions;
static constexpr auto value = glz::object(&T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::FoldingRangeRegistrationOptions>
{
using T = lsp::protocol::FoldingRangeRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::FoldingRangeParams>
{
using T = lsp::protocol::FoldingRangeParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument);
};
template<>
struct meta<lsp::protocol::SelectionRangeOptions>
{
using T = lsp::protocol::SelectionRangeOptions;
static constexpr auto value = glz::object(&T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::SelectionRangeRegistrationOptions>
{
using T = lsp::protocol::SelectionRangeRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::SelectionRangeParams>
{
using T = lsp::protocol::SelectionRangeParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::textDocument, &T::positions);
};
template<>
struct meta<lsp::protocol::WorkspaceSymbolOptions>
{
using T = lsp::protocol::WorkspaceSymbolOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::InlayHintOptions>
{
using T = lsp::protocol::InlayHintOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::resolveProvider);
};
template<>
struct meta<lsp::protocol::InlayHintRegistrationOptions>
{
using T = lsp::protocol::InlayHintRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::resolveProvider, &T::id);
};
template<>
struct meta<lsp::protocol::InlineValueRegistrationOptions>
{
using T = lsp::protocol::InlineValueRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::TypeHierarchyRegistrationOptions>
{
using T = lsp::protocol::TypeHierarchyRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::MonikerRegistrationOptions>
{
using T = lsp::protocol::MonikerRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress);
};
template<>
struct meta<lsp::protocol::LinkedEditingRangeRegistrationOptions>
{
using T = lsp::protocol::LinkedEditingRangeRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::SemanticTokensOptions>
{
using T = lsp::protocol::SemanticTokensOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::legend, &T::range, &T::full);
};
template<>
struct meta<lsp::protocol::SemanticTokensRegistrationOptions>
{
using T = lsp::protocol::SemanticTokensRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::legend, &T::range, &T::full, &T::id);
};
template<>
struct meta<lsp::protocol::CallHierarchyRegistrationOptions>
{
using T = lsp::protocol::CallHierarchyRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::RenameOptions>
{
using T = lsp::protocol::RenameOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::prepareProvider);
};
template<>
struct meta<lsp::protocol::RenameRegistrationOptions>
{
using T = lsp::protocol::RenameRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::prepareProvider);
};
template<>
struct meta<lsp::protocol::RenameParams>
{
using T = lsp::protocol::RenameParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::newName);
};
template<>
struct meta<lsp::protocol::PrepareRenameParams>
{
using T = lsp::protocol::PrepareRenameParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken);
};
template<>
struct meta<lsp::protocol::LinkedEditingRangeParams>
{
using T = lsp::protocol::LinkedEditingRangeParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::partialResultToken);
};
template<>
struct meta<lsp::protocol::DocumentOnTypeFormattingOptions>
{
using T = lsp::protocol::DocumentOnTypeFormattingOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::firstTriggerCharacter, &T::moreTriggerCharacter);
};
template<>
struct meta<lsp::protocol::DocumentRangeFormattingOptions>
{
using T = lsp::protocol::DocumentRangeFormattingOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::rangesSupport);
};
template<>
struct meta<lsp::protocol::DocumentSymbolOptions>
{
using T = lsp::protocol::DocumentSymbolOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::label);
};
template<>
struct meta<lsp::protocol::ImplementationRegistrationOptions>
{
using T = lsp::protocol::ImplementationRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::SignatureHelpOptions>
{
using T = lsp::protocol::SignatureHelpOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::triggerCharacters, &T::retriggerCharacters);
};
template<>
struct meta<lsp::protocol::DeclarationRegistrationOptions>
{
using T = lsp::protocol::DeclarationRegistrationOptions;
static constexpr auto value = glz::object(&T::workDoneProgress, &T::documentSelector, &T::id);
};
template<>
struct meta<lsp::protocol::TypeDefinitionRegistrationOptions>
{
using T = lsp::protocol::TypeDefinitionRegistrationOptions;
static constexpr auto value = glz::object(&T::documentSelector, &T::workDoneProgress, &T::id);
};
template<>
struct meta<lsp::protocol::DeclarationParams>
{
using T = lsp::protocol::DeclarationParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::partialResultToken);
};
template<>
struct meta<lsp::protocol::DefinitionParams>
{
using T = lsp::protocol::DefinitionParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::partialResultToken);
};
template<>
struct meta<lsp::protocol::TypeDefinitionParams>
{
using T = lsp::protocol::TypeDefinitionParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken);
};
template<>
struct meta<lsp::protocol::ImplementationParams>
{
using T = lsp::protocol::ImplementationParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken);
};
template<>
struct meta<lsp::protocol::ReferenceContext>
{
using T = lsp::protocol::ReferenceContext;
static constexpr auto value = glz::object(&T::includeDeclaration);
};
template<>
struct meta<lsp::protocol::ReferenceParams>
{
using T = lsp::protocol::ReferenceParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken, &T::partialResultToken, &T::context);
};
template<>
struct meta<lsp::protocol::CallHierarchyParams>
{
using T = lsp::protocol::CallHierarchyParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken);
};
template<>
struct meta<lsp::protocol::CallHierarchyIncomingCallsParams>
{
using T = lsp::protocol::CallHierarchyIncomingCallsParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::item);
};
template<>
struct meta<lsp::protocol::CallHierarchyOutgoingCallsParams>
{
using T = lsp::protocol::CallHierarchyOutgoingCallsParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::item);
};
template<>
struct meta<lsp::protocol::TypeHierarchyParams>
{
using T = lsp::protocol::TypeHierarchyParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken);
};
template<>
struct meta<lsp::protocol::TypeHierarchyPrepareParams>
{
using T = lsp::protocol::TypeHierarchyPrepareParams;
static constexpr auto value = glz::object(&T::textDocument, &T::position, &T::workDoneToken);
};
template<>
struct meta<lsp::protocol::TypeHierarchySupertypesParams>
{
using T = lsp::protocol::TypeHierarchySupertypesParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::item);
};
template<>
struct meta<lsp::protocol::TypeHierarchySubtypesParams>
{
using T = lsp::protocol::TypeHierarchySubtypesParams;
static constexpr auto value = glz::object(&T::workDoneToken, &T::partialResultToken, &T::item);
};
template<>
struct meta<lsp::protocol::NotebookDocumentSyncRegistrationOptions>
{
using T = lsp::protocol::NotebookDocumentSyncRegistrationOptions;
static constexpr auto value = glz::object(&T::notebookSelector, &T::save, &T::id);
};
template<>
struct meta<lsp::protocol::VersionedTextDocumentIdentifier>
{
using T = lsp::protocol::VersionedTextDocumentIdentifier;
static constexpr auto value = glz::object(&T::uri, &T::version);
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,17 @@
#pragma once
#include "./basic_types.hpp"
#include "./document_sync.hpp"
#include "./progress.hpp"
#include "./workspace.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.text_document.code_actions;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.window.progress;
import lsp.protocol.workspace.workspace;
import lsp.protocol.text_document.diagnostics;
import lsp.protocol.common.registration;
export namespace lsp::protocol
{
using CodeActionKind = std::string_view;
namespace CodeActionKindLiterals

View File

@ -1,9 +1,15 @@
#pragma once
#include "./basic_types.hpp"
#include "./document_sync.hpp"
#include "./progress.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.text_document.completion;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.window.progress;
import lsp.protocol.common.registration;
export namespace lsp::protocol
{
enum class CompletionItemTag
{

View File

@ -1,9 +1,15 @@
#pragma once
#include "./basic_types.hpp"
#include "./document_sync.hpp"
#include "./progress.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.text_document.diagnostics;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.window.progress;
import lsp.protocol.common.registration;
export namespace lsp::protocol
{
enum class DiagnosticSeverity
{

View File

@ -1,9 +1,15 @@
#pragma once
#include "./basic_types.hpp"
#include "./document_sync.hpp"
#include "./progress.hpp"
module;
namespace lsp::protocol
export module lsp.protocol.text_document.document_features;
import std;
import lsp.protocol.common.basic_types;
import lsp.protocol.text_document.document_sync;
import lsp.protocol.window.progress;
import lsp.protocol.common.registration;
export namespace lsp::protocol
{
// Document Highlight
struct DocumentHighlightClientCapabilities
@ -183,7 +189,6 @@ namespace lsp::protocol
std::optional<boolean> refreshSupport;
};
// Sekection Range
struct SelectionRangeClientCapabilities
{

Some files were not shown because too many files have changed in this diff Show More