ProjectState.cs 44.2 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// 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.
P
Pilchie 已提交
4

5 6
#nullable enable

P
Pilchie 已提交
7 8 9 10
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
11
using System.Diagnostics.CodeAnalysis;
P
Pilchie 已提交
12
using System.Linq;
13
using System.Runtime.CompilerServices;
P
Pilchie 已提交
14 15 16
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
17
using Microsoft.CodeAnalysis.Diagnostics;
18
using Microsoft.CodeAnalysis.Host;
19
using Microsoft.CodeAnalysis.Serialization;
P
Pilchie 已提交
20 21 22 23 24 25
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
    internal partial class ProjectState
    {
26 27 28
        private readonly ProjectInfo _projectInfo;
        private readonly HostLanguageServices _languageServices;
        private readonly SolutionServices _solutionServices;
29 30 31 32 33 34 35 36 37 38 39 40 41

        /// <summary>
        /// The documents in this project. They are sorted by <see cref="DocumentId.Id"/> to provide a stable sort for
        /// <see cref="GetChecksumAsync(CancellationToken)"/>.
        /// </summary>
        private readonly ImmutableSortedDictionary<DocumentId, DocumentState> _documentStates;

        /// <summary>
        /// The additional documents in this project. They are sorted by <see cref="DocumentId.Id"/> to provide a stable sort for
        /// <see cref="GetChecksumAsync(CancellationToken)"/>.
        /// </summary>
        private readonly ImmutableSortedDictionary<DocumentId, TextDocumentState> _additionalDocumentStates;

42 43 44 45 46 47
        /// <summary>
        /// The analyzer config documents in this project.  They are sorted by <see cref="DocumentId.Id"/> to provide a stable sort for
        /// <see cref="GetChecksumAsync(CancellationToken)"/>.
        /// </summary>
        private readonly ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> _analyzerConfigDocumentStates;

48 49
        private readonly ImmutableList<DocumentId> _documentIds;
        private readonly ImmutableList<DocumentId> _additionalDocumentIds;
50 51
        private readonly AsyncLazy<VersionStamp> _lazyLatestDocumentVersion;
        private readonly AsyncLazy<VersionStamp> _lazyLatestDocumentTopLevelChangeVersion;
52

53 54 55
        // Checksums for this solution state
        private readonly ValueSource<ProjectStateChecksums> _lazyChecksums;

56 57 58 59 60
        /// <summary>
        /// The <see cref="AnalyzerConfigSet"/> to be used for analyzer options for specific trees.
        /// </summary>
        private readonly ValueSource<AnalyzerConfigSet> _lazyAnalyzerConfigSet;

61
        private AnalyzerOptions? _lazyAnalyzerOptions;
P
Pilchie 已提交
62 63 64

        private ProjectState(
            ProjectInfo projectInfo,
65
            HostLanguageServices languageServices,
P
Pilchie 已提交
66
            SolutionServices solutionServices,
67 68
            ImmutableList<DocumentId> documentIds,
            ImmutableList<DocumentId> additionalDocumentIds,
69 70
            ImmutableSortedDictionary<DocumentId, DocumentState> documentStates,
            ImmutableSortedDictionary<DocumentId, TextDocumentState> additionalDocumentStates,
71
            ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> analyzerConfigDocumentStates,
P
Pilchie 已提交
72
            AsyncLazy<VersionStamp> lazyLatestDocumentVersion,
73 74
            AsyncLazy<VersionStamp> lazyLatestDocumentTopLevelChangeVersion,
            ValueSource<AnalyzerConfigSet> lazyAnalyzerConfigSet)
P
Pilchie 已提交
75
        {
76 77
            _solutionServices = solutionServices;
            _languageServices = languageServices;
78 79
            _documentIds = documentIds;
            _additionalDocumentIds = additionalDocumentIds;
80 81
            _documentStates = documentStates;
            _additionalDocumentStates = additionalDocumentStates;
82
            _analyzerConfigDocumentStates = analyzerConfigDocumentStates;
83 84
            _lazyLatestDocumentVersion = lazyLatestDocumentVersion;
            _lazyLatestDocumentTopLevelChangeVersion = lazyLatestDocumentTopLevelChangeVersion;
85
            _lazyAnalyzerConfigSet = lazyAnalyzerConfigSet;
86

87
            // ownership of information on document has moved to project state. clear out documentInfo the state is
N
nnpcYvIVl 已提交
88
            // holding on. otherwise, these information will be held onto unnecessarily by projectInfo even after
89 90 91
            // the info has changed by DocumentState.
            _projectInfo = ClearAllDocumentsFromProjectInfo(projectInfo);

92
            _lazyChecksums = new AsyncLazy<ProjectStateChecksums>(ComputeChecksumsAsync, cacheResult: true);
P
Pilchie 已提交
93 94
        }

95
        public ProjectState(ProjectInfo projectInfo, HostLanguageServices languageServices, SolutionServices solutionServices)
P
Pilchie 已提交
96 97
        {
            Contract.ThrowIfNull(projectInfo);
98
            Contract.ThrowIfNull(languageServices);
P
Pilchie 已提交
99 100
            Contract.ThrowIfNull(solutionServices);

101 102
            _languageServices = languageServices;
            _solutionServices = solutionServices;
P
Pilchie 已提交
103

104
            var projectInfoFixed = FixProjectInfo(projectInfo);
P
Pilchie 已提交
105

106
            // We need to compute our AnalyerConfigDocumentStates first, since we use those to produce our DocumentStates
107 108 109
            _analyzerConfigDocumentStates = ImmutableSortedDictionary.CreateRange(DocumentIdComparer.Instance,
                projectInfoFixed.AnalyzerConfigDocuments.Select(d =>
                    KeyValuePairUtil.Create(d.Id, new AnalyzerConfigDocumentState(d, solutionServices))));
110 111
            _lazyAnalyzerConfigSet = ComputeAnalyzerConfigSetValueSource(_analyzerConfigDocumentStates.Values);

112 113
            _documentIds = projectInfoFixed.Documents.Select(d => d.Id).ToImmutableList();
            _additionalDocumentIds = projectInfoFixed.AdditionalDocuments.Select(d => d.Id).ToImmutableList();
P
Pilchie 已提交
114

115
            var parseOptions = projectInfoFixed.ParseOptions;
116
            var docStates = ImmutableSortedDictionary.CreateRange(DocumentIdComparer.Instance,
117
                projectInfoFixed.Documents.Select(d =>
P
Pilchie 已提交
118
                    new KeyValuePair<DocumentId, DocumentState>(d.Id,
119
                        CreateDocument(d, parseOptions))));
P
Pilchie 已提交
120

121
            _documentStates = docStates;
P
Pilchie 已提交
122

123
            var additionalDocStates = ImmutableSortedDictionary.CreateRange(DocumentIdComparer.Instance,
124
                    projectInfoFixed.AdditionalDocuments.Select(d =>
125
                        new KeyValuePair<DocumentId, TextDocumentState>(d.Id, new TextDocumentState(d, solutionServices))));
126

127 128 129
            _additionalDocumentStates = additionalDocStates;
            _lazyLatestDocumentVersion = new AsyncLazy<VersionStamp>(c => ComputeLatestDocumentVersionAsync(docStates, additionalDocStates, c), cacheResult: true);
            _lazyLatestDocumentTopLevelChangeVersion = new AsyncLazy<VersionStamp>(c => ComputeLatestDocumentTopLevelChangeVersionAsync(docStates, additionalDocStates, c), cacheResult: true);
130

131
            // ownership of information on document has moved to project state. clear out documentInfo the state is
N
nnpcYvIVl 已提交
132
            // holding on. otherwise, these information will be held onto unnecessarily by projectInfo even after
133 134 135 136
            // the info has changed by DocumentState.
            // we hold onto the info so that we don't need to duplicate all information info already has in the state
            _projectInfo = ClearAllDocumentsFromProjectInfo(projectInfoFixed);

137
            _lazyChecksums = new AsyncLazy<ProjectStateChecksums>(ComputeChecksumsAsync, cacheResult: true);
P
Pilchie 已提交
138 139
        }

140 141
        private static ProjectInfo ClearAllDocumentsFromProjectInfo(ProjectInfo projectInfo)
        {
142 143 144 145
            return projectInfo
                .WithDocuments(ImmutableArray<DocumentInfo>.Empty)
                .WithAdditionalDocuments(ImmutableArray<DocumentInfo>.Empty)
                .WithAnalyzerConfigDocuments(ImmutableArray<DocumentInfo>.Empty);
146 147
        }

P
Pilchie 已提交
148 149 150 151
        private ProjectInfo FixProjectInfo(ProjectInfo projectInfo)
        {
            if (projectInfo.CompilationOptions == null)
            {
152
                var compilationFactory = _languageServices.GetService<ICompilationFactoryService>();
153 154 155 156
                if (compilationFactory != null)
                {
                    projectInfo = projectInfo.WithCompilationOptions(compilationFactory.GetDefaultCompilationOptions());
                }
P
Pilchie 已提交
157 158 159 160
            }

            if (projectInfo.ParseOptions == null)
            {
161
                var syntaxTreeFactory = _languageServices.GetService<ISyntaxTreeFactoryService>();
162 163 164 165
                if (syntaxTreeFactory != null)
                {
                    projectInfo = projectInfo.WithParseOptions(syntaxTreeFactory.GetDefaultParseOptions());
                }
P
Pilchie 已提交
166 167 168 169 170
            }

            return projectInfo;
        }

171
        private static async Task<VersionStamp> ComputeLatestDocumentVersionAsync(IImmutableDictionary<DocumentId, DocumentState> documentStates, IImmutableDictionary<DocumentId, TextDocumentState> additionalDocumentStates, CancellationToken cancellationToken)
P
Pilchie 已提交
172 173 174
        {
            // this may produce a version that is out of sync with the actual Document versions.
            var latestVersion = VersionStamp.Default;
175
            foreach (var (_, doc) in documentStates)
P
Pilchie 已提交
176 177 178 179 180 181 182 183 184 185
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (!doc.IsGenerated)
                {
                    var version = await doc.GetTextVersionAsync(cancellationToken).ConfigureAwait(false);
                    latestVersion = version.GetNewerVersion(latestVersion);
                }
            }

186
            foreach (var (_, additionalDoc) in additionalDocumentStates)
187 188 189 190 191 192 193
            {
                cancellationToken.ThrowIfCancellationRequested();

                var version = await additionalDoc.GetTextVersionAsync(cancellationToken).ConfigureAwait(false);
                latestVersion = version.GetNewerVersion(latestVersion);
            }

P
Pilchie 已提交
194 195 196
            return latestVersion;
        }

