private static bool SpanStillValid(NavigationBarModel model, ITextSnapshot snapshot, CancellationToken cancellationToken)
        {
            // even if semantic version is same, portion of text could have been copied & pasted with
            // exact same top level content.
            // go through spans to see whether this happened.
            //
            // paying cost of moving spans forward shouldn't be matter since we need to pay that
            // price soon or later to figure out selected item.
            foreach (var type in model.Types)
            {
                if (!SpanStillValid(type.TrackingSpans, snapshot))
                {
                    return(false);
                }

                foreach (var member in type.ChildItems)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    if (!SpanStillValid(member.TrackingSpans, snapshot))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Computes a model for the given snapshot.
        /// </summary>
        private async Task <NavigationBarModel> ComputeModelAsync(Document document, ITextSnapshot snapshot, CancellationToken cancellationToken)
        {
            // TODO: remove .FirstOrDefault()
            var languageService = document.Project.LanguageServices.GetService <INavigationBarItemService>();

            if (languageService != null)
            {
                // check whether we can re-use lastCompletedModel. otherwise, update lastCompletedModel here.
                // the model should be only updated here
                if (_lastCompletedModel != null)
                {
                    var semanticVersion = await document.Project.GetDependentSemanticVersionAsync(CancellationToken.None).ConfigureAwait(false);

                    if (_lastCompletedModel.SemanticVersionStamp == semanticVersion && SpanStillValid(_lastCompletedModel, snapshot, cancellationToken))
                    {
                        // it looks like we can re-use previous model
                        return(_lastCompletedModel);
                    }
                }

                using (Logger.LogBlock(FunctionId.NavigationBar_ComputeModelAsync, cancellationToken))
                {
                    var items = await languageService.GetItemsAsync(document, cancellationToken).ConfigureAwait(false);

                    if (items != null)
                    {
                        items.Do(i => i.InitializeTrackingSpans(snapshot));
                        var version = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);

                        _lastCompletedModel = new NavigationBarModel(items, version, languageService);
                        return(_lastCompletedModel);
                    }
                }
            }

            _lastCompletedModel = _lastCompletedModel ??
                                  new NavigationBarModel(SpecializedCollections.EmptyList <NavigationBarItem>(), new VersionStamp(), null);
            return(_lastCompletedModel);
        }
        internal static NavigationBarSelectedTypeAndMember ComputeSelectedTypeAndMember(NavigationBarModel model, SnapshotPoint caretPosition, CancellationToken cancellationToken)
        {
            var leftItem = GetMatchingItem(model.Types, caretPosition, model.ItemService, cancellationToken);

            if (leftItem.item == null)
            {
                // Nothing to show at all
                return(new NavigationBarSelectedTypeAndMember(null, null));
            }

            var rightItem = GetMatchingItem(leftItem.item.ChildItems, caretPosition, model.ItemService, cancellationToken);

            return(new NavigationBarSelectedTypeAndMember(leftItem.item, leftItem.gray, rightItem.item, rightItem.gray));
        }
        /// <summary>
        /// Computes a model for the given snapshot.
        /// </summary>
        private async Task<NavigationBarModel> ComputeModelAsync(Document document, ITextSnapshot snapshot, CancellationToken cancellationToken)
        {
            // TODO: remove .FirstOrDefault()
            var languageService = document.Project.LanguageServices.GetService<INavigationBarItemService>();
            if (languageService != null)
            {
                // check whether we can re-use lastCompletedModel. otherwise, update lastCompletedModel here.
                // the model should be only updated here
                if (_lastCompletedModel != null)
                {
                    var semanticVersion = await document.Project.GetDependentSemanticVersionAsync(CancellationToken.None).ConfigureAwait(false);
                    if (_lastCompletedModel.SemanticVersionStamp == semanticVersion && SpanStillValid(_lastCompletedModel, snapshot, cancellationToken))
                    {
                        // it looks like we can re-use previous model
                        return _lastCompletedModel;
                    }
                }

                using (Logger.LogBlock(FunctionId.NavigationBar_ComputeModelAsync, cancellationToken))
                {
                    var items = await languageService.GetItemsAsync(document, cancellationToken).ConfigureAwait(false);
                    if (items != null)
                    {
                        items.Do(i => i.InitializeTrackingSpans(snapshot));
                        var version = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);

                        _lastCompletedModel = new NavigationBarModel(items, version, languageService);
                        return _lastCompletedModel;
                    }
                }
            }

            _lastCompletedModel = _lastCompletedModel ??
                    new NavigationBarModel(SpecializedCollections.EmptyList<NavigationBarItem>(), new VersionStamp(), null);
            return _lastCompletedModel;
        }
        internal static NavigationBarSelectedTypeAndMember ComputeSelectedTypeAndMember(NavigationBarModel model, SnapshotPoint caretPosition, CancellationToken cancellationToken)
        {
            var leftItem = GetMatchingItem(model.Types, caretPosition, model.ItemService, cancellationToken);

            if (leftItem.item == null)
            {
                // Nothing to show at all
                return new NavigationBarSelectedTypeAndMember(null, null);
            }

            var rightItem = GetMatchingItem(leftItem.item.ChildItems, caretPosition, model.ItemService, cancellationToken);

            return new NavigationBarSelectedTypeAndMember(leftItem.item, leftItem.gray, rightItem.item, rightItem.gray);
        }