module; export module lsp.test.provider.completion; import std; import lsp.test.framework; import lsp.provider.text_document.completion; import lsp.provider.completion_item.resolve; import lsp.core.dispatcher; 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 CompletionTests { public: static void Register(TestRunner& runner); private: static TestResult TestClassMethodCompletion(); static TestResult TestClassMethodEmptyPrefixCompletion(); static TestResult TestNewCompletion(); static TestResult TestNewEmptyPrefixCompletion(); static TestResult TestUnitScopedNewCompletion(); static TestResult TestCreateObjectCompletion(); static TestResult TestCreateObjectEmptyPrefixCompletion(); static TestResult TestCreateObjectQuotedCompletion(); static TestResult TestCreateObjectQuotedEmptyPrefixCompletion(); static TestResult TestCreateObjectSingleQuoteCompletion(); static TestResult TestCreateObjectQualifiedCompletion(); static TestResult TestCreateObjectQuotedQualifiedCompletion(); static TestResult TestCreateObjectUnitCallQualifiedCompletion(); static TestResult TestUnitContextCompletion(); static TestResult TestUnitContextEmptyPrefixCompletion(); static TestResult TestUnitMemberCompletion(); static TestResult TestUnitMemberDotCompletion(); static TestResult TestObjectMemberCompletion(); static TestResult TestObjectMemberQualifiedSelfUnitCompletion(); static TestResult TestObjectMemberQualifiedTypeCompletion(); static TestResult TestFunctionCompletion(); static TestResult TestKeywordCompletion(); static TestResult TestClassContextCompletion(); static TestResult TestClassContextEmptyPrefixCompletion(); static TestResult TestUnitScopedNewAliasCompletion(); static TestResult TestCompletionResolveNewSnippet(); static TestResult TestCompletionResolveCreateObjectSnippet(); static TestResult TestCompletionResolveCreateObjectUnquotedSnippet(); static TestResult TestCompletionResolveCreateObjectSingleQuoteSnippet(); static TestResult TestCompletionResolveFunctionSnippet(); static TestResult TestCompletionResolveUnitMemberFunctionSnippet(); static TestResult TestCompletionResolveInstanceMethodSnippet(); static TestResult TestCompletionResolveStaticMethodSnippet(); }; } 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++; } } result.character += static_cast(marker.size()); 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); } std::vector RequestCompletion(ProviderEnv& env, const std::string& uri, const std::string& content, const std::string& marker) { protocol::CompletionParams params; params.textDocument.uri = uri; params.position = FindPosition(content, marker); protocol::RequestMessage request; request.id = "c1"; request.method = "textDocument/completion"; request.params = codec::ToLSPAny(params); ::lsp::provider::text_document::Completion handler; auto json = handler.ProvideResponse(request, env.context); auto response = ParseResponse(json); if (!response.result.has_value()) { return {}; } auto list = codec::FromLSPAny.template operator()(response.result.value()); return list.items; } bool HasLabel(const std::vector& items, const std::string& label) { return std::any_of(items.begin(), items.end(), [&](const auto& item) { return item.label == label; }); } std::optional FindItem(const std::vector& items, const std::string& label) { auto it = std::find_if(items.begin(), items.end(), [&](const auto& item) { return item.label == label; }); if (it == items.end()) { return std::nullopt; } return *it; } } void CompletionTests::Register(TestRunner& runner) { runner.addTest("completion class(Widget).", TestClassMethodCompletion); runner.addTest("completion class(Widget).", TestClassMethodEmptyPrefixCompletion); runner.addTest("completion new Widget", TestNewCompletion); runner.addTest("completion new ", TestNewEmptyPrefixCompletion); runner.addTest("completion new unit(MainUnit).Widget", TestUnitScopedNewCompletion); runner.addTest("completion createobject", TestCreateObjectCompletion); runner.addTest("completion createobject ", TestCreateObjectEmptyPrefixCompletion); runner.addTest("completion createobject quoted", TestCreateObjectQuotedCompletion); runner.addTest("completion createobject quoted ", TestCreateObjectQuotedEmptyPrefixCompletion); runner.addTest("completion createobject single quote", TestCreateObjectSingleQuoteCompletion); runner.addTest("completion createobject qualified", TestCreateObjectQualifiedCompletion); runner.addTest("completion createobject qualified quoted", TestCreateObjectQuotedQualifiedCompletion); runner.addTest("completion createobject qualified unit()", TestCreateObjectUnitCallQualifiedCompletion); runner.addTest("completion unit(", TestUnitContextCompletion); runner.addTest("completion unit()", TestUnitContextEmptyPrefixCompletion); runner.addTest("completion unit member", TestUnitMemberCompletion); runner.addTest("completion unit member dot", TestUnitMemberDotCompletion); runner.addTest("completion object member", TestObjectMemberCompletion); runner.addTest("completion object member qualified self unit", TestObjectMemberQualifiedSelfUnitCompletion); runner.addTest("completion object member qualified type", TestObjectMemberQualifiedTypeCompletion); runner.addTest("completion function prefix", TestFunctionCompletion); runner.addTest("completion keyword prefix", TestKeywordCompletion); runner.addTest("completion class(", TestClassContextCompletion); runner.addTest("completion class()", TestClassContextEmptyPrefixCompletion); runner.addTest("completion new alias", TestUnitScopedNewAliasCompletion); runner.addTest("completion resolve new snippet", TestCompletionResolveNewSnippet); runner.addTest("completion resolve createobject snippet", TestCompletionResolveCreateObjectSnippet); runner.addTest("completion resolve createobject unquoted snippet", TestCompletionResolveCreateObjectUnquotedSnippet); runner.addTest("completion resolve createobject single quote snippet", TestCompletionResolveCreateObjectSingleQuoteSnippet); runner.addTest("completion resolve function snippet", TestCompletionResolveFunctionSnippet); runner.addTest("completion resolve unit member function snippet", TestCompletionResolveUnitMemberFunctionSnippet); runner.addTest("completion resolve instance method snippet", TestCompletionResolveInstanceMethodSnippet); runner.addTest("completion resolve static method snippet", TestCompletionResolveStaticMethodSnippet); } TestResult CompletionTests::TestClassMethodCompletion() { 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); auto items = RequestCompletion(env, uri, content, "class(Widget).Sta"); assertTrue(HasLabel(items, "StaticFoo"), "class() should suggest static methods"); return result; } TestResult CompletionTests::TestClassMethodEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, "class(Widget)."); assertTrue(HasLabel(items, "StaticFoo"), "class() should suggest static methods with empty prefix"); return result; } TestResult CompletionTests::TestNewCompletion() { 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); auto items = RequestCompletion(env, uri, content, "new Wid"); assertTrue(HasLabel(items, "Widget"), "new context should suggest classes"); return result; } TestResult CompletionTests::TestNewEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, "obj := new "); assertTrue(HasLabel(items, "Widget"), "new context should suggest classes with empty prefix"); return result; } TestResult CompletionTests::TestUnitScopedNewCompletion() { 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); auto items = RequestCompletion(env, uri, content, "new unit(MainUnit).Wid"); assertTrue(HasLabel(items, "Widget"), "unit scoped new should suggest unit classes"); return result; } TestResult CompletionTests::TestCreateObjectCompletion() { 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); auto items = RequestCompletion(env, uri, content, "createobject(Wid"); assertTrue(HasLabel(items, "Widget"), "createobject should suggest classes"); return result; } TestResult CompletionTests::TestCreateObjectEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, "aliased := createobject("); assertTrue(HasLabel(items, "Widget"), "createobject should suggest classes with empty prefix"); return result; } TestResult CompletionTests::TestCreateObjectQuotedCompletion() { 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); auto items = RequestCompletion(env, uri, content, "createobject(\"Wid"); assertTrue(HasLabel(items, "Widget"), "quoted createobject should suggest classes"); return result; } TestResult CompletionTests::TestCreateObjectQuotedEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, "aliased := createobject(\""); assertTrue(HasLabel(items, "Widget"), "quoted createobject should suggest classes with empty prefix"); return result; } TestResult CompletionTests::TestCreateObjectSingleQuoteCompletion() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto replacement_pos = content.find("createobject(\"Wid\");"); assertTrue(replacement_pos != std::string::npos, "Fixture should include createobject(\"Wid\");"); content.replace(replacement_pos, std::string_view("createobject(\"Wid\");").size(), "createobject('Wid');"); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); auto items = RequestCompletion(env, uri, content, "createobject('Wid"); assertTrue(HasLabel(items, "Widget"), "single-quote createobject should suggest classes"); return result; } TestResult CompletionTests::TestCreateObjectQualifiedCompletion() { 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); auto items = RequestCompletion(env, uri, content, "createobject(MainUnit.Wid"); assertTrue(HasLabel(items, "Widget"), "qualified createobject should suggest classes"); return result; } TestResult CompletionTests::TestCreateObjectQuotedQualifiedCompletion() { 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); auto items = RequestCompletion(env, uri, content, "createobject(\"MainUnit."); assertTrue(HasLabel(items, "Widget"), "qualified quoted createobject should suggest unit classes"); return result; } TestResult CompletionTests::TestCreateObjectUnitCallQualifiedCompletion() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto replacement_pos = content.find("createobject(MainUnit.Wid);"); assertTrue(replacement_pos != std::string::npos, "Fixture should include createobject(MainUnit.Wid);"); content.replace(replacement_pos, std::string_view("createobject(MainUnit.Wid);").size(), "createobject(unit(MainUnit).Wid);"); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); auto items = RequestCompletion(env, uri, content, "createobject(unit(MainUnit).Wid"); assertTrue(HasLabel(items, "Widget"), "unit() qualified createobject should suggest unit classes"); return result; } TestResult CompletionTests::TestUnitContextCompletion() { 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); auto items = RequestCompletion(env, uri, content, "unit(Main"); assertTrue(HasLabel(items, "MainUnit"), "unit context should list visible units"); return result; } TestResult CompletionTests::TestUnitContextEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, " unit("); assertTrue(HasLabel(items, "MainUnit"), "unit context should list visible units with empty prefix"); return result; } TestResult CompletionTests::TestUnitMemberCompletion() { 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); auto items = RequestCompletion(env, uri, content, "unit(MainUnit).Uni"); assertTrue(HasLabel(items, "UnitFunc"), "unit member completion should list functions"); return result; } TestResult CompletionTests::TestUnitMemberDotCompletion() { 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); auto items = RequestCompletion(env, uri, content, "MainUnit.Uni"); assertTrue(HasLabel(items, "UnitFunc"), "unit member dot completion should list functions"); return result; } TestResult CompletionTests::TestObjectMemberCompletion() { 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); auto items = RequestCompletion(env, uri, content, "obj.Fo"); assertTrue(HasLabel(items, "Foo"), "object member completion should list instance methods"); return result; } TestResult CompletionTests::TestObjectMemberQualifiedSelfUnitCompletion() { 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); auto items = RequestCompletion(env, uri, content, "aliased.Fo"); assertTrue(HasLabel(items, "Foo"), "qualified self unit type should resolve instance members"); return result; } TestResult CompletionTests::TestObjectMemberQualifiedTypeCompletion() { TestResult result{ "", true, "ok" }; ProviderEnv env; env.hub.symbols().LoadWorkspace(ToUri(FixturePath("workspace"))); auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); auto items = RequestCompletion(env, uri, content, "unit_inst.Wor"); assertTrue(HasLabel(items, "Work"), "qualified type should resolve workspace class members"); return result; } TestResult CompletionTests::TestFunctionCompletion() { 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); auto items = RequestCompletion(env, uri, content, "UnitF"); assertTrue(HasLabel(items, "UnitFunc"), "function prefix should return UnitFunc"); return result; } TestResult CompletionTests::TestKeywordCompletion() { 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); auto items = RequestCompletion(env, uri, content, "fun"); assertTrue(HasLabel(items, "function"), "keyword completion should include 'function'"); return result; } TestResult CompletionTests::TestClassContextCompletion() { 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); auto items = RequestCompletion(env, uri, content, "class(Wid"); assertTrue(HasLabel(items, "Widget"), "class context should suggest classes with static methods"); return result; } TestResult CompletionTests::TestClassContextEmptyPrefixCompletion() { 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); auto items = RequestCompletion(env, uri, content, "class("); assertTrue(HasLabel(items, "Widget"), "class context should suggest classes with static methods"); assertFalse(HasLabel(items, "Plain"), "class context should not suggest classes without static methods"); return result; } TestResult CompletionTests::TestUnitScopedNewAliasCompletion() { 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); auto items = RequestCompletion(env, uri, content, "new MainUnit.Wid"); assertTrue(HasLabel(items, "Widget"), "alias scoped new should suggest classes"); return result; } TestResult CompletionTests::TestCompletionResolveNewSnippet() { 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); auto items = RequestCompletion(env, uri, content, "new Wid"); auto item = FindItem(items, "Widget"); assertTrue(item.has_value(), "Expected Widget completion"); protocol::RequestMessage request; request.id = "r1"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved item should have insertText"); assertTrue(resolved.insertText.value().find("Widget(") == 0, "Resolved new snippet should start with Widget("); assertTrue(resolved.insertText.value().contains("${1:a}"), "Resolved new snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveCreateObjectSnippet() { 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); auto items = RequestCompletion(env, uri, content, "createobject(\"Wid"); auto item = FindItem(items, "Widget"); assertTrue(item.has_value(), "Expected Widget completion"); protocol::RequestMessage request; request.id = "r2"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved item should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("Widget\""), "Resolved createobject snippet should close quote but not add opening quote"); assertTrue(snippet.contains("${1:a}"), "Resolved createobject snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveCreateObjectUnquotedSnippet() { 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); auto items = RequestCompletion(env, uri, content, "createobject(Wid"); auto item = FindItem(items, "Widget"); assertTrue(item.has_value(), "Expected Widget completion"); protocol::RequestMessage request; request.id = "r3"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved item should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("\"Widget\""), "Resolved createobject snippet should add opening quote for unquoted call"); assertTrue(snippet.contains("${1:a}"), "Resolved createobject snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveCreateObjectSingleQuoteSnippet() { TestResult result{ "", true, "ok" }; ProviderEnv env; auto path = FixturePath("main_unit.tsf"); auto content = ReadTextFile(path); auto replacement_pos = content.find("createobject(\"Wid\");"); assertTrue(replacement_pos != std::string::npos, "Fixture should include createobject(\"Wid\");"); content.replace(replacement_pos, std::string_view("createobject(\"Wid\");").size(), "createobject('Wid');"); auto uri = ToUri(path); OpenDocument(env.hub, uri, content, 1); auto items = RequestCompletion(env, uri, content, "createobject('Wid"); auto item = FindItem(items, "Widget"); assertTrue(item.has_value(), "Expected Widget completion"); protocol::RequestMessage request; request.id = "r4"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved item should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("Widget'"), "Resolved createobject snippet should close single quote but not add opening quote"); assertTrue(snippet.contains("${1:a}"), "Resolved createobject snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveFunctionSnippet() { 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); auto items = RequestCompletion(env, uri, content, "UnitF"); auto item = FindItem(items, "UnitFunc"); assertTrue(item.has_value(), "Expected UnitFunc completion"); protocol::RequestMessage request; request.id = "r5"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved function item should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("UnitFunc("), "Resolved function snippet should start with UnitFunc("); assertTrue(snippet.contains("${1:a}"), "Resolved function snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveUnitMemberFunctionSnippet() { 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); auto items = RequestCompletion(env, uri, content, "MainUnit.Uni"); auto item = FindItem(items, "UnitFunc"); assertTrue(item.has_value(), "Expected UnitFunc completion from unit member context"); protocol::RequestMessage request; request.id = "r6"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved unit member function should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("UnitFunc("), "Resolved unit member function snippet should start with UnitFunc("); assertTrue(snippet.contains("${1:a}"), "Resolved unit member function snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveInstanceMethodSnippet() { 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); auto items = RequestCompletion(env, uri, content, "obj.Fo"); auto item = FindItem(items, "Foo"); assertTrue(item.has_value(), "Expected Foo completion from object member context"); protocol::RequestMessage request; request.id = "r7"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved method item should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("Foo("), "Resolved method snippet should start with Foo("); assertTrue(snippet.contains("${1:x}"), "Resolved method snippet should include param placeholders"); return result; } TestResult CompletionTests::TestCompletionResolveStaticMethodSnippet() { 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); auto items = RequestCompletion(env, uri, content, "class(Widget).Sta"); auto item = FindItem(items, "StaticFoo"); assertTrue(item.has_value(), "Expected StaticFoo completion from class method context"); protocol::RequestMessage request; request.id = "r8"; request.method = "completionItem/resolve"; request.params = codec::ToLSPAny(*item); ::lsp::provider::completion_item::Resolve resolver; auto json = resolver.ProvideResponse(request, env.context); auto response = ParseResponse(json); auto resolved = codec::FromLSPAny.template operator()(response.result.value()); assertTrue(resolved.insertText.has_value(), "Resolved static method should have insertText"); auto snippet = resolved.insertText.value(); assertTrue(snippet.starts_with("StaticFoo("), "Resolved static method snippet should start with StaticFoo("); assertTrue(snippet.contains("${1:y}"), "Resolved static method snippet should include param placeholders"); return result; } }