197
        private AsyncLazy<VersionStamp> CreateLazyLatestDocumentTopLevelChangeVersion(
198
            TextDocumentState newDocument,
199 200
            IImmutableDictionary<DocumentId, DocumentState> newDocumentStates,
            IImmutableDictionary<DocumentId, TextDocumentState> newAdditionalDocumentStates)
P
Pilchie 已提交
201
        {
C
CyrusNajmabadi 已提交
202
            if (_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var oldVersion))
P
Pilchie 已提交
203 204 205 206 207
            {
                return new AsyncLazy<VersionStamp>(c => ComputeTopLevelChangeTextVersionAsync(oldVersion, newDocument, c), cacheResult: true);
            }
            else
            {
208
                return new AsyncLazy<VersionStamp>(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true);
P
Pilchie 已提交
209 210 211
            }
        }

212
        private static async Task<VersionStamp> ComputeTopLevelChangeTextVersionAsync(VersionStamp oldVersion, TextDocumentState newDocument, CancellationToken cancellationToken)
P
Pilchie 已提交
213 214 215 216 217
        {
            var newVersion = await newDocument.GetTopLevelChangeTextVersionAsync(cancellationToken).ConfigureAwait(false);
            return newVersion.GetNewerVersion(oldVersion);
        }

218
        private static async Task<VersionStamp> ComputeLatestDocumentTopLevelChangeVersionAsync(IImmutableDictionary<DocumentId, DocumentState> documentStates, IImmutableDictionary<DocumentId, TextDocumentState> additionalDocumentStates, CancellationToken cancellationToken)
