Example #1
0
    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));
            }
Example #3
0
    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());
    }
Example #4
0
            // 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());
    }
Example #6
0
        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
                    }
                });
            }
        }
Example #7
0
        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);
        }
Example #8
0
    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);
    }