Repository: dotnet/roslyn
File: src/LanguageServer/Protocol/Extensions/Extensions.cs (C#)

Code before:
        {
            // We were not passed a project context.  This can happen when the LSP powered NavBar is not enabled.
            // This branch should be removed when we're using the LSP based navbar in all scenarios.

            var solution = documents.First().Project.Solution;

            // Lookup which of the linked documents is currently active in the workspace.
            var documentIdInCurrentContext = solution.Workspace.GetDocumentIdInCurrentContext(documents.First().Id);

            var document = documentGetter(solution, documentIdInCurrentContext);

            // When a misc document is loaded into the host workspace, it's project file path is the same as the document file path.
            // We always want to prefer the non-misc document.
            if (IsMiscDocumentInHostWorkspace(document))
            {
                var nonMisc = documents.First(d => d.Id != document.Id);
                return nonMisc;
            }

            return document;
        });
    }

    public static bool IsMiscDocumentInHostWorkspace(this TextDocument document)
    {
        return document.Project.Solution.WorkspaceKind == WorkspaceKind.Host && document.Project.FilePath != null && document.Project.FilePath == document.FilePath;
    }

    public static Project? GetProject(this Solution solution, TextDocumentIdentifier projectIdentifier)
    {
        // We need to parse the URI (scheme, file path) to be able to lookup the URI in the solution.
        if (projectIdentifier.DocumentUri.ParsedUri is null)
        {
            return null;
        }

        var projects = solution.Projects.WhereAsArray(project => project.FilePath == projectIdentifier.DocumentUri.ParsedUri.LocalPath);
        return !projects.Any()
            ? null
            : FindItemInProjectContext(projects, projectIdentifier, projectIdGetter: (item) => item.Id, defaultGetter: () => projects[0]);
    }

    public static TextDocument? GetAdditionalDocument(this Solution solution, TextDocumentIdentifier documentIdentifier)
    {
        var documentIds = GetDocumentIds(solution, documentIdentifier.DocumentUri);

        // We don't call GetRequiredAdditionalDocument as the id could be referring to a regular document.
        var additionalDocuments = documentIds.Select(solution.GetAdditionalDocument).WhereNotNull().ToImmutableArray();
        return !additionalDocuments.Any()
            ? null
            : additionalDocuments.FindDocumentInProjectContext(documentIdentifier, (sln, id) => sln.GetRequiredAdditionalDocument(id));

Review comment (suggestion):
This was the best I could come up with to determine if we had a FBP without needing access to the filebased program project system.

Seems reasonable to me to have some indicator on the FBP `Project` indicating it may not be a normal project

Code after:
        {
            // We were not passed a project context.  This can happen when the LSP powered NavBar is not enabled.
            // This branch should be removed when we're using the LSP based navbar in all scenarios.

            var solution = documents.First().Project.Solution;
            // Lookup which of the linked documents is currently active in the workspace.
            var documentIdInCurrentContext = solution.Workspace.GetDocumentIdInCurrentContext(documents.First().Id);
            return documentGetter(solution, documentIdInCurrentContext);
        });
    }

    public static Project? GetProject(this Solution solution, TextDocumentIdentifier projectIdentifier)
    {
        // We need to parse the URI (scheme, file path) to be able to lookup the URI in the solution.
        if (projectIdentifier.DocumentUri.ParsedUri is null)
        {
            return null;
        }

        var projects = solution.Projects.WhereAsArray(project => project.FilePath == projectIdentifier.DocumentUri.ParsedUri.LocalPath);
        return !projects.Any()
            ? null
            : FindItemInProjectContext(projects, projectIdentifier, projectIdGetter: (item) => item.Id, defaultGetter: () => projects[0]);
    }

    public static TextDocument? GetAdditionalDocument(this Solution solution, TextDocumentIdentifier documentIdentifier)
    {
        var documentIds = GetDocumentIds(solution, documentIdentifier.DocumentUri);

        // We don't call GetRequiredAdditionalDocument as the id could be referring to a regular document.
        var additionalDocuments = documentIds.Select(solution.GetAdditionalDocument).WhereNotNull().ToImmutableArray();
        return !additionalDocuments.Any()
            ? null
            : additionalDocuments.FindDocumentInProjectContext(documentIdentifier, (sln, id) => sln.GetRequiredAdditionalDocument(id));
    }

    public static async Task<int> GetPositionFromLinePositionAsync(this TextDocument document, LinePosition linePosition, CancellationToken cancellationToken)
    {
        var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false);
        return text.Lines.GetPosition(linePosition);
    }

    public static bool HasVisualStudioLspCapability(this ClientCapabilities? clientCapabilities)
    {
        if (clientCapabilities is VSInternalClientCapabilities vsClientCapabilities)
        {
            return vsClientCapabilities.SupportsVisualStudioExtensions;
        }

        return false;
    }