Skip to content

Commit

Permalink
Support partial public registry completions
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Nov 21, 2024
1 parent cd32858 commit 9f481ee
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 52 deletions.
81 changes: 49 additions & 32 deletions src/Bicep.LangServer.IntegrationTests/CompletionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4128,19 +4128,9 @@ public async Task ModuleRegistryReferenceCompletions_GetCompletionsForFolderInsi
[DataRow("using 'br/public:|", BicepSourceFileKind.ParamsFile)]
public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string inputWithCursors, BicepSourceFileKind kind)
{
var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
var (text, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|');

var fileName = kind switch
{
BicepSourceFileKind.BicepFile => "main.bicep",
BicepSourceFileKind.ParamsFile => "main.bicepparam",
_ => throw new InvalidOperationException(),
};
var mainBicepFilePath = FileHelper.SaveResultFile(TestContext, fileName, text, testOutputPath);
var mainUri = DocumentUri.FromFileSystemPath(mainBicepFilePath);

FileHelper.SaveResultFile(TestContext, "groups.bicep", string.Empty, Path.Combine(testOutputPath, "br"));
var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep";
var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|');
var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}");

var settingsProvider = StrictMock.Of<ISettingsProvider>();
settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false);
Expand All @@ -4151,11 +4141,10 @@ public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string i
using var helper = await MultiFileLanguageServerHelper.StartLanguageServer(
TestContext,
services => services
.AddSingleton<IPublicRegistryModuleMetadataProvider>(publicRegistryModuleMetadataProvider.Object)
.AddSingleton<ISettingsProvider>(settingsProvider.Object)
.WithFileResolver(new FileResolver(new LocalFileSystem())));
.AddSingleton(publicRegistryModuleMetadataProvider.Object)
.AddSingleton(settingsProvider.Object));

var file = await new ServerRequestHelper(TestContext, helper).OpenFile(mainUri.ToUriEncoded(), text);
var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText);
var completions = await file.RequestCompletion(cursor);

completions.Count().Should().Be(2);
Expand All @@ -4174,39 +4163,67 @@ public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string i
[DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)]
public async Task ModuleRegistryReferenceCompletions_GetVersionCompletions(string inputWithCursors, BicepSourceFileKind kind)
{
var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
var (text, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|');

var fileName = kind switch
{
BicepSourceFileKind.BicepFile => "main.bicep",
BicepSourceFileKind.ParamsFile => "main.bicepparam",
_ => throw new InvalidOperationException(),
};
var mainBicepFilePath = FileHelper.SaveResultFile(TestContext, fileName, text, testOutputPath);
var mainUri = DocumentUri.FromFileSystemPath(mainBicepFilePath);
var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep";
var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|');
var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}");

var settingsProvider = StrictMock.Of<ISettingsProvider>();
settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false);

var publicRegistryModuleMetadataProvider = StrictMock.Of<IPublicRegistryModuleMetadataProvider>();
publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerapp", "d1", "contoso.com/help1")]);
publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]);
publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]);

using var helper = await MultiFileLanguageServerHelper.StartLanguageServer(
TestContext,
services => services
.AddSingleton<IPublicRegistryModuleMetadataProvider>(publicRegistryModuleMetadataProvider.Object)
.AddSingleton<ISettingsProvider>(settingsProvider.Object)
.WithFileResolver(new FileResolver(new LocalFileSystem())));
.AddSingleton(publicRegistryModuleMetadataProvider.Object)
.AddSingleton(settingsProvider.Object));

var file = await new ServerRequestHelper(TestContext, helper).OpenFile(mainUri.ToUriEncoded(), text);
var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText);
var completions = await file.RequestCompletion(cursor);

completions.Count().Should().Be(2);
completions.Should().Contain(x => x.Label == "1.0.1" && x.SortText == "0001" && x.Kind == CompletionItemKind.Snippet && x.Detail == null && x.Documentation == null);
completions.Should().Contain(x => x.Label == "1.0.2" && x.SortText == "0000" && x.Kind == CompletionItemKind.Snippet && x.Detail == "d1" && x.Documentation!.MarkupContent!.Value == "[View Documentation](contoso.com/help1)");
}