P
Pilchie 已提交
219 220 221
        {
            // this may produce a version that is out of sync with the actual Document versions.
            var latestVersion = VersionStamp.Default;
222
            foreach (var (_, doc) in documentStates)
P
Pilchie 已提交
223 224 225 226 227 228 229
            {
                cancellationToken.ThrowIfCancellationRequested();

                var version = await doc.GetTopLevelChangeTextVersionAsync(cancellationToken).ConfigureAwait(false);
                latestVersion = version.GetNewerVersion(latestVersion);
            }

230
            foreach (var (_, additionalDoc) in additionalDocumentStates)
231 232 233 234 235 236 237
            {
                cancellationToken.ThrowIfCancellationRequested();

                var version = await additionalDoc.GetTopLevelChangeTextVersionAsync(cancellationToken).ConfigureAwait(false);
                latestVersion = version.GetNewerVersion(latestVersion);
            }

P
Pilchie 已提交
238 239 240
            return latestVersion;
        }

241
        internal DocumentState CreateDocument(DocumentInfo documentInfo, ParseOptions? parseOptions)
P
Pilchie 已提交
242
        {
243
            var doc = new DocumentState(documentInfo, parseOptions, _lazyAnalyzerConfigSet, _languageServices, _solutionServices);
P
Pilchie 已提交
244 245 246 247 248 249 250 251

            if (doc.SourceCodeKind != documentInfo.SourceCodeKind)
            {
                doc = doc.UpdateSourceCodeKind(documentInfo.SourceCodeKind);
            }

            return doc;
        }
252

253
        public AnalyzerOptions AnalyzerOptions
254 255 256
            => _lazyAnalyzerOptions ??= new AnalyzerOptions(
                additionalFiles: _additionalDocumentStates.Values.Select(d => new AdditionalTextWithState(d)).ToImmutableArray<AdditionalText>(),
                optionsProvider: new WorkspaceAnalyzerConfigOptionsProvider(this));
257

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
        public ImmutableDictionary<string, ReportDiagnostic> GetAnalyzerConfigSpecialDiagnosticOptions()
        {
            // We need to find the analyzer config options at the root of the project.
            // Currently, there is no compiler API to query analyzer config options for a directory in a language agnostic fashion.
            // So, we use a dummy language-specific file name appended to the project directory to query analyzer config options.

            var projectDirectory = PathUtilities.GetDirectoryName(_projectInfo.FilePath);
            if (!PathUtilities.IsAbsolute(projectDirectory))
            {
                return ImmutableDictionary<string, ReportDiagnostic>.Empty;
            }

            var fileName = Guid.NewGuid().ToString();
            string sourceFilePath;
            switch (_projectInfo.Language)
            {
                case LanguageNames.CSharp:
275 276
                    // Suppression should be removed or addressed https://github.com/dotnet/roslyn/issues/41636
                    sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.cs")!;
277 278 279
                    break;

                case LanguageNames.VisualBasic:
280 281
                    // Suppression should be removed or addressed https://github.com/dotnet/roslyn/issues/41636
                    sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.vb")!;
282 283 284 285 286 287 288 289 290
                    break;

                default:
                    return ImmutableDictionary<string, ReportDiagnostic>.Empty;
            }

            return _lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(sourceFilePath).TreeOptions;
        }

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
        private sealed class WorkspaceAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
        {
            private readonly ProjectState _projectState;

            public WorkspaceAnalyzerConfigOptionsProvider(ProjectState projectState)
            {
                _projectState = projectState;
            }

            public override AnalyzerConfigOptions GetOptions(SyntaxTree tree)
            {
                return new WorkspaceAnalyzerConfigOptions(_projectState._lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(tree.FilePath));
            }

            public override AnalyzerConfigOptions GetOptions(AdditionalText textFile)
            {
                // TODO: correctly find the file path, since it looks like we give this the document's .Name under the covers if we don't have one
                return new WorkspaceAnalyzerConfigOptions(_projectState._lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(textFile.Path));
            }

            // PROTOTYPE: why isn't this just a provided implementation?
            private sealed class WorkspaceAnalyzerConfigOptions : AnalyzerConfigOptions
            {
314
                private readonly ImmutableDictionary<string, string> _backing;
315 316 317

                public WorkspaceAnalyzerConfigOptions(AnalyzerConfigOptionsResult analyzerConfigOptions)
                {
318
                    _backing = analyzerConfigOptions.AnalyzerOptions;
319 320
                }

321
                public override bool TryGetValue(string key, out string value) => _backing.TryGetValue(key, out value);
322 323 324 325
            }
        }

        private static ValueSource<AnalyzerConfigSet> ComputeAnalyzerConfigSetValueSource(IEnumerable<AnalyzerConfigDocumentState> analyzerConfigDocumentStates)
