Skip to content

Commit

Permalink
Augment hover support on imported symbols and properties thereof
Browse files Browse the repository at this point in the history
  • Loading branch information
jeskew committed Jul 27, 2023
1 parent aa0f8f5 commit 2a691da
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 8 deletions.
11 changes: 4 additions & 7 deletions src/Bicep.Core/Emit/CompileTimeImports/ImportClosureInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,13 @@ private static ISemanticModel GetImportedModel(WildcardImportSymbol symbol)
.Select<TypeAliasSymbol, (string, IntraTemplateSymbolicTypeReference)>(t => (t.Name, new BicepSymbolicTypeReference(t, model)));

private static IEnumerable<(string symbolName, IntraTemplateSymbolicTypeReference reference)> EnumerateExportedSymbolsAsIntraTemplateSymbols(ArmTemplateSemanticModel model)
=> EnumerateExportedTypesAsPointers(model)
.Select<string, (string, IntraTemplateSymbolicTypeReference)>(pointer => (pointer, new ArmSymbolicTypeReference(pointer, model.SourceFile, model)));
=> model.ExportedTypes.Keys
.Select<string, (string, IntraTemplateSymbolicTypeReference)>(typeName => (typeName, new ArmSymbolicTypeReference($"{ArmTypeRefPrefix}{typeName}", model.SourceFile, model)));

private static IEnumerable<(string symbolName, IntraTemplateSymbolicTypeReference reference)> EnumerateExportedSymbolsAsIntraTemplateSymbols(TemplateSpecSemanticModel model)
=> EnumerateExportedTypesAsPointers(model)
=> model.ExportedTypes.Keys
.Select<string, (string, IntraTemplateSymbolicTypeReference)>(
pointer => (pointer, new ArmSymbolicTypeReference(pointer, model.SourceFile.MainTemplateFile, model)));

private static IEnumerable<string> EnumerateExportedTypesAsPointers(ISemanticModel model)
=> model.ExportedTypes.Keys.Select(typeName => $"{ArmTypeRefPrefix}{typeName}");
typeName => (typeName, new ArmSymbolicTypeReference($"{ArmTypeRefPrefix}{typeName}", model.SourceFile.MainTemplateFile, model)));

private static IReadOnlyDictionary<IntraTemplateSymbolicTypeReference, (ImportedSymbolOriginMetadata OriginMetadata, string UniqueNameWithinClosure)>
CalculateImportedTypeMetadata(SemanticModel model, ImportClosure closure)
Expand Down
40 changes: 40 additions & 0 deletions src/Bicep.LangServer.IntegrationTests/HoverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,46 @@ param foobar bool
);
}

[TestMethod]
public async Task Hovers_are_displayed_on_imported_types()
{
var moduleText = """
@export()
@description('The foo type')
type foo = string
""";

var mainTextWithCursor = """
import {foo} from 'mod.bicep'
import * as mod from 'mod.bicep'

type fooAlias = f|oo
type fooAliasOffWildcard = m|od.f|oo
""";

var (mainText, cursors) = ParserHelper.GetFileWithCursors(mainTextWithCursor, '|');

var mainFile = SourceFileFactory.CreateBicepFile(new Uri("file:///path/to/main.bicep"), mainText);
var moduleFile = SourceFileFactory.CreateBicepFile(new Uri("file:///path/to/mod.bicep"), moduleText);

var files = new Dictionary<Uri, string>
{
[mainFile.FileUri] = mainText,
[moduleFile.FileUri] = moduleText
};

using var helper = await LanguageServerHelper.StartServerWithText(this.TestContext, files, mainFile.FileUri,
services => services.WithFeatureOverrides(new(TestContext, UserDefinedTypesEnabled: true, CompileTimeImportsEnabled: true)));
var client = helper.Client;

var hovers = await RequestHovers(client, mainFile, cursors);

hovers.Should().SatisfyRespectively(
h => h!.Contents.MarkupContent!.Value.Should().EndWith("```bicep\ntype foo: Type<string>\n```\nThe foo type\n"),
h => h!.Contents.MarkupContent!.Value.Should().EndWith("```bicep\nmod namespace\n```\n"),
h => h!.Contents.MarkupContent!.Value.Should().EndWith("```bicep\nfoo: Type<string>\n```\nThe foo type\n"));
}


private string GetManifestFileContents(string? documentationUri, string? description)
{
Expand Down
16 changes: 15 additions & 1 deletion src/Bicep.LangServer/Handlers/BicepHoverHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public BicepHoverHandler(
return null;
}

private static string? TryGetDescriptionMarkdown(SymbolResolutionResult result, WildcardImportSymbol symbol)
=> DescriptionHelper.TryGetFromDecorator(result.Context.Compilation.GetEntrypointSemanticModel(), symbol.EnclosingDeclaration);

private static async Task<MarkedStringsOrMarkupContent?> GetMarkdown(
HoverParams request,
SymbolResolutionResult result,
Expand Down Expand Up @@ -100,6 +103,13 @@ public BicepHoverHandler(
return AsMarkdown(CodeBlockWithDescription(
WithTypeModifiers($"type {declaredType.Name}: {declaredType.Type}", declaredType.Type), TryGetDescriptionMarkdown(result, declaredType)));

case ImportedTypeSymbol importedType:
return AsMarkdown(CodeBlockWithDescription(
WithTypeModifiers($"type {importedType.Name}: {importedType.Type}", importedType.Type),
importedType.TryGetSemanticModel(out var model, out _) && model.ExportedTypes.TryGetValue(importedType.OriginalSymbolName, out var exportedTypeMetadata)
? exportedTypeMetadata.Description
: null));

case AmbientTypeSymbol ambientType:
return AsMarkdown(CodeBlock(WithTypeModifiers($"type {ambientType.Name}: {ambientType.Type}", ambientType.Type)));

Expand All @@ -124,6 +134,9 @@ public BicepHoverHandler(
case BuiltInNamespaceSymbol builtInNamespace:
return AsMarkdown(CodeBlock($"{builtInNamespace.Name} namespace"));

case WildcardImportSymbol wildcardImport:
return AsMarkdown(CodeBlockWithDescription($"{wildcardImport.Name} namespace", TryGetDescriptionMarkdown(result, wildcardImport)));

case IFunctionSymbol function when result.Origin is FunctionCallSyntaxBase functionCall:
// it's not possible for a non-function call syntax to resolve to a function symbol
// but this simplifies the checks
Expand All @@ -132,8 +145,9 @@ public BicepHoverHandler(
case DeclaredFunctionSymbol function:
// A declared function can only have a single overload!
return AsMarkdown(GetFunctionOverloadMarkdown(function.Overloads.Single()));

case PropertySymbol property:
return AsMarkdown(CodeBlockWithDescription($"{property.Name}: {property.Type}", property.Description));
return AsMarkdown(CodeBlockWithDescription(WithTypeModifiers($"{property.Name}: {property.Type}", property.Type), property.Description));

case LocalVariableSymbol local:
return AsMarkdown(CodeBlock($"{local.Name}: {local.Type}"));
Expand Down

0 comments on commit 2a691da

Please sign in to comment.