private static async Task <(Data existingData, Data newData)> ComputeAndPersistTodoCommentsAsync( Document document, TodoCommentState state, TodoCommentTokens todoCommentTokens, CancellationToken cancellationToken) { // use tree version so that things like compiler option changes are considered var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false); if (existingData != null) { // check whether we can use the data as it is (can happen when re-using persisted data from previous VS session) if (CheckVersions(document, textVersion, syntaxVersion, existingData)) { return(existingData, existingData); } } var tokens = todoCommentTokens.GetTokens(document); var comments = await GetTodoCommentsAsync(document, tokens, cancellationToken).ConfigureAwait(false); var items = await CreateItemsAsync(document, comments, cancellationToken).ConfigureAwait(false); var data = new Data(textVersion, syntaxVersion, items); await state.PersistAsync(document, data, cancellationToken).ConfigureAwait(false); return(existingData, data); }
public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) { // it has an assumption that this will not be called concurrently for same document. // in fact, in current design, it won't be even called concurrently for different documents. // but, can be called concurrently for different documents in future if we choose to. Contract.ThrowIfFalse(document.IsFromPrimaryBranch()); if (!document.Project.Solution.Options.GetOption(InternalFeatureOnOffOptions.TodoComments)) { return; } // use tree version so that things like compiler option changes are considered var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); var existingData = await _state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false); if (existingData != null) { // check whether we can use the data as it is (can happen when re-using persisted data from previous VS session) if (CheckVersions(document, textVersion, syntaxVersion, existingData)) { Contract.Requires(_workspace == document.Project.Solution.Workspace); RaiseTaskListUpdated(_workspace, document.Project.Solution, document.Id, existingData.Items); return; } } var service = document.GetLanguageService <ITodoCommentService>(); if (service == null) { return; } var tokens = _todoCommentTokens.GetTokens(document, cancellationToken); var comments = await service.GetTodoCommentsAsync(document, tokens, cancellationToken).ConfigureAwait(false); var items = await CreateItemsAsync(document, comments, cancellationToken).ConfigureAwait(false); var data = new Data(textVersion, syntaxVersion, items); await _state.PersistAsync(document, data, cancellationToken).ConfigureAwait(false); // * NOTE * cancellation can't throw after this point. if (existingData == null || existingData.Items.Length > 0 || data.Items.Length > 0) { Contract.Requires(_workspace == document.Project.Solution.Workspace); RaiseTaskListUpdated(_workspace, document.Project.Solution, document.Id, data.Items); } }