public async Task TestWorkspaceEventUpdatesCorrectWorkspaceWithMultipleWorkspacesAsync() { var firstWorkspaceXml = @$ "<Workspace> <Project Language=" "C#" " CommonReferences=" "true" " AssemblyName=" "FirstWorkspaceProject" "> <Document FilePath=" "C:\FirstWorkspace.cs" ">FirstWorkspace</Document> </Project> </Workspace>"; var secondWorkspaceXml = @$ "<Workspace> <Project Language=" "C#" " CommonReferences=" "true" " AssemblyName=" "SecondWorkspaceProject" "> <Document FilePath=" "C:\SecondWorkspace.cs" ">SecondWorkspace</Document> </Project> </Workspace>"; using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); var exportProvider = testLspServer.TestWorkspace.ExportProvider; using var testWorkspaceTwo = TestWorkspace.Create( XElement.Parse(secondWorkspaceXml), workspaceKind: WorkspaceKind.MSBuild, exportProvider: exportProvider); // Wait for workspace operations to complete for the second workspace. await WaitForWorkspaceOperationsAsync(testWorkspaceTwo); var firstWorkspaceDocumentUri = ProtocolConversions.GetUriFromFilePath(@"C:\FirstWorkspace.cs"); var secondWorkspaceDocumentUri = ProtocolConversions.GetUriFromFilePath(@"C:\SecondWorkspace.cs"); await testLspServer.OpenDocumentAsync(firstWorkspaceDocumentUri); // Verify we can get both documents from their respective workspaces. var firstDocument = GetLspDocument(firstWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(firstDocument); Assert.Equal(firstWorkspaceDocumentUri, firstDocument.GetURI()); Assert.Equal(testLspServer.TestWorkspace, firstDocument.Project.Solution.Workspace); var secondDocument = GetLspDocument(secondWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(secondDocument); Assert.Equal(secondWorkspaceDocumentUri, secondDocument.GetURI()); Assert.Equal(testWorkspaceTwo, secondDocument.Project.Solution.Workspace); // Verify making a workspace change only changes the respective workspace. var newProjectWorkspaceTwo = testWorkspaceTwo.CurrentSolution.Projects.First().WithAssemblyName("NewCSProj1"); await testWorkspaceTwo.ChangeProjectAsync(newProjectWorkspaceTwo.Id, newProjectWorkspaceTwo.Solution); // The second document should have an updated project assembly name. var secondDocumentChangedProject = GetLspDocument(secondWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(secondDocumentChangedProject); Assert.Equal("NewCSProj1", secondDocumentChangedProject.Project.AssemblyName); Assert.NotEqual(secondDocument, secondDocumentChangedProject); // The first document should be the same document as the last one since that workspace was not changed. Assert.Equal(firstDocument, GetLspDocument(firstWorkspaceDocumentUri, testLspServer)); }
static Uri GetDiagnosticUri(Document document, DiagnosticData diagnosticData) { Contract.ThrowIfNull(diagnosticData.DataLocation, "Diagnostic data location should not be null here"); var filePath = diagnosticData.DataLocation.MappedFilePath ?? diagnosticData.DataLocation.OriginalFilePath; return(ProtocolConversions.GetUriFromFilePath(filePath)); }
public async Task TestDocumentOpenedBeforeAddedToWorkspaceAsync() { var markup = "One"; using var testLspServer = await CreateTestLspServerAsync(markup); // Create a new document, but do not update the workspace solution yet. var newDocumentId = DocumentId.CreateNewId(testLspServer.TestWorkspace.CurrentSolution.ProjectIds[0]); var newDocumentFilePath = @"C:/NewDoc.cs"; var newDocumentInfo = DocumentInfo.Create(newDocumentId, "NewDoc.cs", filePath: newDocumentFilePath, loader: new TestTextLoader("New Doc")); var newDocumentUri = ProtocolConversions.GetUriFromFilePath(newDocumentFilePath); // Open the document via LSP before the workspace sees it. await testLspServer.OpenDocumentAsync(newDocumentUri, "LSP text"); // Verify it is in the lsp misc workspace. var miscDocument = GetLspDocument(newDocumentUri, testLspServer); AssertEx.NotNull(miscDocument); Assert.Equal(testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace(), miscDocument.Project.Solution.Workspace); Assert.Equal("LSP text", (await miscDocument.GetTextAsync(CancellationToken.None)).ToString()); // Make a change and verify the misc document is updated. await testLspServer.InsertTextAsync(newDocumentUri, (0, 0, "More LSP text")); miscDocument = GetLspDocument(newDocumentUri, testLspServer); AssertEx.NotNull(miscDocument); var miscText = await miscDocument.GetTextAsync(CancellationToken.None); Assert.Equal("More LSP textLSP text", miscText.ToString()); // Update the registered workspace with the new document. await testLspServer.TestWorkspace.AddDocumentAsync(newDocumentInfo); // Verify a fork was triggered. Assert.Null(GetManagerWorkspaceState(testLspServer.TestWorkspace, testLspServer)); // Verify that the newly added document in the registered workspace is returned. var document = GetLspDocument(newDocumentUri, testLspServer); AssertEx.NotNull(document); Assert.Equal(testLspServer.TestWorkspace, document.Project.Solution.Workspace); Assert.Equal(newDocumentId, document.Id); // Verify we still are using the tracked LSP text for the document. var documentText = await document.GetTextAsync(CancellationToken.None); Assert.Equal("More LSP textLSP text", documentText.ToString()); }
// Local functions static async Task <LSP.Location?> ComputeLocationAsync( Document document, int position, DocumentSpan documentSpan, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken) { if (documentSpan != default) { // We do have a document span, so compute location normally. return(await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan, cancellationToken).ConfigureAwait(false)); } // If we have no document span, our location may be in metadata or may be a namespace. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol == null || symbol.Locations.IsEmpty || symbol.Kind == SymbolKind.Namespace) { // Either: // (1) We couldn't find the location in metadata and it's not in any of our known documents. // (2) The symbol is a namespace (and therefore has no location). return(null); } var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync( document.Project, symbol, allowDecompilation : false, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; if (string.IsNullOrEmpty(declarationFile.FilePath)) { return(null); } try { return(new LSP.Location { Uri = ProtocolConversions.GetUriFromFilePath(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan), }); } catch (UriFormatException e) when(FatalError.ReportAndCatch(e)) { // We might reach this point if the file path is formatted incorrectly. return(null); } }
public async Task TestLspTransfersDocumentToNewWorkspaceAsync() { var markup = "One"; // Create a server that includes the LSP misc files workspace so we can test transfers to and from it. using var testLspServer = await CreateTestLspServerAsync(markup, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); // Create a new document, but do not update the workspace solution yet. var newDocumentId = DocumentId.CreateNewId(testLspServer.TestWorkspace.CurrentSolution.ProjectIds[0]); var newDocumentFilePath = @"C:/NewDoc.cs"; var newDocumentInfo = DocumentInfo.Create(newDocumentId, "NewDoc.cs", filePath: newDocumentFilePath, loader: new TestTextLoader("New Doc")); var newDocumentUri = ProtocolConversions.GetUriFromFilePath(newDocumentFilePath); // Open the document via LSP before the workspace sees it. await testLspServer.OpenDocumentAsync(newDocumentUri, "LSP text"); // Verify it is in the lsp misc workspace. var miscDocument = await GetLspDocumentAsync(newDocumentUri, testLspServer).ConfigureAwait(false); AssertEx.NotNull(miscDocument); Assert.Equal(testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace(), miscDocument.Project.Solution.Workspace); Assert.Equal("LSP text", (await miscDocument.GetTextAsync(CancellationToken.None)).ToString()); // Make a change and verify the misc document is updated. await testLspServer.InsertTextAsync(newDocumentUri, (0, 0, "More LSP text")); miscDocument = await GetLspDocumentAsync(newDocumentUri, testLspServer).ConfigureAwait(false); AssertEx.NotNull(miscDocument); var miscText = await miscDocument.GetTextAsync(CancellationToken.None); Assert.Equal("More LSP textLSP text", miscText.ToString()); // Update the registered workspace with the new document. await testLspServer.TestWorkspace.AddDocumentAsync(newDocumentInfo); // Verify that the newly added document in the registered workspace is returned. var document = await GetLspDocumentAsync(newDocumentUri, testLspServer).ConfigureAwait(false); AssertEx.NotNull(document); Assert.Equal(testLspServer.TestWorkspace, document.Project.Solution.Workspace); Assert.Equal(newDocumentId, document.Id); // Verify we still are using the tracked LSP text for the document. var documentText = await document.GetTextAsync(CancellationToken.None); Assert.Equal("More LSP textLSP text", documentText.ToString()); }
private static async Task <LSP.Location?> GetSourceDefinitionLocationAsync(XamlSourceDefinition sourceDefinition, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(sourceDefinition.FilePath); if (sourceDefinition.Span != null) { // If the Span is not null, use the span. var document = context.Solution?.GetDocuments(ProtocolConversions.GetUriFromFilePath(sourceDefinition.FilePath)).FirstOrDefault(); if (document != null) { return(await ProtocolConversions.TextSpanToLocationAsync( document, sourceDefinition.Span.Value, isStale : false, cancellationToken).ConfigureAwait(false)); } else { // Cannot find the file in solution. This is probably a file lives outside of the solution like generic.xaml // which lives in the Windows SDK folder. Try getting the SourceText from the file path. using var fileStream = new FileStream(sourceDefinition.FilePath, FileMode.Open, FileAccess.Read); var sourceText = SourceText.From(fileStream); return(new LSP.Location { Uri = new Uri(sourceDefinition.FilePath), Range = ProtocolConversions.TextSpanToRange(sourceDefinition.Span.Value, sourceText) }); } } else { // We should have the line and column, so use them to build the LSP Range. var position = new Position(sourceDefinition.Line, sourceDefinition.Column); return(new LSP.Location { Uri = new Uri(sourceDefinition.FilePath), Range = new LSP.Range() { Start = position, End = position } }); } }
private static async Task <LSP.Location?> GetSourceDefinitionLocationAsync(XamlSourceDefinition sourceDefinition, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(sourceDefinition.FilePath); var document = context.Solution?.GetDocuments(ProtocolConversions.GetUriFromFilePath(sourceDefinition.FilePath)).FirstOrDefault(); if (document != null) { var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var span = sourceDefinition.GetTextSpan(sourceText); if (span != null) { return(await ProtocolConversions.TextSpanToLocationAsync( document, span.Value, isStale : false, cancellationToken).ConfigureAwait(false)); } } else { // Cannot find the file in solution. This is probably a file lives outside of the solution like generic.xaml // which lives in the Windows SDK folder. Try getting the SourceText from the file path. using var fileStream = new FileStream(sourceDefinition.FilePath, FileMode.Open, FileAccess.Read); var sourceText = SourceText.From(fileStream); var span = sourceDefinition.GetTextSpan(sourceText); if (span != null) { return(new LSP.Location { Uri = new Uri(sourceDefinition.FilePath), Range = ProtocolConversions.TextSpanToRange(span.Value, sourceText), }); } } return(null); }
public async Task TestLspUpdatesCorrectWorkspaceWithMultipleWorkspacesAsync() { var firstWorkspaceXml = @$ "<Workspace> <Project Language=" "C#" " CommonReferences=" "true" " AssemblyName=" "FirstWorkspaceProject" "> <Document FilePath=" "C:\FirstWorkspace.cs" ">FirstWorkspace</Document> </Project> </Workspace>"; var secondWorkspaceXml = @$ "<Workspace> <Project Language=" "C#" " CommonReferences=" "true" " AssemblyName=" "SecondWorkspaceProject" "> <Document FilePath=" "C:\SecondWorkspace.cs" ">SecondWorkspace</Document> </Project> </Workspace>"; using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); var exportProvider = testLspServer.TestWorkspace.ExportProvider; using var testWorkspaceTwo = TestWorkspace.Create( XElement.Parse(secondWorkspaceXml), workspaceKind: WorkspaceKind.MSBuild, exportProvider: exportProvider); // Wait for workspace creation operations to complete for the second workspace. await WaitForWorkspaceOperationsAsync(testWorkspaceTwo); // Verify both workspaces registered. Assert.Null(GetManagerWorkspaceState(testLspServer.TestWorkspace, testLspServer)); Assert.Null(GetManagerWorkspaceState(testWorkspaceTwo, testLspServer)); var firstWorkspaceDocumentUri = ProtocolConversions.GetUriFromFilePath(@"C:\FirstWorkspace.cs"); var secondWorkspaceDocumentUri = ProtocolConversions.GetUriFromFilePath(@"C:\SecondWorkspace.cs"); await testLspServer.OpenDocumentAsync(firstWorkspaceDocumentUri); // Verify both workspaces forked on document open. Assert.NotNull(GetManagerWorkspaceState(testLspServer.TestWorkspace, testLspServer)); Assert.NotNull(GetManagerWorkspaceState(testWorkspaceTwo, testLspServer)); // Verify we can get both documents from their respective workspaces. var firstDocument = GetLspDocument(firstWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(firstDocument); Assert.Equal(firstWorkspaceDocumentUri, firstDocument.GetURI()); Assert.Equal(testLspServer.TestWorkspace, firstDocument.Project.Solution.Workspace); var secondDocument = GetLspDocument(secondWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(secondDocument); Assert.Equal(secondWorkspaceDocumentUri, secondDocument.GetURI()); Assert.Equal(testWorkspaceTwo, secondDocument.Project.Solution.Workspace); // Verify making an LSP change only changes the respective workspace and document. await testLspServer.InsertTextAsync(firstWorkspaceDocumentUri, (0, 0, "Change in first workspace")); // The first document should now different text. var changedFirstDocument = GetLspDocument(firstWorkspaceDocumentUri, testLspServer); AssertEx.NotNull(changedFirstDocument); var changedFirstDocumentText = await changedFirstDocument.GetTextAsync(CancellationToken.None); var firstDocumentText = await firstDocument.GetTextAsync(CancellationToken.None); Assert.NotEqual(firstDocumentText, changedFirstDocumentText); // The second document should return the same document instance since it was not changed. var unchangedSecondDocument = GetLspDocument(secondWorkspaceDocumentUri, testLspServer); Assert.Equal(secondDocument, unchangedSecondDocument); }