tsl-devkit/lsp-server/test/test_provider/completion_test.cppm

813 lines
34 KiB
C++

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.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 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<protocol::ResponseMessage>(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<std::uint32_t>(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<protocol::CompletionItem> 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()<protocol::CompletionList>(response.result.value());
return list.items;
}
bool HasLabel(const std::vector<protocol::CompletionItem>& items, const std::string& label)
{
return std::any_of(items.begin(), items.end(), [&](const auto& item) {
return item.label == label;
});
}
std::optional<protocol::CompletionItem> FindItem(const std::vector<protocol::CompletionItem>& 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).<empty>", TestClassMethodEmptyPrefixCompletion);
runner.addTest("completion new Widget", TestNewCompletion);
runner.addTest("completion new <empty>", TestNewEmptyPrefixCompletion);
runner.addTest("completion new unit(MainUnit).Widget", TestUnitScopedNewCompletion);
runner.addTest("completion createobject", TestCreateObjectCompletion);
runner.addTest("completion createobject <empty>", TestCreateObjectEmptyPrefixCompletion);
runner.addTest("completion createobject quoted", TestCreateObjectQuotedCompletion);
runner.addTest("completion createobject quoted <empty>", 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(<empty>)", 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(<empty>)", 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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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()<protocol::CompletionItem>(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;
}
}