File: Completion\SharedSyntaxContextsWithSpeculativeModel.cs
Web Access
Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Completion;

internal sealed class SharedSyntaxContextsWithSpeculativeModel
{
    private readonly Document _document;
    private readonly int _position;

    private readonly ConcurrentDictionary<Document, AsyncLazy<SyntaxContext>> _cache;
    private readonly Lazy<ImmutableArray<DocumentId>> _lazyRelatedDocumentIds;

    public SharedSyntaxContextsWithSpeculativeModel(Document document, int position)
    {
        _document = document;
        _position = position;
        _cache = [];
        _lazyRelatedDocumentIds = new(_document.GetLinkedDocumentIds, isThreadSafe: true);
    }

    public Task<SyntaxContext> GetSyntaxContextAsync(Document document, CancellationToken cancellationToken)
    {
        if (!_cache.TryGetValue(document, out var lazyContext))
        {
            if (_document.Id != document.Id && !_lazyRelatedDocumentIds.Value.Contains(document.Id))
                throw new ArgumentException("Don't support getting SyntaxContext for document unrelated to the original document");

            lazyContext = GetLazySyntaxContextWithSpeculativeModel(document, this);
        }

        return lazyContext.GetValueAsync(cancellationToken);

        // Extract a local function to avoid creating a closure for code path of cache hit.
        static AsyncLazy<SyntaxContext> GetLazySyntaxContextWithSpeculativeModel(Document document, SharedSyntaxContextsWithSpeculativeModel self)
        {
            return self._cache.GetOrAdd(document, d => AsyncLazy.Create(static (arg, cancellationToken)
                => Utilities.CreateSyntaxContextWithExistingSpeculativeModelAsync(arg.d, arg._position, cancellationToken), (d, self._position)));
        }
    }
}