module; export module lsp.test.provider.definitions; import std; import lsp.test.framework; import lsp.provider.text_document.definition; import lsp.core.dispacther; import lsp.manager.manager_hub; import lsp.scheduler.async_executor; import lsp.protocol; import lsp.codec.facade; import lsp.test.provider.fixtures; export namespace lsp::test::provider { class DefinitionTests { public: static void Register(TestRunner& runner); private: static TestResult TestDefinitionResolvesInDocument(); static TestResult TestDefinitionResolvesInWorkspaceIndex(); static TestResult TestDefinitionResolvesInSystemIndex(); }; } namespace lsp::test::provider { namespace { struct ProviderEnv { scheduler::AsyncExecutor scheduler{ 1 }; manager::ManagerHub hub{}; core::ExecutionContext context; ProviderEnv() : context([](core::ServerLifecycleEvent) {}, scheduler, hub) { hub.Initialize(); } }; protocol::ResponseMessage ParseResponse(const std::string& json) { auto parsed = codec::Deserialize(json); if (!parsed) { throw std::runtime_error("Failed to deserialize response"); } return *parsed; } protocol::Position FindPosition(const std::string& content, const std::string& marker) { auto pos = content.find(marker); assertTrue(pos != std::string::npos, "Marker not found in fixture"); protocol::Position result{}; result.line = 0; result.character = 0; for (std::size_t i = 0; i < pos; ++i) { if (content[i] == '\n') { result.line++; result.character = 0; } else { result.character++; } } return result; } void OpenDocument(manager::ManagerHub& hub, const std::string& uri, const std::string& text, int version) { protocol::DidOpenTextDocumentParams open_params; open_params.textDocument.uri = uri; open_params.textDocument.languageId = "tsl"; open_params.textDocument.version = version; open_params.textDocument.text = text; hub.documents().OpenDocument(open_params); } protocol::Location ExtractLocation(const protocol::ResponseMessage& response) { if (!response.result.has_value()) { throw std::runtime_error("Expected definition result"); } return codec::FromLSPAny.template operator()(response.result.value()); } bool LocationMatchesLine(const protocol::Location& location, std::uint32_t line) { return location.range.start.line == line; } } void DefinitionTests::Register(TestRunner& runner) { runner.addTest("definition resolves in document", TestDefinitionResolvesInDocument); runner.addTest("definition resolves in workspace index", TestDefinitionResolvesInWorkspaceIndex); runner.addTest("definition resolves in system index", TestDefinitionResolvesInSystemIndex); } TestResult DefinitionTests::TestDefinitionResolvesInDocument() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); protocol::DefinitionParams params; params.textDocument.uri = uri; params.position = FindPosition(content, "UnitFunc(1);"); protocol::RequestMessage request; request.id = "1"; request.method = "textDocument/definition"; request.params = codec::ToLSPAny(params); ::lsp::provider::text_document::Definition handler; auto json = handler.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto location = ExtractLocation(response); auto def_pos = FindPosition(content, "function UnitFunc"); assertTrue(LocationMatchesLine(location, def_pos.line), "Definition should resolve in document"); return result; } TestResult DefinitionTests::TestDefinitionResolvesInWorkspaceIndex() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto workspace_root = FixturePath("workspace"); env.hub.symbols().LoadWorkspace(ToUri(workspace_root)); auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); protocol::DefinitionParams params; params.textDocument.uri = uri; params.position = FindPosition(content, "WorkspaceFunc()"); protocol::RequestMessage request; request.id = "2"; request.method = "textDocument/definition"; request.params = codec::ToLSPAny(params); ::lsp::provider::text_document::Definition handler; auto json = handler.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto location = ExtractLocation(response); auto expected = ReadTextFile(FixturePath("workspace/workspace_script.tsl")); auto expected_pos = FindPosition(expected, "function WorkspaceFunc"); assertTrue(LocationMatchesLine(location, expected_pos.line), "Definition should resolve in workspace index"); return result; } TestResult DefinitionTests::TestDefinitionResolvesInSystemIndex() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto system_root = FixturePath("system"); env.hub.symbols().LoadSystemLibrary(system_root.string()); auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); protocol::DefinitionParams params; params.textDocument.uri = uri; params.position = FindPosition(content, "SystemUnit"); protocol::RequestMessage request; request.id = "3"; request.method = "textDocument/definition"; request.params = codec::ToLSPAny(params); ::lsp::provider::text_document::Definition handler; auto json = handler.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto location = ExtractLocation(response); auto expected = ReadTextFile(FixturePath("system/SystemUnit.tsf")); auto expected_pos = FindPosition(expected, "unit SystemUnit"); assertTrue(LocationMatchesLine(location, expected_pos.line), "Definition should resolve in system index"); return result; } }