326
        {
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
            return new AsyncLazy<AnalyzerConfigSet>(
                asynchronousComputeFunction: async cancellationToken =>
                {
                    var tasks = analyzerConfigDocumentStates.Select(a => a.GetAnalyzerConfigAsync(cancellationToken));
                    var analyzerConfigs = await Task.WhenAll(tasks).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();

                    return AnalyzerConfigSet.Create(analyzerConfigs);
                },
                synchronousComputeFunction: cancellationToken =>
                {
                    var analyzerConfigs = analyzerConfigDocumentStates.SelectAsArray(a => a.GetAnalyzerConfig(cancellationToken));
                    return AnalyzerConfigSet.Create(analyzerConfigs);
                },
                cacheResult: true);
343 344
        }

P
Pilchie 已提交
345 346
        public Task<VersionStamp> GetLatestDocumentVersionAsync(CancellationToken cancellationToken)
        {
347
            return _lazyLatestDocumentVersion.GetValueAsync(cancellationToken);
P
Pilchie 已提交
348 349 350 351
        }

        public Task<VersionStamp> GetLatestDocumentTopLevelChangeVersionAsync(CancellationToken cancellationToken)
        {
352
            return _lazyLatestDocumentTopLevelChangeVersion.GetValueAsync(cancellationToken);
P
Pilchie 已提交
353 354
        }

C
CyrusNajmabadi 已提交
355
        public async Task<VersionStamp> GetSemanticVersionAsync(CancellationToken cancellationToken = default)
P
Pilchie 已提交
356 357 358 359 360 361
        {
            var docVersion = await this.GetLatestDocumentTopLevelChangeVersionAsync(cancellationToken).ConfigureAwait(false);
            return docVersion.GetNewerVersion(this.Version);
        }

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
362
        public ProjectId Id => this.ProjectInfo.Id;
P
Pilchie 已提交
363 364

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
365
        public string? FilePath => this.ProjectInfo.FilePath;
P
Pilchie 已提交
366 367

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
368
        public string? OutputFilePath => this.ProjectInfo.OutputFilePath;
P
Pilchie 已提交
369

370
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
371
        public string? OutputRefFilePath => this.ProjectInfo.OutputRefFilePath;
372

373
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
374
        public string? DefaultNamespace => this.ProjectInfo.DefaultNamespace;
375

P
Pilchie 已提交
376
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
377
        public HostLanguageServices LanguageServices => _languageServices;
P
Pilchie 已提交
378

379 380 381
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public string Language => LanguageServices.Language;

P
Pilchie 已提交
382
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
383
        public string Name => this.ProjectInfo.Name;
P
Pilchie 已提交
384

385
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
386
        public bool IsSubmission => this.ProjectInfo.IsSubmission;
387

P
Pilchie 已提交
388
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
389
        public Type? HostObjectType => this.ProjectInfo.HostObjectType;
P
Pilchie 已提交
390

391
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
392
        public bool SupportsCompilation => this.LanguageServices.GetService<ICompilationFactoryService>() != null;
393

P
Pilchie 已提交
394
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
395
        public VersionStamp Version => this.ProjectInfo.Version;
P
Pilchie 已提交
396 397

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
398
        public ProjectInfo ProjectInfo => _projectInfo;
P
Pilchie 已提交
399 400

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
401
        public string AssemblyName => this.ProjectInfo.AssemblyName;
P
Pilchie 已提交
402

403
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
404
        public CompilationOptions? CompilationOptions => this.ProjectInfo.CompilationOptions;
405

P
Pilchie 已提交
406
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
407
        public ParseOptions? ParseOptions => this.ProjectInfo.ParseOptions;
P
Pilchie 已提交
408

409
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
410 411 412 413 414 415 416 417 418 419 420
        public IReadOnlyList<MetadataReference> MetadataReferences => this.ProjectInfo.MetadataReferences;

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public IReadOnlyList<AnalyzerReference> AnalyzerReferences => this.ProjectInfo.AnalyzerReferences;

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public IReadOnlyList<ProjectReference> ProjectReferences => this.ProjectInfo.ProjectReferences;

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public bool HasAllInformation => this.ProjectInfo.HasAllInformation;

421 422 423
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public bool RunAnalyzers => this.ProjectInfo.RunAnalyzers;

424 425 426 427
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public bool HasDocuments => _documentIds.Count > 0;

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
428
        public IEnumerable<DocumentState> OrderedDocumentStates => this.DocumentIds.Select(GetDocumentState)!;
429 430 431 432 433 434 435

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public IReadOnlyList<DocumentId> DocumentIds => _documentIds;

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        public IReadOnlyList<DocumentId> AdditionalDocumentIds => _additionalDocumentIds;

436 437 438 439 440
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
        // Regular documents and additionald documents have an ordering, and so we maintain lists of the IDs in order; in the case of analyzerconfig documents,
        // we don't define a workspace ordering because they are ordered via fancier algorithms in the compiler based on directory depth.
        public IEnumerable<DocumentId> AnalyzerConfigDocumentIds => _analyzerConfigDocumentStates.Keys;

441
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
442
        public ImmutableSortedDictionary<DocumentId, DocumentState> DocumentStates => _documentStates;
443 444

        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
445
        public ImmutableSortedDictionary<DocumentId, TextDocumentState> AdditionalDocumentStates => _additionalDocumentStates;
446

