public static BicepFile CreateBicepFile(Uri fileUri, string fileContents) { var parser = new Parser(fileContents); var lineStarts = TextCoordinateConverter.GetLineStarts(fileContents); return(new BicepFile(fileUri, lineStarts, parser.Program())); }
public static SyntaxTree Create(string filePath, string fileContents) { var parser = new Parser.Parser(fileContents); var lineStarts = TextCoordinateConverter.GetLineStarts(fileContents); return(new SyntaxTree(filePath, lineStarts, parser.Program())); }
private static (string, IEnumerable <object>) CompileInternal(string content) { try { var lineStarts = TextCoordinateConverter.GetLineStarts(content); var compilation = GetCompilation(content); var emitterSettings = new EmitterSettings(features); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); // memory stream is not ideal for frequent large allocations using var stream = new MemoryStream(); var emitResult = emitter.Emit(stream); if (emitResult.Status != EmitStatus.Failed) { // compilation was successful or had warnings - return the compiled template stream.Position = 0; return(ReadStreamToEnd(stream), emitResult.Diagnostics.Select(d => ToMonacoDiagnostic(d, lineStarts))); } // compilation failed return("Compilation failed!", emitResult.Diagnostics.Select(d => ToMonacoDiagnostic(d, lineStarts))); } catch (Exception exception) { return(exception.ToString(), Enumerable.Empty <object>()); } }
public void GetLineStarts_MixedEndOfLines_ReturnsCorrectLineStarts(string contents, int[] expectedLineStarts) { IReadOnlyList <int> lineStarts = TextCoordinateConverter.GetLineStarts(contents); lineStarts.Should().HaveCount(expectedLineStarts.Length); lineStarts.Should().BeEquivalentTo(expectedLineStarts); }
public async Task RenamingFunctionsShouldProduceEmptyEdit(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var validFunctionCallPairs = symbolTable .Where(pair => pair.Value.Kind == SymbolKind.Function) .Select(pair => pair.Key); foreach (var syntax in validFunctionCallPairs) { var edit = await client.RequestRename(new RenameParams { NewName = "NewIdentifier", TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); edit.DocumentChanges.Should().BeNullOrEmpty(); edit.Changes.Should().BeNull(); } }
public static string AddDiagsToSourceText <T>(string bicepOutput, string newlineSequence, IEnumerable <T> items, Func <T, TextSpan> getSpanFunc, Func <T, string> diagsFunc) { var lineStarts = TextCoordinateConverter.GetLineStarts(bicepOutput); var itemsByLine = items .Select(item => { var(line, character) = TextCoordinateConverter.GetPosition(lineStarts, getSpanFunc(item).Position); return(line, character, item); }) .ToLookup(t => t.line); var sourceTextLines = bicepOutput.Split(newlineSequence); var stringBuilder = new StringBuilder(); for (var i = 0; i < sourceTextLines.Length; i++) { stringBuilder.Append(sourceTextLines[i]); stringBuilder.Append(newlineSequence); foreach (var(line, character, item) in itemsByLine[i]) { var escapedDiagsText = EscapeWhitespace(diagsFunc(item)); stringBuilder.Append($"//@[{character}:{character + getSpanFunc(item).Length}) {escapedDiagsText}"); stringBuilder.Append(newlineSequence); } } return(stringBuilder.ToString()); }
public async Task GoToDefinitionRequestOnUnsupportedOrInvalidSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var undeclaredSymbolBindings = symbolTable.Where(pair => !(pair.Value is DeclaredSymbol)); foreach (var(syntax, _) in undeclaredSymbolBindings) { var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); // go to definition on a symbol that isn't declared by the user (like error or function symbol) // should produce an empty response response.Should().BeEmpty(); } }
public void GetLineStarts_SingleLine_ReturnsZero() { IReadOnlyList <int> lineStarts = TextCoordinateConverter.GetLineStarts("The quick brown fox jumps over the lazy dog"); lineStarts.Should().HaveCount(1); lineStarts[0].Should().Be(0); }
public void GetLineStarts_EmptyContents_ReturnsZero() { IReadOnlyList <int> lineStarts = TextCoordinateConverter.GetLineStarts(string.Empty); lineStarts.Should().HaveCount(1); lineStarts[0].Should().Be(0); }
private void BuildManyFilesToStdOut(IDiagnosticLogger logger, string[] bicepPaths) { using var writer = new JsonTextWriter(this.outputWriter) { Formatting = Formatting.Indented }; if (bicepPaths.Length > 1) { writer.WriteStartArray(); } foreach (var bicepPath in bicepPaths) { string text = File.ReadAllText(bicepPath); var lineStarts = TextCoordinateConverter.GetLineStarts(text); var compilation = new Compilation(SyntaxFactory.CreateFromText(text)); var emitter = new TemplateEmitter(compilation.GetSemanticModel()); var result = emitter.Emit(writer); foreach (var diagnostic in result.Diagnostics) { logger.LogDiagnostic(bicepPath, diagnostic, lineStarts); } } if (bicepPaths.Length > 1) { writer.WriteEndArray(); } }
public async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in symbolTable.Where(s => s.Value.Kind != SymbolKind.Error)) { var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams { TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // calculate expected highlights var expectedHighlights = symbolToSyntaxLookup[symbol].Select(node => CreateExpectedHighlight(lineStarts, node)); // ranges should match what we got from our own symbol table highlights.Should().BeEquivalentTo(expectedHighlights); } }
public void ProgramsShouldProduceExpectedUserDeclaredSymbols(DataSet dataSet) { var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var model = compilation.GetSemanticModel(); var symbols = SymbolCollector .CollectSymbols(model) .OfType <DeclaredSymbol>(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); string getLoggingString(DeclaredSymbol symbol) { (_, var startChar) = TextCoordinateConverter.GetPosition(lineStarts, symbol.DeclaringSyntax.Span.Position); return($"{symbol.Kind} {symbol.Name}. Type: {symbol.Type}. Declaration start char: {startChar}, length: {symbol.DeclaringSyntax.Span.Length}"); } var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(dataSet, symbols, symb => symb.NameSyntax.Span, getLoggingString); var resultsFile = FileHelper.SaveResultFile(this.TestContext !, Path.Combine(dataSet.Name, DataSet.TestFileMainSymbols), sourceTextWithDiags); sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput( dataSet.Symbols, expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainSymbols), actualLocation: resultsFile); }
public CompilationContext Create(string text) { var lineStarts = TextCoordinateConverter.GetLineStarts(text); var parser = new Parser(text); var program = parser.Program(); var compilation = new Compilation(program); return(new CompilationContext(compilation, lineStarts)); }
public static IList <CompletionTrigger> GetTriggers(string text) { var program = ParserHelper.Parse(text); var lineStarts = TextCoordinateConverter.GetLineStarts(text); var triggers = new List <CompletionTrigger>(); var visitor = new CompletionTriggerCollector(triggers, lineStarts); visitor.Visit(program); return(triggers); }
public async Task RequestingCodeActionWithFixableDiagnosticsShouldProduceQuickFixes(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); // construct a parallel compilation var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var fixables = compilation.GetSemanticModel().GetAllDiagnostics().OfType <IFixable>(); foreach (IFixable fixable in fixables) { foreach (var span in GetOverlappingSpans(fixable.Span)) { CommandOrCodeActionContainer?quickFixes = await client.RequestCodeAction(new CodeActionParams { TextDocument = new TextDocumentIdentifier(uri), Range = span.ToRange(lineStarts) }); // Assert. quickFixes.Should().NotBeNull(); var quickFixList = quickFixes.ToList(); var bicepFixList = fixable.Fixes.ToList(); quickFixList.Should().HaveSameCount(bicepFixList); for (int i = 0; i < quickFixList.Count; i++) { var quickFix = quickFixList[i]; var bicepFix = bicepFixList[i]; quickFix.IsCodeAction.Should().BeTrue(); quickFix.CodeAction.Kind.Should().Be(CodeActionKind.QuickFix); quickFix.CodeAction.Title.Should().Be(bicepFix.Description); quickFix.CodeAction.Edit.Changes.Should().ContainKey(uri); var textEditList = quickFix.CodeAction.Edit.Changes[uri].ToList(); var replacementList = bicepFix.Replacements.ToList(); for (int j = 0; j < textEditList.Count; j++) { var textEdit = textEditList[j]; var replacement = replacementList[j]; textEdit.Range.Should().Be(replacement.ToRange(lineStarts)); textEdit.NewText.Should().Be(replacement.Text); } } } } }
private IEnumerable <string> GetAllDiagnostics(string text, string bicepFilePath) { var compilation = new Compilation(SyntaxFactory.CreateFromText(text)); var lineStarts = TextCoordinateConverter.GetLineStarts(text); return(compilation.GetSemanticModel() .GetAllDiagnostics() .Select(d => { var(line, character) = TextCoordinateConverter.GetPosition(lineStarts, d.Span.Position); return $"{bicepFilePath}({line + 1},{character + 1}) : {d.Level} {d.Code}: {d.Message}"; })); }
private static void BuildSingleFile(IDiagnosticLogger logger, string bicepPath, string outputPath) { string text = File.ReadAllText(bicepPath); var lineStarts = TextCoordinateConverter.GetLineStarts(text); var compilation = new Compilation(SyntaxFactory.CreateFromText(text)); var emitter = new TemplateEmitter(compilation.GetSemanticModel()); var result = emitter.Emit(outputPath); foreach (var diagnostic in result.Diagnostics) { logger.LogDiagnostic(bicepPath, diagnostic, lineStarts); } }
private string?GetSelectedTextFromFile(DocumentUri uri, Range?range) { if (range is null) { return(null); } var contents = File.ReadAllText(uri.GetFileSystemPath()); var lineStarts = TextCoordinateConverter.GetLineStarts(contents); var start = TextCoordinateConverter.GetOffset(lineStarts, range.Start.Line, range.Start.Character); var end = TextCoordinateConverter.GetOffset(lineStarts, range.End.Line, range.End.Character); var selectedText = contents.Substring(start, end - start); return(selectedText); }
public async Task RenamingIdentifierAccessOrDeclarationShouldRenameDeclarationAndAllReferences(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); var validVariableAccessPairs = symbolTable .Where(pair => (pair.Key is VariableAccessSyntax || pair.Key is IDeclarationSyntax) && pair.Value.Kind != SymbolKind.Error && pair.Value.Kind != SymbolKind.Function && pair.Value.Kind != SymbolKind.Namespace // symbols whose identifiers have parse errors will have a name like <error> or <missing> && pair.Value.Name.Contains("<") == false); const string expectedNewText = "NewIdentifier"; foreach (var(syntax, symbol) in validVariableAccessPairs) { var edit = await client.RequestRename(new RenameParams { NewName = expectedNewText, TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); edit.DocumentChanges.Should().BeNullOrEmpty(); edit.Changes.Should().HaveCount(1); edit.Changes.Should().ContainKey(uri); var textEdits = edit.Changes[uri]; textEdits.Should().NotBeEmpty(); var expectedEdits = symbolToSyntaxLookup[symbol] .Select(node => CreateExpectedTextEdit(lineStarts, expectedNewText, node)); textEdits.Should().BeEquivalentTo(expectedEdits); } }
private void BuildSingleFile(IDiagnosticLogger logger, string bicepPath, string outputPath) { string text = ReadFile(bicepPath); var lineStarts = TextCoordinateConverter.GetLineStarts(text); var compilation = new Compilation(resourceTypeProvider, SyntaxFactory.CreateFromText(text)); var emitter = new TemplateEmitter(compilation.GetSemanticModel()); using var outputStream = CreateFileStream(outputPath); var result = emitter.Emit(outputStream); foreach (var diagnostic in result.Diagnostics) { logger.LogDiagnostic(bicepPath, diagnostic, lineStarts); } }
public async Task RenamingNonSymbolsShouldProduceEmptyEdit(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var edit = await client.RequestRename(new RenameParams { NewName = "NewIdentifier", TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); edit.DocumentChanges.Should().BeNullOrEmpty(); edit.Changes.Should().BeNull(); } }
public static string AddDiagsToSourceText <T>(string bicepOutput, string newlineSequence, IEnumerable <T> items, Func <T, TextSpan> getSpanFunc, Func <T, string> diagsFunc) { var lineStarts = TextCoordinateConverter.GetLineStarts(bicepOutput); var diagsByLine = items .Select(item => { var span = getSpanFunc(item); var(line, startChar) = TextCoordinateConverter.GetPosition(lineStarts, span.Position); var endChar = startChar + span.Length; var escapedText = EscapeWhitespace(diagsFunc(item)); return(line, startChar, endChar, escapedText); }) .ToLookup(t => t.line); var diags = diagsByLine.SelectMany(x => x); var startCharPadding = diags.Any() ? CountDigits(diags.Max(x => x.startChar)) : 0; var endCharPadding = diags.Any() ? CountDigits(diags.Max(x => x.endChar)) : 0; var sourceTextLines = bicepOutput.Split(newlineSequence); var stringBuilder = new StringBuilder(); for (var i = 0; i < sourceTextLines.Length; i++) { stringBuilder.Append(sourceTextLines[i]); stringBuilder.Append(newlineSequence); foreach (var diag in diagsByLine[i]) { var startCharPadded = diag.startChar.ToString().PadLeft(startCharPadding, '0'); var endCharPadded = diag.endChar.ToString().PadLeft(endCharPadding, '0'); // Pad the start & end char with zeros to ensure that the escaped text always starts at the same place // This makes it easier to compare lines visually stringBuilder.Append($"//@[{startCharPadded}:{endCharPadded}) {diag.escapedText}"); stringBuilder.Append(newlineSequence); } } return(stringBuilder.ToString()); }
public async Task FindReferencesOnNonSymbolsShouldProduceEmptyResult(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); locations.Should().BeEmpty(); } }
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.ProgramSyntax, seed: new List <SyntaxBase>(), function: (accumulated, syntax) => { if (IsUnboundNode(symbolTable, syntax) && !(syntax is ProgramSyntax)) { // only collect unbound nodes non-program nodes accumulated.Add(syntax); } return(accumulated); }, resultSelector: accumulated => accumulated, // visit children only if current node is not bound continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax)); foreach (var syntax in unboundNodes) { var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); // go to definition on a syntax node that isn't bound to a symbol should produce an empty response response.Should().BeEmpty(); } }
public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); // construct a parallel compilation var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolReferences = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is IDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (SyntaxBase symbolReference in symbolReferences) { var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, symbolReference.Span.Position) }); if (symbolTable.TryGetValue(symbolReference, out var symbol) == false) { // symbol ref not bound to a symbol ValidateEmptyHover(hover); continue; } switch (symbol !.Kind) {
public object GetSemanticTokens(string content) { var lineStarts = TextCoordinateConverter.GetLineStarts(content); var compilation = new Compilation(SyntaxFactory.CreateFromText(content)); var tokens = SemanticTokenVisitor.BuildSemanticTokens(compilation.ProgramSyntax, lineStarts); var data = new List <int>(); SemanticToken?prevToken = null; foreach (var token in tokens) { if (prevToken == null) { data.Add(token.Line); data.Add(token.Character); data.Add(token.Length); } else if (prevToken.Line != token.Line) { data.Add(token.Line - prevToken.Line); data.Add(token.Character); data.Add(token.Length); } else { data.Add(0); data.Add(token.Character - prevToken.Character); data.Add(token.Length); } data.Add((int)token.TokenType); data.Add(0); prevToken = token; } return(new { data = data.ToArray(), }); }
public async Task FindReferencesWithoutDeclarationsShouldProduceCorrectResults(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in symbolTable.Where(s => s.Value.Kind != SymbolKind.Error)) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); // all URIs should be the same in the results locations.Select(r => r.Uri).Should().AllBeEquivalentTo(uri); // exclude declarations when calculating expected ranges var expectedRanges = symbolToSyntaxLookup[symbol] .Where(node => !(node is IDeclarationSyntax)) .Select(node => PositionHelper.GetNameRange(lineStarts, node)); // ranges should match what we got from our own symbol table locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); } }
public async Task GoToDefinitionRequestOnValidSymbolReferenceShouldReturnLocationOfDeclaredSymbol(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); // filter out symbols that don't have locations var declaredSymbolBindings = symbolTable .Where(pair => pair.Value is DeclaredSymbol) .Select(pair => new KeyValuePair <SyntaxBase, DeclaredSymbol>(pair.Key, (DeclaredSymbol)pair.Value)); foreach (var(syntax, symbol) in declaredSymbolBindings) { var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); var link = ValidateDefinitionResponse(response); // document should match the requested document link.TargetUri.Should().Be(uri); // target range should be the whole span of the symbol link.TargetRange.Should().Be(symbol.DeclaringSyntax.Span.ToRange(lineStarts)); // selection range should be the span of the identifier of the symbol link.TargetSelectionRange.Should().Be(symbol.NameSyntax.Span.ToRange(lineStarts)); // origin selection range should be the span of the syntax node that references the symbol link.OriginSelectionRange.Should().Be(syntax.ToRange(lineStarts)); } }
public async Task RequestingHighlightsForWrongNodeShouldProduceNoHighlights(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); highlights.Should().BeEmpty(); } }
public async Task RequestingCodeActionWithNonFixableDiagnosticsShouldProduceEmptyQuickFixes(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); // construct a parallel compilation var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var nonFixables = compilation.GetSemanticModel().GetAllDiagnostics().Where(diagnostic => !(diagnostic is IFixable)); foreach (var nonFixable in nonFixables) { CommandOrCodeActionContainer?quickFixes = await client.RequestCodeAction(new CodeActionParams { TextDocument = new TextDocumentIdentifier(uri), Range = nonFixable.Span.ToRange(lineStarts) }); // Assert. quickFixes.Should().NotBeNull(); quickFixes.Should().BeEmpty(); } }