public async Task GoToDefinitionRequestOnUnsupportedOrInvalidSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var undeclaredSymbolBindings = symbolTable.Where(pair => pair.Value is not DeclaredSymbol and not PropertySymbol); foreach (var(syntax, _) in undeclaredSymbolBindings) { var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, syntax.Span)) { // 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 async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; // filter out binding failures and locals with invalid identifiers // (locals are special because their full span is the same as the identifier span, // which makes it impossible to highlight locals with invalid identifiers) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in filteredSymbolTable) { 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 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 async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; 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 async Task RenamingFunctionsShouldProduceEmptyEdit(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; 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 async Task GoToDefinitionRequestOnValidSymbolReferenceShouldReturnLocationOfDeclaredSymbol(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; // filter out symbols that don't have locations as well as locals with invalid identifiers // (locals are special because their full span is the same as the identifier span, // which makes it impossible to go to definition on a local with invalid identifiers) var declaredSymbolBindings = symbolTable .Where(pair => pair.Value is DeclaredSymbol && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)) .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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); 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)); if (syntax is ParameterDeclarationSyntax parameterSyntax) { // we only underline the key of the param declaration syntax link.OriginSelectionRange.Should().Be(parameterSyntax.Name.ToRange(lineStarts)); } else if (syntax is ITopLevelNamedDeclarationSyntax namedSyntax) { // Instead of underlining everything, we only underline the resource name link.OriginSelectionRange.Should().Be(namedSyntax.Name.ToRange(lineStarts)); } else { // 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 FindReferencesWithoutDeclarationsShouldProduceCorrectResults(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; // filter out bind failures and locals with invalid identifiers // (locals are special because their span is equal to their identifier span) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); // TODO: Implement for PropertySymbol filteredSymbolTable = filteredSymbolTable.Where(pair => pair.Value is not PropertySymbol); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in filteredSymbolTable) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // 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 INamedDeclarationSyntax)) .Select(node => PositionHelper.GetNameRange(lineStarts, node)); using (new AssertionScope() .WithAnnotations(compilation.SourceFileGrouping.EntryPoint, "expected", expectedRanges, _ => "here", x => x) .WithAnnotations(compilation.SourceFileGrouping.EntryPoint, "actual", locations, _ => "here", x => x.Range)) { // ranges should match what we got from our own symbol table locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); } } }
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.SourceFileGrouping.EntryPoint.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)); 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), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // 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 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); } }
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 async Task FindReferencesOnNonSymbolsShouldProduceEmptyResult(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is ITopLevelNamedDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var wrongNodes = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); locations.Should().BeEmpty(); } }
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.StartServerWithTextAsync(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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); locations.Should().BeEmpty(); } }
public async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; // filter out binding failures and locals with invalid identifiers // (locals are special because their full span is the same as the identifier span, // which makes it impossible to highlight locals with invalid identifiers) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); // TODO: Implement for PropertySymbol filteredSymbolTable = filteredSymbolTable.Where(pair => pair.Value is not PropertySymbol); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in filteredSymbolTable) { 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)); using (new AssertionScope() .WithAnnotations(compilation.SourceFileGrouping.EntryPoint, "expected", expectedHighlights, _ => "here", x => x.Range) .WithAnnotations(compilation.SourceFileGrouping.EntryPoint, "actual", highlights, _ => "here", x => x.Range)) { // ranges should match what we got from our own symbol table highlights.Should().BeEquivalentTo(expectedHighlights); } } }
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.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.SyntaxTreeGrouping.EntryPoint.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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // 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 RenamingIdentifierAccessOrDeclarationShouldRenameDeclarationAndAllReferences(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; 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 ResourceAccessSyntax || pair.Key is ITopLevelNamedDeclarationSyntax) && 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.Should().NotBeNull(); edit !.DocumentChanges.Should().BeNullOrEmpty(); edit.Changes.Should().NotBeNull(); edit.Changes.Should().HaveCount(1); edit.Changes.Should().ContainKey(uri); var textEdits = edit.Changes ![uri];
public async Task GoToDefinitionRequestOnValidSymbolReferenceShouldReturnLocationOfDeclaredSymbol(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; // filter out symbols that don't have locations as well as locals with invalid identifiers // (locals are special because their full span is the same as the identifier span, // which makes it impossible to go to definition on a local with invalid identifiers) var declaredSymbolBindings = symbolTable .Where(pair => pair.Value is DeclaredSymbol && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)) .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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); 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 FindReferencesWithoutDeclarationsShouldProduceCorrectResults(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; // filter out bind failures and locals with invalid identifiers // (locals are special because their span is equal to their identifier span) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in filteredSymbolTable) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // 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 INamedDeclarationSyntax)) .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 RequestingHighlightsForWrongNodeShouldProduceNoHighlights(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is INamedDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var wrongNodes = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.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 = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); highlights.Should().BeNull(); } }
public async Task FindReferencesWithoutDeclarationsShouldProduceCorrectResults(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 locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // 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 FindReferencesWithDeclarationsShouldProduceCorrectResults(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; 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 = true }, Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // all URIs should be the same in the results locations.Select(r => r.Uri).Should().AllBeEquivalentTo(uri); // calculate expected ranges var expectedRanges = symbolToSyntaxLookup[symbol] .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); } }