447
        [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
448
        public ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> AnalyzerConfigDocumentStates => _analyzerConfigDocumentStates;
449

P
Pilchie 已提交
450 451
        public bool ContainsDocument(DocumentId documentId)
        {
452
            return _documentStates.ContainsKey(documentId);
P
Pilchie 已提交
453 454
        }

455 456
        public bool ContainsAdditionalDocument(DocumentId documentId)
        {
457
            return _additionalDocumentStates.ContainsKey(documentId);
458 459
        }

460 461 462 463 464
        public bool ContainsAnalyzerConfigDocument(DocumentId documentId)
        {
            return _analyzerConfigDocumentStates.ContainsKey(documentId);
        }

465
        public DocumentState? GetDocumentState(DocumentId documentId)
P
Pilchie 已提交
466
        {
C
CyrusNajmabadi 已提交
467
            _documentStates.TryGetValue(documentId, out var state);
P
Pilchie 已提交
468 469 470
            return state;
        }

471
        public TextDocumentState? GetAdditionalDocumentState(DocumentId documentId)
472
        {
C
CyrusNajmabadi 已提交
473
            _additionalDocumentStates.TryGetValue(documentId, out var state);
474 475 476
            return state;
        }

477
        public AnalyzerConfigDocumentState? GetAnalyzerConfigDocumentState(DocumentId documentId)
478 479 480 481 482
        {
            _analyzerConfigDocumentStates.TryGetValue(documentId, out var state);
            return state;
        }

P
Pilchie 已提交
483
        private ProjectState With(
484 485 486 487 488 489 490 491 492
            ProjectInfo? projectInfo = null,
            ImmutableList<DocumentId>? documentIds = null,
            ImmutableList<DocumentId>? additionalDocumentIds = null,
            ImmutableSortedDictionary<DocumentId, DocumentState>? documentStates = null,
            ImmutableSortedDictionary<DocumentId, TextDocumentState>? additionalDocumentStates = null,
            ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState>? analyzerConfigDocumentStates = null,
            AsyncLazy<VersionStamp>? latestDocumentVersion = null,
            AsyncLazy<VersionStamp>? latestDocumentTopLevelChangeVersion = null,
            ValueSource<AnalyzerConfigSet>? analyzerConfigSet = null)
P
Pilchie 已提交
493 494
        {
            return new ProjectState(
495 496 497
                projectInfo ?? _projectInfo,
                _languageServices,
                _solutionServices,
498 499
                documentIds ?? _documentIds,
                additionalDocumentIds ?? _additionalDocumentIds,
500 501
                documentStates ?? _documentStates,
                additionalDocumentStates ?? _additionalDocumentStates,
502
                analyzerConfigDocumentStates ?? _analyzerConfigDocumentStates,
503
                latestDocumentVersion ?? _lazyLatestDocumentVersion,
504 505
                latestDocumentTopLevelChangeVersion ?? _lazyLatestDocumentTopLevelChangeVersion,
                analyzerConfigSet ?? _lazyAnalyzerConfigSet);
P
Pilchie 已提交
506 507
        }

508 509 510 511 512 513 514 515 516 517
        public ProjectState UpdateName(string name)
        {
            if (name == this.Name)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithName(name).WithVersion(this.Version.GetNewerVersion()));
        }

518
        public ProjectState UpdateFilePath(string? filePath)
519 520 521 522 523 524 525 526 527
        {
            if (filePath == this.FilePath)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithFilePath(filePath).WithVersion(this.Version.GetNewerVersion()));
        }

P
Pilchie 已提交
528 529 530 531 532 533 534 535 536 537
        public ProjectState UpdateAssemblyName(string assemblyName)
        {
            if (assemblyName == this.AssemblyName)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithAssemblyName(assemblyName).WithVersion(this.Version.GetNewerVersion()));
        }

538
        public ProjectState UpdateOutputFilePath(string? outputFilePath)
P
Pilchie 已提交
539 540 541 542 543 544 545 546 547
        {
            if (outputFilePath == this.OutputFilePath)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithOutputFilePath(outputFilePath).WithVersion(this.Version.GetNewerVersion()));
        }

548
        public ProjectState UpdateOutputRefFilePath(string? outputRefFilePath)
549 550 551 552 553 554
        {
            if (outputRefFilePath == this.OutputRefFilePath)
            {
                return this;
            }

555
            return this.With(projectInfo: this.ProjectInfo.WithOutputRefFilePath(outputRefFilePath).WithVersion(this.Version.GetNewerVersion()));
556 557
        }

558
        public ProjectState UpdateDefaultNamespace(string? defaultNamespace)
559 560 561 562 563 564 565 566 567
        {
            if (defaultNamespace == this.DefaultNamespace)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithDefaultNamespace(defaultNamespace).WithVersion(this.Version.GetNewerVersion()));
        }

P
Pilchie 已提交
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
        public ProjectState UpdateCompilationOptions(CompilationOptions options)
        {
            if (options == this.CompilationOptions)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithCompilationOptions(options).WithVersion(this.Version.GetNewerVersion()));
        }

        public ProjectState UpdateParseOptions(ParseOptions options)
        {
            if (options == this.ParseOptions)
            {
                return this;
            }

            // update parse options for all documents too
586
            var docMap = _documentStates;
P
Pilchie 已提交
587

588
            foreach (var (docId, oldDocState) in _documentStates)
P
Pilchie 已提交
589 590 591 592 593 594 595 596 597 598
            {
                var newDocState = oldDocState.UpdateParseOptions(options);
                docMap = docMap.SetItem(docId, newDocState);
            }

            return this.With(
                projectInfo: this.ProjectInfo.WithParseOptions(options).WithVersion(this.Version.GetNewerVersion()),
                documentStates: docMap);
        }

599 600 601 602 603 604 605 606 607 608
        public ProjectState UpdateHasAllInformation(bool hasAllInformation)
        {
            if (hasAllInformation == this.HasAllInformation)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithHasAllInformation(hasAllInformation).WithVersion(this.Version.GetNewerVersion()));
        }

609 610 611 612 613 614 615 616 617 618
        public ProjectState UpdateRunAnalyzers(bool runAnalyzers)
        {
            if (runAnalyzers == this.RunAnalyzers)
            {
                return this;
            }

            return this.With(projectInfo: this.ProjectInfo.WithRunAnalyzers(runAnalyzers).WithVersion(this.Version.GetNewerVersion()));
        }

