Skip to content

Commit

Permalink
Implement goto for resource and variable property accesses; module pa…
Browse files Browse the repository at this point in the history
…ths, params and outputs (#3690)

* Initial commit for module and property access goto

* disable module path gotos for non existent files

* Implement property and module property accesses

* Added valid unbound heuristic for tests

* Addressed feedback and using workplace to get
module compilations

* Removed duplicate method and added json file goto
  • Loading branch information
TSunny007 authored Aug 3, 2021
1 parent e4f7847 commit afe6cb3
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 30 deletions.
12 changes: 6 additions & 6 deletions src/Bicep.Core/FileSystem/FileResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public bool TryRead(Uri fileUri, [NotNullWhen(true)] out string? fileContents, [
failureBuilder = null;
if (Directory.Exists(fileUri.LocalPath))
{
// Docs suggest this is the error to throw when we give a directory.
// Docs suggest this is the error to throw when we give a directory.
// A trailing backslash causes windows not to throw this exception.
throw new UnauthorizedAccessException($"Access to the path '{fileUri.LocalPath}' is denied.");
}
Expand Down Expand Up @@ -58,13 +58,13 @@ public bool TryRead(Uri fileUri, [NotNullWhen(true)] out string? fileContents, [
failureBuilder = null;
if (Directory.Exists(fileUri.LocalPath))
{
// Docs suggest this is the error to throw when we give a directory.
// Docs suggest this is the error to throw when we give a directory.
// A trailing backslash causes windows not to throw this exception.
throw new UnauthorizedAccessException($"Access to the path '{fileUri.LocalPath}' is denied.");
}
using var fileStream = File.OpenRead(fileUri.LocalPath);
using var sr = new StreamReader(fileStream, fileEncoding, true);

Span<char> buffer = stackalloc char[LanguageConstants.MaxLiteralCharacterLimit + 1];
var sb = new StringBuilder();
while (!sr.EndOfStream)
Expand Down Expand Up @@ -107,7 +107,7 @@ public bool TryReadAsBase64(Uri fileUri, [NotNullWhen(true)] out string? fileBas
failureBuilder = null;
if (Directory.Exists(fileUri.LocalPath))
{
// Docs suggest this is the error to throw when we give a directory.
// Docs suggest this is the error to throw when we give a directory.
// A trailing backslash causes windows not to throw this exception.
throw new UnauthorizedAccessException($"Access to the path '{fileUri.LocalPath}' is denied.");
}
Expand Down Expand Up @@ -162,14 +162,14 @@ public bool TryReadAtMostNCharaters(Uri fileUri, Encoding fileEncoding, int n, [
{
if (Directory.Exists(fileUri.LocalPath))
{
// Docs suggest this is the error to throw when we give a directory.
// Docs suggest this is the error to throw when we give a directory.
// A trailing backslash causes windows not to throw this exception.
throw new UnauthorizedAccessException($"Access to the path '{fileUri.LocalPath}' is denied.");
}

using var fileStream = File.OpenRead(fileUri.LocalPath);
using var sr = new StreamReader(fileStream, fileEncoding, true);

var buffer = new char[n];
n = sr.ReadBlock(buffer, 0, n);

Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core/FileSystem/InMemoryFileResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public bool TryRead(Uri fileUri, [NotNullWhen(true)] out string? fileContents, [
return false;
}
if (maxCharacters > 0)
{
{
if (fileContents.Length > maxCharacters)
{
failureBuilder = x => x.FileExceedsMaximumSize(fileUri.LocalPath, maxCharacters, "characters");
Expand Down Expand Up @@ -114,7 +114,7 @@ public bool TryReadAsBase64(Uri fileUri, [NotNullWhen(true)] out string? fileBas
return false;
}
}
fileBase64 = Convert.ToBase64String(bytes);
fileBase64 = Convert.ToBase64String(bytes);
return true;
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,38 @@ public static ImmutableDictionary<string, SyntaxBase> ToNamedPropertyValueDictio
return result;
}

public static ObjectPropertySyntax? SafeGetPropertyByNameRecursive(this ObjectSyntax syntax, IList<string> propertyAccesses)
{
var currentSyntax = syntax;
for (int i = 0; i < propertyAccesses.Count; i++)
{
if (currentSyntax.SafeGetPropertyByName(propertyAccesses[i]) is ObjectPropertySyntax propertySyntax)
{
// we have found our last property access
if (i == propertyAccesses.Count-1)
{
return propertySyntax;
}
// we have successfully gone one level deeper into the object
else if (propertySyntax.Value is ObjectSyntax propertyObjectSyntax)
{
currentSyntax = propertyObjectSyntax;
}
// our path isn't fully traversed yet and we hit a terminal value (not an object)
else
{
break;
}
}
// we couldn't even find this property on the object
else
{
break;
}
}
return null;
}

public static ObjectSyntax MergeProperty(this ObjectSyntax? syntax, string propertyName, string propertyValue) =>
syntax.MergeProperty(propertyName, SyntaxFactory.CreateStringLiteral(propertyValue));

Expand Down
17 changes: 15 additions & 2 deletions src/Bicep.LangServer.IntegrationTests/DefinitionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OmniSharp.Extensions.LanguageServer.Client;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
Expand Down Expand Up @@ -131,8 +130,13 @@ public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(Dat
// visit children only if current node is not bound
continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax));

foreach (var syntax in unboundNodes)
for (int i = 0; i < unboundNodes.Count(); i++)
{
var syntax = unboundNodes[i];
if (ValidUnboundNode(unboundNodes, i))
{
continue;
}
var response = await client.RequestDefinition(new DefinitionParams
{
TextDocument = new TextDocumentIdentifier(uri),
Expand All @@ -144,6 +148,15 @@ public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(Dat
}
}

private bool ValidUnboundNode(List<SyntaxBase> accumulated, int index)
{
// Module path
return index > 1
&& accumulated[index] is StringSyntax
&& accumulated[index-1] is IdentifierSyntax
&& accumulated[index-2] is ModuleDeclarationSyntax;
}

private static LocationLink ValidateDefinitionResponse(LocationOrLocationLinks response)
{
// go to def should produce single result in all cases
Expand Down
2 changes: 1 addition & 1 deletion src/Bicep.LangServer/BicepCompilationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ private ImmutableArray<ISourceFile> CloseCompilationInternal(DocumentUri documen
Message = exception.Message,
Code = new DiagnosticCode("Fatal")
};

// the file is no longer in a state that can be parsed
// clear all info to prevent cascading failures elsewhere
var closedFiles = CloseCompilationInternal(documentUri, version, fatalError.AsEnumerable());
Expand Down
Loading

0 comments on commit afe6cb3

Please sign in to comment.