[TestMethod]
[DataRow("module test 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.BicepFile)]
[DataRow("module test 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.BicepFile)]
[DataRow("module test 'br/public:foo|'", BicepSourceFileKind.BicepFile)]
[DataRow("module test 'br/public:foo|", BicepSourceFileKind.BicepFile)]
[DataRow("using 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.ParamsFile)]
[DataRow("using 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.ParamsFile)]
[DataRow("using 'br/public:foo|'", BicepSourceFileKind.ParamsFile)]
[DataRow("using 'br/public:foo|", BicepSourceFileKind.ParamsFile)]
public async Task Public_registry_completions_support_prefix_matching(string text, BicepSourceFileKind kind)
{
var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep";
var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(text, '|');
var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}");

var settingsProvider = StrictMock.Of<ISettingsProvider>();
settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false);

var publicRegistryModuleMetadataProvider = StrictMock.Of<IPublicRegistryModuleMetadataProvider>();
publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("foo/bar", "d1", "contoso.com/help1"), new("food/bar", "d2", "contoso.com/help2"), new("bar/bar", "d2", "contoso.com/help2")]);

using var helper = await MultiFileLanguageServerHelper.StartLanguageServer(
TestContext,
services => services
.AddSingleton(publicRegistryModuleMetadataProvider.Object)
.AddSingleton(settingsProvider.Object));

var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText);
var completions = await file.RequestCompletion(cursor);

completions.Count().Should().Be(2);
completions.Should().Contain(x => x.Label == "foo/bar");
completions.Should().Contain(x => x.Label == "food/bar");
}

[DataTestMethod]
[DataRow("var arr1 = [|]")]
[DataRow("param arr array = [|]")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,22 +297,11 @@ private IEnumerable<CompletionItem> GetOciModulePathCompletions(BicepCompletionC
return [];
}

if (replacementText == "'br/public:'" ||
replacementText == $"'br:{PublicMCRRegistry}/bicep/'" ||
replacementText == "'br/public:" ||
replacementText == $"'br:{PublicMCRRegistry}/bicep/")
{
return GetPublicModuleCompletions(replacementText, context, sourceFileUri);
}
else
{
List<CompletionItem> completions = new();

completions.AddRange(GetACRPartialPathCompletionsFromBicepConfig(replacementText, context, sourceFileUri));
completions.AddRange(GetMCRPathCompletionFromBicepConfig(replacementText, context, sourceFileUri));

return completions;
}
return [
.. GetPublicModuleCompletions(replacementText, context),
.. GetACRPartialPathCompletionsFromBicepConfig(replacementText, context, sourceFileUri),
.. GetMCRPathCompletionFromBicepConfig(replacementText, context, sourceFileUri),
];
}


Expand Down Expand Up @@ -503,16 +492,30 @@ private IEnumerable<CompletionItem> GetACRPartialPathCompletionsFromBicepConfig(
// br/public:<CURSOR>
// or
// br:mcr.microsoft.com/bicep/:<CURSOR>
private IEnumerable<CompletionItem> GetPublicModuleCompletions(string replacementText, BicepCompletionContext context, Uri sourceUri)
private IEnumerable<CompletionItem> GetPublicModuleCompletions(string replacementText, BicepCompletionContext context)
{
List<CompletionItem> completions = new();
var (prefix, suffix) = replacementText switch {
{} x when x.StartsWith("'br/public:", StringComparison.Ordinal) => ("'br/public:", x["'br/public:".Length..].TrimEnd('\'')),
{} x when x.StartsWith($"'br:{PublicMCRRegistry}/bicep/", StringComparison.Ordinal) => ($"'br:{PublicMCRRegistry}/bicep/", x[$"'br:{PublicMCRRegistry}/bicep/".Length..].TrimEnd('\'')),
_ => (null, null),
};

var replacementTextWithTrimmedEnd = replacementText.TrimEnd('\'');
if (prefix is null || suffix is null)
{
return [];
}

List<CompletionItem> completions = new();

var modules = publicRegistryModuleMetadataProvider.GetModulesMetadata();
foreach (var (moduleName, description, documentationUri) in modules)
{
var insertText = $"{replacementTextWithTrimmedEnd}{moduleName}:$0'";
if (!moduleName.StartsWith(suffix, StringComparison.Ordinal))
{
continue;
}

var insertText = $"{prefix}{moduleName}:$0'";

var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, moduleName)
.WithSnippetEdit(context.ReplacementRange, insertText)
Expand Down

0 comments on commit 9f481ee

Please sign in to comment.