P
Pilchie 已提交
619 620 621 622 623
        public static bool IsSameLanguage(ProjectState project1, ProjectState project2)
        {
            return project1.LanguageServices == project2.LanguageServices;
        }

S
Sam Harwell 已提交
624 625 626 627 628 629
        /// <summary>
        /// Determines whether <see cref="ProjectReferences"/> contains a reference to a specified project.
        /// </summary>
        /// <param name="projectId">The target project of the reference.</param>
        /// <returns><see langword="true"/> if this project references <paramref name="projectId"/>; otherwise, <see langword="false"/>.</returns>
        public bool ContainsReferenceToProject(ProjectId projectId)
630 631 632 633 634 635 636 637 638 639
        {
            foreach (var projectReference in ProjectReferences)
            {
                if (projectReference.ProjectId == projectId)
                    return true;
            }

            return false;
        }

P
Pilchie 已提交
640 641
        public ProjectState RemoveProjectReference(ProjectReference projectReference)
        {
642
            Debug.Assert(this.ProjectReferences.Contains(projectReference));
P
Pilchie 已提交
643 644

            return this.With(
645
                projectInfo: this.ProjectInfo.WithProjectReferences(this.ProjectReferences.ToImmutableArray().Remove(projectReference)).WithVersion(this.Version.GetNewerVersion()));
P
Pilchie 已提交
646 647 648 649 650 651 652
        }

        public ProjectState AddProjectReferences(IEnumerable<ProjectReference> projectReferences)
        {
            var newProjectRefs = this.ProjectReferences;
            foreach (var projectReference in projectReferences)
            {
653
                Debug.Assert(!newProjectRefs.Contains(projectReference));
654
                newProjectRefs = newProjectRefs.ToImmutableArray().Add(projectReference);
P
Pilchie 已提交
655 656 657 658 659 660 661 662 663
            }

            return this.With(
                projectInfo: this.ProjectInfo.WithProjectReferences(newProjectRefs).WithVersion(this.Version.GetNewerVersion()));
        }

        public ProjectState WithProjectReferences(IEnumerable<ProjectReference> projectReferences)
        {
            return this.With(
664
                projectInfo: this.ProjectInfo.WithProjectReferences(projectReferences).WithVersion(this.Version.GetNewerVersion()));
P
Pilchie 已提交
665 666 667 668
        }

        public ProjectState AddMetadataReference(MetadataReference toMetadata)
        {
669
            Debug.Assert(!this.MetadataReferences.Contains(toMetadata));
P
Pilchie 已提交
670 671

            return this.With(
672
                projectInfo: this.ProjectInfo.WithMetadataReferences(this.MetadataReferences.ToImmutableArray().Add(toMetadata)).WithVersion(this.Version.GetNewerVersion()));
P
Pilchie 已提交
673 674 675 676
        }

        public ProjectState RemoveMetadataReference(MetadataReference toMetadata)
        {
677
            Debug.Assert(this.MetadataReferences.Contains(toMetadata));
P
Pilchie 已提交
678 679

            return this.With(
680
                projectInfo: this.ProjectInfo.WithMetadataReferences(this.MetadataReferences.ToImmutableArray().Remove(toMetadata)).WithVersion(this.Version.GetNewerVersion()));
P
Pilchie 已提交
681 682 683 684 685 686 687
        }

        public ProjectState AddMetadataReferences(IEnumerable<MetadataReference> metadataReferences)
        {
            var newMetaRefs = this.MetadataReferences;
            foreach (var metadataReference in metadataReferences)
            {
688
                Debug.Assert(!newMetaRefs.Contains(metadataReference));
689
                newMetaRefs = newMetaRefs.ToImmutableArray().Add(metadataReference);
P
Pilchie 已提交
690 691 692 693 694 695 696 697 698
            }

            return this.With(
                projectInfo: this.ProjectInfo.WithMetadataReferences(newMetaRefs).WithVersion(this.Version.GetNewerVersion()));
        }

        public ProjectState WithMetadataReferences(IEnumerable<MetadataReference> metadataReferences)
        {
            return this.With(
699
                projectInfo: this.ProjectInfo.WithMetadataReferences(metadataReferences).WithVersion(this.Version.GetNewerVersion()));
P
Pilchie 已提交
700 701
        }

702
        public ProjectState AddAnalyzerReference(AnalyzerReference analyzerReference)
703
        {
704
            Debug.Assert(!this.AnalyzerReferences.Contains(analyzerReference));
705 706

            return this.With(
707
                projectInfo: this.ProjectInfo.WithAnalyzerReferences(this.AnalyzerReferences.ToImmutableArray().Add(analyzerReference)).WithVersion(this.Version.GetNewerVersion()));
708 709
        }

710
        public ProjectState RemoveAnalyzerReference(AnalyzerReference analyzerReference)
711
        {
712
            Debug.Assert(this.AnalyzerReferences.Contains(analyzerReference));
713 714

            return this.With(
715
                projectInfo: this.ProjectInfo.WithAnalyzerReferences(this.AnalyzerReferences.ToImmutableArray().Remove(analyzerReference)).WithVersion(this.Version.GetNewerVersion()));
716 717
        }

718
        public ProjectState AddAnalyzerReferences(IEnumerable<AnalyzerReference> analyzerReferences)
719
        {
720 721
            var newAnalyzerReferences = this.AnalyzerReferences;
            foreach (var analyzerReference in analyzerReferences)
722
            {
723
                Debug.Assert(!newAnalyzerReferences.Contains(analyzerReference));
724
                newAnalyzerReferences = newAnalyzerReferences.ToImmutableArray().Add(analyzerReference);
725 726 727
            }

            return this.With(
728
                projectInfo: this.ProjectInfo.WithAnalyzerReferences(newAnalyzerReferences).WithVersion(this.Version.GetNewerVersion()));
729 730
        }

