♻️ 使用`module`重构所有代码
This commit is contained in:
parent
549f1d1b0a
commit
f7d5a74615
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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=[]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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);
|
||||
// 通知没有响应,所以只记录日志
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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"}})";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
|
||||
// 设置 Session(Provider 通过 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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 管理资源
|
||||
- 收集错误而非立即中断
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
module;
|
||||
|
||||
export module lsp.language.ast;
|
||||
|
||||
// 导出接口分区
|
||||
export import :types;
|
||||
export import :deserializer;
|
||||
export import :ts_utils;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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, ""};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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, "" };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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]))
|
||||
{
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,10 @@
|
|||
#include "./coordinator.hpp"
|
||||
module;
|
||||
|
||||
export module lsp.language.symbol:index.coordinator;
|
||||
|
||||
import std;
|
||||
|
||||
import :types;
|
||||
|
||||
namespace lsp::language::symbol::index
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,4 +1,10 @@
|
|||
#include "./table.hpp"
|
||||
module;
|
||||
|
||||
import std;
|
||||
|
||||
module lsp.language.symbol:internal.table;
|
||||
|
||||
import :types;
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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[])
|
||||
{
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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++;
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
Loading…
Reference in New Issue