731
        public ProjectState WithAnalyzerReferences(IEnumerable<AnalyzerReference> analyzerReferences)
732 733
        {
            return this.With(
734
                projectInfo: this.ProjectInfo.WithAnalyzerReferences(analyzerReferences).WithVersion(this.Version.GetNewerVersion()));
735 736
        }

737
        public ProjectState AddDocuments(ImmutableArray<DocumentState> documents)
P
Pilchie 已提交
738
        {
739
            Debug.Assert(!documents.Any(d => this.DocumentStates.ContainsKey(d.Id)));
P
Pilchie 已提交
740 741

            return this.With(
742
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
743 744
                documentIds: _documentIds.AddRange(documents.Select(d => d.Id)),
                documentStates: _documentStates.AddRange(documents.Select(d => KeyValuePairUtil.Create(d.Id, d))));
P
Pilchie 已提交
745 746
        }

747
        public ProjectState AddAdditionalDocuments(ImmutableArray<TextDocumentState> documents)
748
        {
749
            Debug.Assert(!documents.Any(d => this.AdditionalDocumentStates.ContainsKey(d.Id)));
750 751

            return this.With(
752
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
753 754
                additionalDocumentIds: _additionalDocumentIds.AddRange(documents.Select(d => d.Id)),
                additionalDocumentStates: _additionalDocumentStates.AddRange(documents.Select(d => KeyValuePairUtil.Create(d.Id, d))));
755 756
        }

757 758 759 760
        public ProjectState AddAnalyzerConfigDocuments(ImmutableArray<AnalyzerConfigDocumentState> documents)
        {
            Debug.Assert(!documents.Any(d => this._analyzerConfigDocumentStates.ContainsKey(d.Id)));

761 762 763 764 765 766 767 768 769 770 771 772 773
            var newAnalyzerConfigDocumentStates = _analyzerConfigDocumentStates.AddRange(documents.Select(d => KeyValuePairUtil.Create(d.Id, d)));

            return CreateNewStateForChangedAnalyzerConfigDocuments(newAnalyzerConfigDocumentStates);
        }

        private ProjectState CreateNewStateForChangedAnalyzerConfigDocuments(ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> newAnalyzerConfigDocumentStates)
        {
            var newAnalyzerConfigSet = ComputeAnalyzerConfigSetValueSource(newAnalyzerConfigDocumentStates.Values);

            // The addition of any .editorconfig can modify the diagnostic reporting options that are on
            // a specific syntax tree; therefore we must update all our syntax trees.
            var docMap = _documentStates;

774
            foreach (var (docId, oldDocState) in _documentStates)
775 776 777 778 779
            {
                var newDocState = oldDocState.UpdateAnalyzerConfigSet(newAnalyzerConfigSet);
                docMap = docMap.SetItem(docId, newDocState);
            }

780 781
            return this.With(
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
782 783 784
                analyzerConfigDocumentStates: newAnalyzerConfigDocumentStates,
                documentStates: docMap,
                analyzerConfigSet: newAnalyzerConfigSet);
785 786
        }

787
        public ProjectState RemoveDocuments(ImmutableArray<DocumentId> documentIds)
P
Pilchie 已提交
788 789
        {
            return this.With(
790
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
791 792
                documentIds: _documentIds.RemoveRange(documentIds),
                documentStates: _documentStates.RemoveRange(documentIds));
P
Pilchie 已提交
793 794
        }

795
        public ProjectState RemoveAdditionalDocuments(ImmutableArray<DocumentId> documentIds)
796 797
        {
            return this.With(
798
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
799 800
                additionalDocumentIds: _additionalDocumentIds.RemoveRange(documentIds),
                additionalDocumentStates: _additionalDocumentStates.RemoveRange(documentIds));
801 802
        }

803 804
        public ProjectState RemoveAnalyzerConfigDocument(DocumentId documentId)
        {
805
            Debug.Assert(_analyzerConfigDocumentStates.ContainsKey(documentId));
806

807 808 809
            var newAnalyzerConfigDocumentStates = _analyzerConfigDocumentStates.Remove(documentId);

            return CreateNewStateForChangedAnalyzerConfigDocuments(newAnalyzerConfigDocumentStates);
810 811
        }

P
Pilchie 已提交
812 813 814
        public ProjectState RemoveAllDocuments()
        {
            return this.With(
815
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()).WithDocuments(SpecializedCollections.EmptyEnumerable<DocumentInfo>()),
816
                documentIds: ImmutableList<DocumentId>.Empty,
817
                documentStates: ImmutableSortedDictionary.Create<DocumentId, DocumentState>(DocumentIdComparer.Instance));
P
Pilchie 已提交
818 819 820 821
        }

        public ProjectState UpdateDocument(DocumentState newDocument, bool textChanged, bool recalculateDependentVersions)
        {
822
            Debug.Assert(this.ContainsDocument(newDocument.Id));
P
Pilchie 已提交
823

824
            var oldDocument = this.GetDocumentState(newDocument.Id)!;
P
Pilchie 已提交
825 826 827 828 829
            if (oldDocument == newDocument)
            {
                return this;
            }

830
            var newDocumentStates = _documentStates.SetItem(newDocument.Id, newDocument);
P
Pilchie 已提交
831
            GetLatestDependentVersions(
832
                newDocumentStates, _additionalDocumentStates, oldDocument, newDocument, recalculateDependentVersions, textChanged,
C
CyrusNajmabadi 已提交
833
                out var dependentDocumentVersion, out var dependentSemanticVersion);
P
Pilchie 已提交
834 835 836 837 838 839 840

            return this.With(
                documentStates: newDocumentStates,
                latestDocumentVersion: dependentDocumentVersion,
                latestDocumentTopLevelChangeVersion: dependentSemanticVersion);
        }

841 842
        public ProjectState UpdateAdditionalDocument(TextDocumentState newDocument, bool textChanged, bool recalculateDependentVersions)
        {
843
            Debug.Assert(this.ContainsAdditionalDocument(newDocument.Id));
844

845
            var oldDocument = this.GetAdditionalDocumentState(newDocument.Id)!;
846 847 848 849 850
            if (oldDocument == newDocument)
            {
                return this;
            }

851
            var newDocumentStates = _additionalDocumentStates.SetItem(newDocument.Id, newDocument);
852
            GetLatestDependentVersions(
853
                _documentStates, newDocumentStates, oldDocument, newDocument, recalculateDependentVersions, textChanged,
C
CyrusNajmabadi 已提交
854
                out var dependentDocumentVersion, out var dependentSemanticVersion);
855 856 857 858 859 860 861

            return this.With(
                additionalDocumentStates: newDocumentStates,
                latestDocumentVersion: dependentDocumentVersion,
                latestDocumentTopLevelChangeVersion: dependentSemanticVersion);
        }

862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
        public ProjectState UpdateAnalyzerConfigDocument(AnalyzerConfigDocumentState newDocument, bool textChanged, bool recalculateDependentVersions)
        {
            Debug.Assert(this.ContainsAnalyzerConfigDocument(newDocument.Id));

            var oldDocument = this.GetAnalyzerConfigDocumentState(newDocument.Id);
            if (oldDocument == newDocument)
            {
                return this;
            }

            var newDocumentStates = _analyzerConfigDocumentStates.SetItem(newDocument.Id, newDocument);

            return CreateNewStateForChangedAnalyzerConfigDocuments(newDocumentStates);
        }

877 878 879 880 881 882 883 884 885 886 887 888
        public ProjectState UpdateDocumentsOrder(ImmutableList<DocumentId> documentIds)
        {
            if (documentIds.IsEmpty)
            {
                throw new ArgumentOutOfRangeException("The specified documents are empty.", nameof(documentIds));
            }

            if (documentIds.Count != _documentIds.Count)
            {
                throw new ArgumentException($"The specified documents do not equal the project document count.", nameof(documentIds));
            }

T
TIHan 已提交
889 890 891
            var hasOrderChanged = false;

            for (var i = 0; i < documentIds.Count; ++i)
892
            {
T
TIHan 已提交
893 894 895
                var documentId = documentIds[i];

                if (!ContainsDocument(documentId))
896 897 898
                {
                    throw new InvalidOperationException($"The document '{documentId}' does not exist in the project.");
                }
T
TIHan 已提交
899 900 901 902 903 904 905 906 907 908

                if (DocumentIds[i] != documentId)
                {
                    hasOrderChanged = true;
                }
            }

            if (!hasOrderChanged)
            {
                return this;
909 910 911
            }

            return this.With(
T
TIHan 已提交
912
                projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
913 914 915
                documentIds: documentIds);
        }

P
Pilchie 已提交
916
        private void GetLatestDependentVersions(
917 918
            IImmutableDictionary<DocumentId, DocumentState> newDocumentStates,
            IImmutableDictionary<DocumentId, TextDocumentState> newAdditionalDocumentStates,
919
            TextDocumentState oldDocument, TextDocumentState newDocument,
P
Pilchie 已提交
920 921 922 923 924 925 926 927
            bool recalculateDependentVersions, bool textChanged,
            out AsyncLazy<VersionStamp> dependentDocumentVersion, out AsyncLazy<VersionStamp> dependentSemanticVersion)
        {
            var recalculateDocumentVersion = false;
            var recalculateSemanticVersion = false;

            if (recalculateDependentVersions)
            {
C
CyrusNajmabadi 已提交
928
                if (oldDocument.TryGetTextVersion(out var oldVersion))
P
Pilchie 已提交
929
                {
C
CyrusNajmabadi 已提交
930
                    if (!_lazyLatestDocumentVersion.TryGetValue(out var documentVersion) || documentVersion == oldVersion)
P
Pilchie 已提交
931 932 933 934
                    {
                        recalculateDocumentVersion = true;
                    }

C
CyrusNajmabadi 已提交
935
                    if (!_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var semanticVersion) || semanticVersion == oldVersion)
P
Pilchie 已提交
936 937 938 939 940 941 942
                    {
                        recalculateSemanticVersion = true;
                    }
                }
            }

            dependentDocumentVersion = recalculateDocumentVersion ?
943
                new AsyncLazy<VersionStamp>(c => ComputeLatestDocumentVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true) :
P
Pilchie 已提交
944 945
                textChanged ?
                    new AsyncLazy<VersionStamp>(newDocument.GetTextVersionAsync, cacheResult: true) :
946
                    _lazyLatestDocumentVersion;
P
Pilchie 已提交
947 948

            dependentSemanticVersion = recalculateSemanticVersion ?
949
                new AsyncLazy<VersionStamp>(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true) :
P
Pilchie 已提交
950
                textChanged ?
951
                    CreateLazyLatestDocumentTopLevelChangeVersion(newDocument, newDocumentStates, newAdditionalDocumentStates) :
952
                    _lazyLatestDocumentTopLevelChangeVersion;
P
Pilchie 已提交
953
        }
954 955 956 957 958 959 960 961 962 963 964 965 966 967

        private sealed class DocumentIdComparer : IComparer<DocumentId>
        {
            public static IComparer<DocumentId> Instance = new DocumentIdComparer();

            private DocumentIdComparer()
            {
            }

            public int Compare(DocumentId x, DocumentId y)
            {
                return x.Id.CompareTo(y.Id);
            }
        }
P
Pilchie 已提交
968 969
    }
}