提交 0a1fb50b 编写于 作者: C Cyrus Najmabadi

Work

上级 f8cdc451
......@@ -12,7 +12,7 @@
<ServiceHubService Include="roslynRemoteDesignerAttributeService" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDesignerAttributeService" />
<ServiceHubService Include="roslynRemoteProjectTelemetryService" ClassName="Microsoft.CodeAnalysis.Remote.RemoteProjectTelemetryService" />
<ServiceHubService Include="roslynRemoteSymbolSearchUpdateEngine" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSymbolSearchUpdateEngine" />
<ServiceHubService Include="roslynRemoteTodoCommentService" ClassName="Microsoft.CodeAnalysis.Remote.RemoteTodoCommentService" />
<ServiceHubService Include="roslynRemoteTodoCommentsService" ClassName="Microsoft.CodeAnalysis.Remote.RemoteTodoCommentsService" />
<ServiceHubService Include="roslynLanguageServer" ClassName="Microsoft.CodeAnalysis.Remote.LanguageServer" />
</ItemGroup>
......
......@@ -7,6 +7,7 @@
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.TodoComments;
namespace Microsoft.CodeAnalysis.Editor
{
......@@ -23,7 +24,7 @@ internal interface ITodoListProvider
/// </summary>
event EventHandler<TodoItemsUpdatedArgs> TodoListUpdated;
ImmutableArray<TodoItem> GetTodoItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken);
ImmutableArray<TodoCommentInfo> GetTodoItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken);
/// <summary>
/// Get current UpdatedEventArgs stored in ITodoListProvider
......
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.TodoComments;
using Microsoft.CodeAnalysis.Versions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
{
internal partial class TodoCommentIncrementalAnalyzer : IIncrementalAnalyzer
{
public const string Name = "Todo Comment Document Worker";
private readonly TodoCommentIncrementalAnalyzerProvider _owner;
private readonly Workspace _workspace;
private readonly TodoCommentTokens _todoCommentTokens;
private readonly TodoCommentState _state;
public TodoCommentIncrementalAnalyzer(Workspace workspace, TodoCommentIncrementalAnalyzerProvider owner, TodoCommentTokens todoCommentTokens)
{
_workspace = workspace;
_owner = owner;
_todoCommentTokens = todoCommentTokens;
_state = new TodoCommentState();
}
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
{
// remove cache
_state.Remove(document.Id);
return _state.PersistAsync(document, new Data(VersionStamp.Default, VersionStamp.Default, ImmutableArray<TodoItem>.Empty), cancellationToken);
}
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;
}
// Compute and persist the TODO comments for this document.
var (existingData, newData) = await ComputeAndPersistTodoCommentsAsync(document, _state, _todoCommentTokens, cancellationToken).ConfigureAwait(false);
// Now update the task list if there are any changes from the previously computed TODO comments, if any.
// NOTE: We don't check for cancellation here to ensure the task list view and our latest
// computed state are identical.
if (existingData == null || existingData.Items.Length != newData.Items.Length)
{
Debug.Assert(_workspace == document.Project.Solution.Workspace);
RaiseTaskListUpdated(_workspace, document.Project.Solution, document.Id, newData.Items);
}
}
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);
}
private static async Task<IList<TodoComment>> GetTodoCommentsAsync(Document document, IList<TodoCommentDescriptor> tokens, CancellationToken cancellationToken)
{
var service = document.GetLanguageService<ITodoCommentService>();
if (service == null)
{
// no inproc support
return SpecializedCollections.EmptyList<TodoComment>();
}
return await service.GetTodoCommentsAsync(document, tokens, cancellationToken).ConfigureAwait(false);
}
private static async Task<ImmutableArray<TodoItem>> CreateItemsAsync(Document document, IList<TodoComment> comments, CancellationToken cancellationToken)
{
var items = ImmutableArray.CreateBuilder<TodoItem>();
if (comments != null)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var syntaxTree = document.SupportsSyntaxTree ? await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false) : null;
foreach (var comment in comments)
{
items.Add(CreateItem(document, text, syntaxTree, comment));
}
}
return items.ToImmutable();
}
private static TodoItem CreateItem(Document document, SourceText text, SyntaxTree tree, TodoComment comment)
{
// make sure given position is within valid text range.
var textSpan = new TextSpan(Math.Min(text.Length, Math.Max(0, comment.Position)), 0);
var location = tree == null ? Location.Create(document.FilePath, textSpan, text.Lines.GetLinePositionSpan(textSpan)) : tree.GetLocation(textSpan);
var originalLineInfo = location.GetLineSpan();
var mappedLineInfo = location.GetMappedLineSpan();
return new TodoItem(
comment.Descriptor.Priority,
comment.Message,
document.Id,
mappedLine: mappedLineInfo.StartLinePosition.Line,
originalLine: originalLineInfo.StartLinePosition.Line,
mappedColumn: mappedLineInfo.StartLinePosition.Character,
originalColumn: originalLineInfo.StartLinePosition.Character,
mappedFilePath: mappedLineInfo.GetMappedFilePathIfExist(),
originalFilePath: document.FilePath);
}
public ImmutableArray<TodoItem> GetTodoItems(Workspace workspace, DocumentId id, CancellationToken cancellationToken)
{
var document = workspace.CurrentSolution.GetDocument(id);
if (document == null)
{
return ImmutableArray<TodoItem>.Empty;
}
// TODO let's think about what to do here. for now, let call it synchronously. also, there is no actual async-ness for the
// TryGetExistingDataAsync, API just happen to be async since our persistent API is async API. but both caller and implementor are
// actually not async.
var (_, newData) = ComputeAndPersistTodoCommentsAsync(document, _state, _todoCommentTokens, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken);
return newData.Items;
}
public IEnumerable<UpdatedEventArgs> GetTodoItemsUpdatedEventArgs(Workspace workspace)
{
foreach (var documentId in _state.GetDocumentIds())
{
yield return new UpdatedEventArgs(Tuple.Create(this, documentId), workspace, documentId.ProjectId, documentId);
}
}
private static bool CheckVersions(Document document, VersionStamp textVersion, VersionStamp syntaxVersion, Data existingData)
{
// first check full version to see whether we can reuse data in same session, if we can't, check timestamp only version to see whether
// we can use it cross-session.
return document.CanReusePersistedTextVersion(textVersion, existingData.TextVersion) &&
document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, existingData.SyntaxVersion);
}
internal ImmutableArray<TodoItem> GetItems_TestingOnly(DocumentId documentId)
{
return _state.GetItems_TestingOnly(documentId);
}
private void RaiseTaskListUpdated(Workspace workspace, Solution solution, DocumentId documentId, ImmutableArray<TodoItem> items)
{
if (_owner != null)
{
_owner.RaiseTaskListUpdated(documentId, workspace, solution, documentId.ProjectId, documentId, items);
}
}
public void RemoveDocument(DocumentId documentId)
{
_state.Remove(documentId);
RaiseTaskListUpdated(_workspace, null, documentId, ImmutableArray<TodoItem>.Empty);
}
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
{
return e.Option == TodoCommentOptions.TokenList;
}
private class Data
{
public readonly VersionStamp TextVersion;
public readonly VersionStamp SyntaxVersion;
public readonly ImmutableArray<TodoItem> Items;
public Data(VersionStamp textVersion, VersionStamp syntaxVersion, ImmutableArray<TodoItem> items)
{
this.TextVersion = textVersion;
this.SyntaxVersion = syntaxVersion;
this.Items = items;
}
}
#region not used
public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void RemoveProject(ProjectId projectId)
{
}
#endregion
}
}
//// 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.Generic;
//using System.Collections.Immutable;
//using System.Diagnostics;
//using System.Threading;
//using System.Threading.Tasks;
//using Microsoft.CodeAnalysis.Common;
//using Microsoft.CodeAnalysis.Editor.Shared.Options;
//using Microsoft.CodeAnalysis.Options;
//using Microsoft.CodeAnalysis.Shared.Extensions;
//using Microsoft.CodeAnalysis.Shared.Options;
//using Microsoft.CodeAnalysis.SolutionCrawler;
//using Microsoft.CodeAnalysis.Text;
//using Microsoft.CodeAnalysis.TodoComments;
//using Microsoft.CodeAnalysis.Versions;
//using Roslyn.Utilities;
//namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
//{
// internal partial class TodoCommentIncrementalAnalyzer : IIncrementalAnalyzer
// {
// public const string Name = "Todo Comment Document Worker";
// private readonly TodoCommentIncrementalAnalyzerProvider _owner;
// private readonly Workspace _workspace;
// private readonly TodoCommentTokens _todoCommentTokens;
// private readonly TodoCommentState _state;
// public TodoCommentIncrementalAnalyzer(Workspace workspace, TodoCommentIncrementalAnalyzerProvider owner, TodoCommentTokens todoCommentTokens)
// {
// _workspace = workspace;
// _owner = owner;
// _todoCommentTokens = todoCommentTokens;
// _state = new TodoCommentState();
// }
// public Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
// {
// // remove cache
// _state.Remove(document.Id);
// return _state.PersistAsync(document, new Data(VersionStamp.Default, VersionStamp.Default, ImmutableArray<TodoItem>.Empty), cancellationToken);
// }
// 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;
// }
// // Compute and persist the TODO comments for this document.
// var (existingData, newData) = await ComputeAndPersistTodoCommentsAsync(document, _state, _todoCommentTokens, cancellationToken).ConfigureAwait(false);
// // Now update the task list if there are any changes from the previously computed TODO comments, if any.
// // NOTE: We don't check for cancellation here to ensure the task list view and our latest
// // computed state are identical.
// if (existingData == null || existingData.Items.Length != newData.Items.Length)
// {
// Debug.Assert(_workspace == document.Project.Solution.Workspace);
// RaiseTaskListUpdated(_workspace, document.Project.Solution, document.Id, newData.Items);
// }
// }
// 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);
// }
// private static async Task<IList<TodoComment>> GetTodoCommentsAsync(Document document, IList<TodoCommentDescriptor> tokens, CancellationToken cancellationToken)
// {
// var service = document.GetLanguageService<ITodoCommentService>();
// if (service == null)
// {
// // no inproc support
// return SpecializedCollections.EmptyList<TodoComment>();
// }
// return await service.GetTodoCommentsAsync(document, tokens, cancellationToken).ConfigureAwait(false);
// }
// private static async Task<ImmutableArray<TodoItem>> CreateItemsAsync(Document document, IList<TodoComment> comments, CancellationToken cancellationToken)
// {
// var items = ImmutableArray.CreateBuilder<TodoItem>();
// if (comments != null)
// {
// var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
// var syntaxTree = document.SupportsSyntaxTree ? await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false) : null;
// foreach (var comment in comments)
// {
// items.Add(CreateItem(document, text, syntaxTree, comment));
// }
// }
// return items.ToImmutable();
// }
// private static TodoItem CreateItem(Document document, SourceText text, SyntaxTree tree, TodoComment comment)
// {
// // make sure given position is within valid text range.
// var textSpan = new TextSpan(Math.Min(text.Length, Math.Max(0, comment.Position)), 0);
// var location = tree == null ? Location.Create(document.FilePath, textSpan, text.Lines.GetLinePositionSpan(textSpan)) : tree.GetLocation(textSpan);
// var originalLineInfo = location.GetLineSpan();
// var mappedLineInfo = location.GetMappedLineSpan();
// return new TodoItem(
// comment.Descriptor.Priority,
// comment.Message,
// document.Id,
// mappedLine: mappedLineInfo.StartLinePosition.Line,
// originalLine: originalLineInfo.StartLinePosition.Line,
// mappedColumn: mappedLineInfo.StartLinePosition.Character,
// originalColumn: originalLineInfo.StartLinePosition.Character,
// mappedFilePath: mappedLineInfo.GetMappedFilePathIfExist(),
// originalFilePath: document.FilePath);
// }
// public ImmutableArray<TodoItem> GetTodoItems(Workspace workspace, DocumentId id, CancellationToken cancellationToken)
// {
// var document = workspace.CurrentSolution.GetDocument(id);
// if (document == null)
// {
// return ImmutableArray<TodoItem>.Empty;
// }
// // TODO let's think about what to do here. for now, let call it synchronously. also, there is no actual async-ness for the
// // TryGetExistingDataAsync, API just happen to be async since our persistent API is async API. but both caller and implementor are
// // actually not async.
// var (_, newData) = ComputeAndPersistTodoCommentsAsync(document, _state, _todoCommentTokens, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken);
// return newData.Items;
// }
// public IEnumerable<UpdatedEventArgs> GetTodoItemsUpdatedEventArgs(Workspace workspace)
// {
// foreach (var documentId in _state.GetDocumentIds())
// {
// yield return new UpdatedEventArgs(Tuple.Create(this, documentId), workspace, documentId.ProjectId, documentId);
// }
// }
// private static bool CheckVersions(Document document, VersionStamp textVersion, VersionStamp syntaxVersion, Data existingData)
// {
// // first check full version to see whether we can reuse data in same session, if we can't, check timestamp only version to see whether
// // we can use it cross-session.
// return document.CanReusePersistedTextVersion(textVersion, existingData.TextVersion) &&
// document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, existingData.SyntaxVersion);
// }
// internal ImmutableArray<TodoItem> GetItems_TestingOnly(DocumentId documentId)
// {
// return _state.GetItems_TestingOnly(documentId);
// }
// private void RaiseTaskListUpdated(Workspace workspace, Solution solution, DocumentId documentId, ImmutableArray<TodoItem> items)
// {
// if (_owner != null)
// {
// _owner.RaiseTaskListUpdated(documentId, workspace, solution, documentId.ProjectId, documentId, items);
// }
// }
// public void RemoveDocument(DocumentId documentId)
// {
// _state.Remove(documentId);
// RaiseTaskListUpdated(_workspace, null, documentId, ImmutableArray<TodoItem>.Empty);
// }
// public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
// {
// return e.Option == TodoCommentOptions.TokenList;
// }
// private class Data
// {
// public readonly VersionStamp TextVersion;
// public readonly VersionStamp SyntaxVersion;
// public readonly ImmutableArray<TodoItem> Items;
// public Data(VersionStamp textVersion, VersionStamp syntaxVersion, ImmutableArray<TodoItem> items)
// {
// this.TextVersion = textVersion;
// this.SyntaxVersion = syntaxVersion;
// this.Items = items;
// }
// }
// #region not used
// public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken)
// {
// return Task.CompletedTask;
// }
// public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken)
// {
// return Task.CompletedTask;
// }
// public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken)
// {
// return Task.CompletedTask;
// }
// public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken)
// {
// return Task.CompletedTask;
// }
// public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken)
// {
// return Task.CompletedTask;
// }
// public void RemoveProject(ProjectId projectId)
// {
// }
// #endregion
// }
//}
// 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.
//// 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.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.SolutionCrawler;
//using System;
//using System.Collections.Generic;
//using System.Collections.Immutable;
//using System.Composition;
//using System.Linq;
//using System.Runtime.CompilerServices;
//using System.Threading;
//using Microsoft.CodeAnalysis.Common;
//using Microsoft.CodeAnalysis.Host;
//using Microsoft.CodeAnalysis.SolutionCrawler;
namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
{
[Shared]
[Export(typeof(ITodoListProvider))]
[ExportIncrementalAnalyzerProvider(
name: nameof(TodoCommentIncrementalAnalyzerProvider),
workspaceKinds: new[] { WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles })]
internal class TodoCommentIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider, ITodoListProvider
{
private static readonly ConditionalWeakTable<Workspace, TodoCommentIncrementalAnalyzer> s_analyzers = new ConditionalWeakTable<Workspace, TodoCommentIncrementalAnalyzer>();
//namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
//{
// [Shared]
// [Export(typeof(ITodoListProvider))]
// [ExportIncrementalAnalyzerProvider(
// name: nameof(TodoCommentIncrementalAnalyzerProvider),
// workspaceKinds: new[] { WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles })]
// internal class TodoCommentIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider, ITodoListProvider
// {
// private static readonly ConditionalWeakTable<Workspace, TodoCommentIncrementalAnalyzer> s_analyzers = new ConditionalWeakTable<Workspace, TodoCommentIncrementalAnalyzer>();
private readonly TodoCommentTokens _todoCommentTokens;
private readonly EventListenerTracker<ITodoListProvider> _eventListenerTracker;
// private readonly TodoCommentTokens _todoCommentTokens;
// private readonly EventListenerTracker<ITodoListProvider> _eventListenerTracker;
[ImportingConstructor]
public TodoCommentIncrementalAnalyzerProvider(
TodoCommentTokens todoCommentTokens,
[ImportMany]IEnumerable<Lazy<IEventListener, EventListenerMetadata>> eventListeners)
{
_todoCommentTokens = todoCommentTokens;
_eventListenerTracker = new EventListenerTracker<ITodoListProvider>(eventListeners, WellKnownEventListeners.TodoListProvider);
}
// [ImportingConstructor]
// public TodoCommentIncrementalAnalyzerProvider(
// TodoCommentTokens todoCommentTokens,
// [ImportMany]IEnumerable<Lazy<IEventListener, EventListenerMetadata>> eventListeners)
// {
// _todoCommentTokens = todoCommentTokens;
// _eventListenerTracker = new EventListenerTracker<ITodoListProvider>(eventListeners, WellKnownEventListeners.TodoListProvider);
// }
public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
{
return s_analyzers.GetValue(workspace, w =>
new TodoCommentIncrementalAnalyzer(w, this, _todoCommentTokens));
}
// public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
// {
// return s_analyzers.GetValue(workspace, w =>
// new TodoCommentIncrementalAnalyzer(w, this, _todoCommentTokens));
// }
internal void RaiseTaskListUpdated(object id, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<TodoItem> items)
{
_eventListenerTracker.EnsureEventListener(workspace, this);
// internal void RaiseTaskListUpdated(object id, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<TodoItem> items)
// {
// _eventListenerTracker.EnsureEventListener(workspace, this);
this.TodoListUpdated?.Invoke(this, new TodoItemsUpdatedArgs(Tuple.Create(this, id), workspace, solution, projectId, documentId, items));
}
// this.TodoListUpdated?.Invoke(this, new TodoItemsUpdatedArgs(Tuple.Create(this, id), workspace, solution, projectId, documentId, items));
// }
public event EventHandler<TodoItemsUpdatedArgs> TodoListUpdated;
// public event EventHandler<TodoItemsUpdatedArgs> TodoListUpdated;
public ImmutableArray<TodoItem> GetTodoItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
{
var analyzer = TryGetAnalyzer(workspace);
if (analyzer == null)
{
return ImmutableArray<TodoItem>.Empty;
}
// public ImmutableArray<TodoItem> GetTodoItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
// {
// var analyzer = TryGetAnalyzer(workspace);
// if (analyzer == null)
// {
// return ImmutableArray<TodoItem>.Empty;
// }
var document = workspace.CurrentSolution.GetDocument(documentId);
if (document == null)
{
return ImmutableArray<TodoItem>.Empty;
}
// var document = workspace.CurrentSolution.GetDocument(documentId);
// if (document == null)
// {
// return ImmutableArray<TodoItem>.Empty;
// }
return analyzer.GetTodoItems(workspace, document.Id, cancellationToken);
}
// return analyzer.GetTodoItems(workspace, document.Id, cancellationToken);
// }
public IEnumerable<UpdatedEventArgs> GetTodoItemsUpdatedEventArgs(Workspace workspace, CancellationToken cancellationToken)
{
var analyzer = TryGetAnalyzer(workspace);
if (analyzer == null)
{
return ImmutableArray<UpdatedEventArgs>.Empty;
}
// public IEnumerable<UpdatedEventArgs> GetTodoItemsUpdatedEventArgs(Workspace workspace, CancellationToken cancellationToken)
// {
// var analyzer = TryGetAnalyzer(workspace);
// if (analyzer == null)
// {
// return ImmutableArray<UpdatedEventArgs>.Empty;
// }
return analyzer.GetTodoItemsUpdatedEventArgs(workspace);
}
// return analyzer.GetTodoItemsUpdatedEventArgs(workspace);
// }
private TodoCommentIncrementalAnalyzer TryGetAnalyzer(Workspace workspace)
{
if (s_analyzers.TryGetValue(workspace, out var analyzer))
{
return analyzer;
}
// private TodoCommentIncrementalAnalyzer TryGetAnalyzer(Workspace workspace)
// {
// if (s_analyzers.TryGetValue(workspace, out var analyzer))
// {
// return analyzer;
// }
return null;
}
}
}
// return null;
// }
// }
//}
// 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.Collections.Immutable;
using System.IO;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.SolutionCrawler.State;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
{
internal partial class TodoCommentIncrementalAnalyzer : IIncrementalAnalyzer
{
private class TodoCommentState : AbstractDocumentAnalyzerState<Data>
{
private const string FormatVersion = "1";
protected override string StateName => "<TodoComments>";
protected override int GetCount(Data data)
{
return data.Items.Length;
}
protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken)
{
using var reader = ObjectReader.TryGetReader(stream, leaveOpen: true, cancellationToken);
if (reader != null)
{
var format = reader.ReadString();
if (string.Equals(format, FormatVersion))
{
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
using var listDisposer = ArrayBuilder<TodoItem>.GetInstance(out var list);
AppendItems(reader, value, list, cancellationToken);
return new Data(textVersion, dataVersion, list.ToImmutable());
}
}
return null;
}
protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken)
{
using var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken: cancellationToken);
writer.WriteString(FormatVersion);
data.TextVersion.WriteTo(writer);
data.SyntaxVersion.WriteTo(writer);
writer.WriteInt32(data.Items.Length);
foreach (var item in data.Items.OfType<TodoItem>())
{
cancellationToken.ThrowIfCancellationRequested();
writer.WriteInt32(item.Priority);
writer.WriteString(item.Message);
writer.WriteString(item.OriginalFilePath);
writer.WriteInt32(item.OriginalLine);
writer.WriteInt32(item.OriginalColumn);
writer.WriteString(item.MappedFilePath);
writer.WriteInt32(item.MappedLine);
writer.WriteInt32(item.MappedColumn);
}
}
public ImmutableArray<DocumentId> GetDocumentIds()
{
return DataCache.Keys.ToImmutableArrayOrEmpty();
}
public ImmutableArray<TodoItem> GetItems_TestingOnly(DocumentId documentId)
{
if (this.DataCache.TryGetValue(documentId, out var entry) && entry.HasCachedData)
{
return entry.Data.Items;
}
return ImmutableArray<TodoItem>.Empty;
}
private void AppendItems(ObjectReader reader, Document document, ArrayBuilder<TodoItem> list, CancellationToken cancellationToken)
{
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var priority = reader.ReadInt32();
var message = reader.ReadString();
var originalFile = reader.ReadString();
var originalLine = reader.ReadInt32();
var originalColumn = reader.ReadInt32();
var mappedFile = reader.ReadString();
var mappedLine = reader.ReadInt32();
var mappedColumn = reader.ReadInt32();
list.Add(new TodoItem(
priority, message,
document.Id,
mappedLine, originalLine, mappedColumn, originalColumn, mappedFile, originalFile));
}
}
}
}
}
//// 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.Collections.Immutable;
//using System.IO;
//using System.Threading;
//using Microsoft.CodeAnalysis.PooledObjects;
//using Microsoft.CodeAnalysis.SolutionCrawler;
//using Microsoft.CodeAnalysis.SolutionCrawler.State;
//using Roslyn.Utilities;
//namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
//{
// internal partial class TodoCommentIncrementalAnalyzer : IIncrementalAnalyzer
// {
// private class TodoCommentState : AbstractDocumentAnalyzerState<Data>
// {
// private const string FormatVersion = "1";
// protected override string StateName => "<TodoComments>";
// protected override int GetCount(Data data)
// {
// return data.Items.Length;
// }
// protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken)
// {
// using var reader = ObjectReader.TryGetReader(stream, leaveOpen: true, cancellationToken);
// if (reader != null)
// {
// var format = reader.ReadString();
// if (string.Equals(format, FormatVersion))
// {
// var textVersion = VersionStamp.ReadFrom(reader);
// var dataVersion = VersionStamp.ReadFrom(reader);
// using var listDisposer = ArrayBuilder<TodoItem>.GetInstance(out var list);
// AppendItems(reader, value, list, cancellationToken);
// return new Data(textVersion, dataVersion, list.ToImmutable());
// }
// }
// return null;
// }
// protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken)
// {
// using var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken: cancellationToken);
// writer.WriteString(FormatVersion);
// data.TextVersion.WriteTo(writer);
// data.SyntaxVersion.WriteTo(writer);
// writer.WriteInt32(data.Items.Length);
// foreach (var item in data.Items.OfType<TodoItem>())
// {
// cancellationToken.ThrowIfCancellationRequested();
// writer.WriteInt32(item.Priority);
// writer.WriteString(item.Message);
// writer.WriteString(item.OriginalFilePath);
// writer.WriteInt32(item.OriginalLine);
// writer.WriteInt32(item.OriginalColumn);
// writer.WriteString(item.MappedFilePath);
// writer.WriteInt32(item.MappedLine);
// writer.WriteInt32(item.MappedColumn);
// }
// }
// public ImmutableArray<DocumentId> GetDocumentIds()
// {
// return DataCache.Keys.ToImmutableArrayOrEmpty();
// }
// public ImmutableArray<TodoItem> GetItems_TestingOnly(DocumentId documentId)
// {
// if (this.DataCache.TryGetValue(documentId, out var entry) && entry.HasCachedData)
// {
// return entry.Data.Items;
// }
// return ImmutableArray<TodoItem>.Empty;
// }
// private void AppendItems(ObjectReader reader, Document document, ArrayBuilder<TodoItem> list, CancellationToken cancellationToken)
// {
// var count = reader.ReadInt32();
// for (var i = 0; i < count; i++)
// {
// cancellationToken.ThrowIfCancellationRequested();
// var priority = reader.ReadInt32();
// var message = reader.ReadString();
// var originalFile = reader.ReadString();
// var originalLine = reader.ReadInt32();
// var originalColumn = reader.ReadInt32();
// var mappedFile = reader.ReadString();
// var mappedLine = reader.ReadInt32();
// var mappedColumn = reader.ReadInt32();
// list.Add(new TodoItem(
// priority, message,
// document.Id,
// mappedLine, originalLine, mappedColumn, originalColumn, mappedFile, originalFile));
// }
// }
// }
// }
//}
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Threading;
using Microsoft.CodeAnalysis.TodoComments;
namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
{
/// <summary>
/// provide comment tokens to scan
///
/// we use this indirection so that we can get different tokens based on host
/// </summary>
[Export]
internal class TodoCommentTokens
{
[ImportingConstructor]
public TodoCommentTokens()
{
}
private class TokenInfo
{
internal readonly string OptionText;
internal readonly ImmutableArray<TodoCommentDescriptor> Tokens;
public TokenInfo(string optionText, ImmutableArray<TodoCommentDescriptor> tokens)
{
this.OptionText = optionText;
this.Tokens = tokens;
}
}
private TokenInfo _lastTokenInfo;
public ImmutableArray<TodoCommentDescriptor> GetTokens(Document document)
{
var optionText = document.Project.Solution.Options.GetOption(TodoCommentOptions.TokenList);
var lastInfo = _lastTokenInfo;
if (lastInfo != null && lastInfo.OptionText == optionText)
{
return lastInfo.Tokens;
}
var tokens = Parse(optionText);
System.Threading.Interlocked.CompareExchange(ref _lastTokenInfo, new TokenInfo(optionText, tokens), lastInfo);
return tokens;
}
}
}
//// 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.Collections.Generic;
//using System.Collections.Immutable;
//using System.ComponentModel.Composition;
//using System.Globalization;
//using System.Threading;
//using Microsoft.CodeAnalysis.TodoComments;
//namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments
//{
// /// <summary>
// /// provide comment tokens to scan
// ///
// /// we use this indirection so that we can get different tokens based on host
// /// </summary>
// [Export]
// internal class TodoCommentTokens
// {
// [ImportingConstructor]
// public TodoCommentTokens()
// {
// }
// private class TokenInfo
// {
// internal readonly string OptionText;
// internal readonly ImmutableArray<TodoCommentDescriptor> Tokens;
// public TokenInfo(string optionText, ImmutableArray<TodoCommentDescriptor> tokens)
// {
// this.OptionText = optionText;
// this.Tokens = tokens;
// }
// }
// private TokenInfo _lastTokenInfo;
// public ImmutableArray<TodoCommentDescriptor> GetTokens(Document document)
// {
// var optionText = document.Project.Solution.Options.GetOption(TodoCommentOptions.TokenList);
// var lastInfo = _lastTokenInfo;
// if (lastInfo != null && lastInfo.OptionText == optionText)
// {
// return lastInfo.Tokens;
// }
// var tokens = Parse(optionText);
// System.Threading.Interlocked.CompareExchange(ref _lastTokenInfo, new TokenInfo(optionText, tokens), lastInfo);
// return tokens;
// }
// }
//}
// 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.
//// 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 Roslyn.Utilities;
//using System;
//using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor
{
internal sealed class TodoItem : IEquatable<TodoItem>
{
public int Priority { get; }
public string Message { get; }
public DocumentId DocumentId { get; }
public string MappedFilePath { get; }
public string OriginalFilePath { get; }
public int MappedLine { get; }
public int MappedColumn { get; }
public int OriginalLine { get; }
public int OriginalColumn { get; }
//namespace Microsoft.CodeAnalysis.Editor
//{
// internal sealed class TodoItem : IEquatable<TodoItem>
// {
// public int Priority { get; }
// public string Message { get; }
// public DocumentId DocumentId { get; }
// public string MappedFilePath { get; }
// public string OriginalFilePath { get; }
// public int MappedLine { get; }
// public int MappedColumn { get; }
// public int OriginalLine { get; }
// public int OriginalColumn { get; }
public TodoItem(
int priority,
string message,
DocumentId documentId,
int mappedLine,
int originalLine,
int mappedColumn,
int originalColumn,
string mappedFilePath,
string originalFilePath)
{
Contract.ThrowIfNull(documentId);
// public TodoItem(
// int priority,
// string message,
// DocumentId documentId,
// int mappedLine,
// int originalLine,
// int mappedColumn,
// int originalColumn,
// string mappedFilePath,
// string originalFilePath)
// {
// Contract.ThrowIfNull(documentId);
Priority = priority;
Message = message;
// Priority = priority;
// Message = message;
DocumentId = documentId;
// DocumentId = documentId;
MappedLine = mappedLine;
MappedColumn = mappedColumn;
MappedFilePath = mappedFilePath;
// MappedLine = mappedLine;
// MappedColumn = mappedColumn;
// MappedFilePath = mappedFilePath;
OriginalLine = originalLine;
OriginalColumn = originalColumn;
OriginalFilePath = originalFilePath;
}
// OriginalLine = originalLine;
// OriginalColumn = originalColumn;
// OriginalFilePath = originalFilePath;
// }
public override bool Equals(object obj)
=> obj is TodoItem other && Equals(other);
// public override bool Equals(object obj)
// => obj is TodoItem other && Equals(other);
public override int GetHashCode()
=> GetHashCode(this);
// public override int GetHashCode()
// => GetHashCode(this);
public override string ToString()
=> $"{Priority} {Message} {MappedFilePath ?? ""} ({MappedLine.ToString()}, {MappedColumn.ToString()}) [original: {OriginalFilePath ?? ""} ({OriginalLine.ToString()}, {OriginalColumn.ToString()})";
// public override string ToString()
// => $"{Priority} {Message} {MappedFilePath ?? ""} ({MappedLine.ToString()}, {MappedColumn.ToString()}) [original: {OriginalFilePath ?? ""} ({OriginalLine.ToString()}, {OriginalColumn.ToString()})";
public bool Equals(TodoItem right)
{
if (ReferenceEquals(this, right))
{
return true;
}
// public bool Equals(TodoItem right)
// {
// if (ReferenceEquals(this, right))
// {
// return true;
// }
if (right is null)
{
return false;
}
// if (right is null)
// {
// return false;
// }
return DocumentId == right.DocumentId &&
Priority == right.Priority &&
Message == right.Message &&
OriginalLine == right.OriginalLine &&
OriginalColumn == right.OriginalColumn;
}
// return DocumentId == right.DocumentId &&
// Priority == right.Priority &&
// Message == right.Message &&
// OriginalLine == right.OriginalLine &&
// OriginalColumn == right.OriginalColumn;
// }
public static int GetHashCode(TodoItem item)
=> Hash.Combine(item.DocumentId,
Hash.Combine(item.Priority,
Hash.Combine(item.Message,
Hash.Combine(item.OriginalLine,
Hash.Combine(item.OriginalColumn, 0)))));
}
}
// public static int GetHashCode(TodoItem item)
// => Hash.Combine(item.DocumentId,
// Hash.Combine(item.Priority,
// Hash.Combine(item.Message,
// Hash.Combine(item.OriginalLine,
// Hash.Combine(item.OriginalColumn, 0)))));
// }
//}
......@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.TodoComments;
namespace Microsoft.CodeAnalysis.Editor
{
......@@ -17,10 +18,10 @@ internal sealed class TodoItemsUpdatedArgs : UpdatedEventArgs
/// <summary>
/// The task items associated with the ID.
/// </summary>
public ImmutableArray<TodoItem> TodoItems { get; }
public ImmutableArray<TodoCommentInfo> TodoItems { get; }
public TodoItemsUpdatedArgs(
object id, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<TodoItem> todoItems)
object id, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<TodoCommentInfo> todoItems)
: base(id, workspace, projectId, documentId)
{
Solution = solution;
......
......@@ -169,7 +169,7 @@ public InProcRemoteServices(bool runCacheCleanup)
RegisterService(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, (s, p) => new RemoteSymbolSearchUpdateEngine(s, p));
RegisterService(WellKnownServiceHubServices.RemoteDesignerAttributeService, (s, p) => new RemoteDesignerAttributeService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteProjectTelemetryService, (s, p) => new RemoteProjectTelemetryService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteTodoCommentService, (s, p) => new RemoteTodoCommentService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteTodoCommentsService, (s, p) => new RemoteTodoCommentsService(s, p));
RegisterService(WellKnownServiceHubServices.LanguageServer, (s, p) => new LanguageServer(s, p));
}
......
......@@ -3,56 +3,12 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.TodoComments
{
/// <summary>
/// Description of a TODO comment type to find in a user's comments.
/// </summary>
internal readonly struct TodoCommentDescriptor
{
public string Text { get; }
public int Priority { get; }
public TodoCommentDescriptor(string text, int priority) : this()
{
Text = text;
Priority = priority;
}
public static ImmutableArray<TodoCommentDescriptor> Parse(string data)
{
if (string.IsNullOrWhiteSpace(data))
return ImmutableArray<TodoCommentDescriptor>.Empty;
var tuples = data.Split('|');
var result = ArrayBuilder<TodoCommentDescriptor>.GetInstance();
foreach (var tuple in tuples)
{
if (string.IsNullOrWhiteSpace(tuple))
continue;
var pair = tuple.Split(':');
if (pair.Length != 2 || string.IsNullOrWhiteSpace(pair[0]))
continue;
if (!int.TryParse(pair[1], NumberStyles.None, CultureInfo.InvariantCulture, out var priority))
continue;
result.Add(new TodoCommentDescriptor(pair[0].Trim(), priority));
}
return result.ToImmutableAndFree();
}
}
/// <summary>
/// A TODO comment that has been found within the user's code.
/// </summary>
......
// 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.Collections.Immutable;
using System.Globalization;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.TodoComments
{
/// <summary>
/// Description of a TODO comment type to find in a user's comments.
/// </summary>
internal readonly struct TodoCommentDescriptor
{
public string Text { get; }
public int Priority { get; }
public TodoCommentDescriptor(string text, int priority) : this()
{
Text = text;
Priority = priority;
}
}
internal readonly struct ParsedTodoCommentDescriptors
{
/// <summary>
/// The original option text that <see cref="Descriptors"/> were parsed out of.
/// </summary>
public readonly string OptionText;
public readonly ImmutableArray<TodoCommentDescriptor> Descriptors;
public ParsedTodoCommentDescriptors(string optionText, ImmutableArray<TodoCommentDescriptor> descriptors)
{
this.OptionText = optionText;
this.Descriptors = descriptors;
}
public static ParsedTodoCommentDescriptors Parse(string data)
{
if (string.IsNullOrWhiteSpace(data))
return new ParsedTodoCommentDescriptors(data, ImmutableArray<TodoCommentDescriptor>.Empty);
var tuples = data.Split('|');
var result = ArrayBuilder<TodoCommentDescriptor>.GetInstance();
foreach (var tuple in tuples)
{
if (string.IsNullOrWhiteSpace(tuple))
continue;
var pair = tuple.Split(':');
if (pair.Length != 2 || string.IsNullOrWhiteSpace(pair[0]))
continue;
if (!int.TryParse(pair[1], NumberStyles.None, CultureInfo.InvariantCulture, out var priority))
continue;
result.Add(new TodoCommentDescriptor(pair[0].Trim(), priority));
}
return new ParsedTodoCommentDescriptors(data, result.ToImmutableAndFree());
}
}
}
......@@ -7,28 +7,28 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.TodoComments;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource
{
internal sealed class TodoTableItem : TableItem
{
public readonly TodoItem Data;
public readonly TodoCommentInfo Data;
private TodoTableItem(
Workspace workspace,
TodoItem data,
TodoCommentInfo data,
string projectName,
Guid projectGuid,
string[] projectNames,
Guid[] projectGuids)
: base(workspace, projectName, projectGuid, projectNames, projectGuids)
{
Contract.ThrowIfNull(data);
Data = data;
}
public static TodoTableItem Create(Workspace workspace, TodoItem data)
public static TodoTableItem Create(Workspace workspace, TodoCommentInfo data)
{
GetProjectNameAndGuid(workspace, data.DocumentId.ProjectId, out var projectName, out var projectGuid);
return new TodoTableItem(workspace, data, projectName, projectGuid, projectNames: Array.Empty<string>(), projectGuids: Array.Empty<Guid>());
......@@ -66,29 +66,19 @@ public override bool EqualsIgnoringLocation(TableItem other)
/// We want to avoid displaying diagnostic multuple times when it is reported from
/// multi-targeted projects and/or files linked to multiple projects.
/// </summary>
internal sealed class GroupingComparer : IEqualityComparer<TodoItem>, IEqualityComparer<TodoTableItem>
internal sealed class GroupingComparer : IEqualityComparer<TodoCommentInfo>, IEqualityComparer<TodoTableItem>
{
public static readonly GroupingComparer Instance = new GroupingComparer();
public bool Equals(TodoItem left, TodoItem right)
public bool Equals(TodoCommentInfo left, TodoCommentInfo right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if (left is null || right is null)
{
return false;
}
// We don't need to compare OriginalFilePath since TODO items are only aggregated within a single file.
return
left.OriginalLine == right.OriginalLine &&
left.OriginalColumn == right.OriginalColumn;
}
public int GetHashCode(TodoItem data)
public int GetHashCode(TodoCommentInfo data)
=> Hash.Combine(data.OriginalLine, data.OriginalColumn);
public bool Equals(TodoTableItem left, TodoTableItem right)
......
......@@ -237,13 +237,13 @@ public override bool TryGetValue(int index, string columnName, out object conten
switch (columnName)
{
case StandardTableKeyNames.Priority:
content = ValueTypeCache.GetOrCreate((VSTASKPRIORITY)data.Priority);
content = ValueTypeCache.GetOrCreate((VSTASKPRIORITY)data.Value.Priority);
return content != null;
case StandardTableKeyNames.Text:
content = data.Message;
content = data.Value.Message;
return content != null;
case StandardTableKeyNames.DocumentName:
content = GetFileName(data.OriginalFilePath, data.MappedFilePath);
content = GetFileName(data.Value.OriginalFilePath, data.Value.MappedFilePath);
return content != null;
case StandardTableKeyNames.Line:
content = GetLineColumn(item).Line;
......
......@@ -7,12 +7,12 @@
using System.Threading;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComment
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments
{
/// <summary>
/// In process service responsible for listening to OOP todo comment notifications.
/// </summary>
internal interface ITodoCommentService : IWorkspaceService
internal interface ITodoCommentsService : IWorkspaceService
{
/// <summary>
/// Called by a host to let this service know that it should start background
......
......@@ -7,27 +7,32 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.ProjectTelemetry;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.TodoComment;
using Microsoft.CodeAnalysis.TodoComments;
using Microsoft.Internal.VisualStudio.Shell;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComment
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments
{
internal class VisualStudioTodoCommentService
: ForegroundThreadAffinitizedObject, ITodoCommentService, ITodoCommentServiceCallback
internal class VisualStudioTodoCommentsService
: ForegroundThreadAffinitizedObject, ITodoCommentsService, ITodoCommentsServiceCallback, ITodoListProvider
{
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly EventListenerTracker<ITodoListProvider> _eventListenerTracker;
private readonly ConditionalWeakTable<DocumentId, StrongBox<ImmutableArray<TodoCommentInfo>>> _documentToInfos
= new ConditionalWeakTable<DocumentId, StrongBox<ImmutableArray<TodoCommentInfo>>>();
/// <summary>
/// Our connections to the remote OOP server. Created on demand when we startup and then
......@@ -40,10 +45,19 @@ internal class VisualStudioTodoCommentService
/// </summary>
private AsyncBatchingWorkQueue<TodoCommentInfo> _workQueue = null!;
public VisualStudioTodoCommentService(VisualStudioWorkspaceImpl workspace, IThreadingContext threadingContext) : base(threadingContext)
=> _workspace = workspace;
public event EventHandler<TodoItemsUpdatedArgs>? TodoListUpdated;
public VisualStudioTodoCommentsService(
VisualStudioWorkspaceImpl workspace,
IThreadingContext threadingContext,
EventListenerTracker<ITodoListProvider> eventListenerTracker)
: base(threadingContext)
{
_workspace = workspace;
_eventListenerTracker = eventListenerTracker;
}
void ITodoCommentService.Start(CancellationToken cancellationToken)
void ITodoCommentsService.Start(CancellationToken cancellationToken)
=> _ = StartAsync(cancellationToken);
private async Task StartAsync(CancellationToken cancellationToken)
......@@ -79,14 +93,17 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken)
// Pass ourselves in as the callback target for the OOP service. As it discovers
// designer attributes it will call back into us to notify VS about it.
_keepAliveSession = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteTodoCommentService,
WellKnownServiceHubServices.RemoteTodoCommentsService,
callbackTarget: this, cancellationToken).ConfigureAwait(false);
if (_keepAliveSession == null)
return;
// Now that we've started, let the VS todo list know to start listening to us
_eventListenerTracker.EnsureEventListener(_workspace, this);
// Now kick off scanning in the OOP process.
var success = await _keepAliveSession.TryInvokeAsync(
nameof(IRemoteTodoCommentService.ComputeTodoCommentsAsync),
nameof(IRemoteTodoCommentsService.ComputeTodoCommentsAsync),
solution: null,
arguments: Array.Empty<object>(),
cancellationToken).ConfigureAwait(false);
......@@ -101,19 +118,31 @@ public Task ReportTodoCommentsAsync(List<TodoCommentInfo> infos, CancellationTok
return Task.CompletedTask;
}
private async Task ProcessTodoCommentInfosAsync(
private Task ProcessTodoCommentInfosAsync(
ImmutableArray<TodoCommentInfo> infos, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var _1 = ArrayBuilder<TodoCommentInfo>.GetInstance(out var filteredInfos);
using var _ = ArrayBuilder<TodoCommentInfo>.GetInstance(out var filteredInfos);
AddFilteredInfos(infos, filteredInfos);
using var _2 = ArrayBuilder<Task>.GetInstance(out var tasks);
foreach (var info in filteredInfos)
tasks.Add(Task.Run(() => NotifyTelemetryService(info), cancellationToken));
foreach (var group in filteredInfos.GroupBy(i => i.DocumentId))
{
var documentId = group.Key;
var documentInfos = group.ToImmutableArray();
// only one thread can be executing ProcessTodoCommentInfosAsync at a time,
// so it's safe to remove/add here.
_documentToInfos.Remove(documentId);
_documentToInfos.Add(documentId, new StrongBox<ImmutableArray<TodoCommentInfo>>(documentInfos));
this.TodoListUpdated?.Invoke(
this, new TodoItemsUpdatedArgs(
documentId, _workspace, _workspace.CurrentSolution,
documentId.ProjectId, documentId, documentInfos));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
return Task.CompletedTask;
}
private void AddFilteredInfos(ImmutableArray<TodoCommentInfo> infos, ArrayBuilder<TodoCommentInfo> filteredInfos)
......@@ -131,38 +160,18 @@ private void AddFilteredInfos(ImmutableArray<TodoCommentInfo> infos, ArrayBuilde
}
}
private void ProcessTodoCommentInfosAsync(ProjectTelemetryInfo info)
public ImmutableArray<TodoCommentInfo> GetTodoItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
{
try
{
var telemetryEvent = TelemetryHelper.TelemetryService.CreateEvent(TelemetryEventPath);
telemetryEvent.SetStringProperty(TelemetryProjectIdName, info.ProjectId.Id.ToString());
telemetryEvent.SetStringProperty(TelemetryProjectGuidName, Guid.Empty.ToString());
telemetryEvent.SetStringProperty(TelemetryLanguageName, info.Language);
telemetryEvent.SetIntProperty(TelemetryAnalyzerReferencesCountName, info.AnalyzerReferencesCount);
telemetryEvent.SetIntProperty(TelemetryProjectReferencesCountName, info.ProjectReferencesCount);
telemetryEvent.SetIntProperty(TelemetryMetadataReferencesCountName, info.MetadataReferencesCount);
telemetryEvent.SetIntProperty(TelemetryDocumentsCountName, info.DocumentsCount);
telemetryEvent.SetIntProperty(TelemetryAdditionalDocumentsCountName, info.AdditionalDocumentsCount);
TelemetryHelper.DefaultTelemetrySession.PostEvent(telemetryEvent);
}
catch (Exception e)
{
// The telemetry service itself can throw.
// So, to be very careful, put this in a try/catch too.
try
{
var exceptionEvent = TelemetryHelper.TelemetryService.CreateEvent(TelemetryExceptionEventPath);
exceptionEvent.SetStringProperty("Type", e.GetTypeDisplayName());
exceptionEvent.SetStringProperty("Message", e.Message);
exceptionEvent.SetStringProperty("StackTrace", e.StackTrace);
TelemetryHelper.DefaultTelemetrySession.PostEvent(exceptionEvent);
}
catch
{
}
}
return _documentToInfos.TryGetValue(documentId, out var values)
? values.Value
: ImmutableArray<TodoCommentInfo>.Empty;
}
public IEnumerable<UpdatedEventArgs> GetTodoItemsUpdatedEventArgs(
Workspace workspace, CancellationToken cancellationToken)
{
// Don't need to implement this. OOP pushes all items over to VS. So there's no need
return SpecializedCollections.EmptyEnumerable<UpdatedEventArgs>();
}
}
}
......@@ -5,6 +5,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
......@@ -12,25 +13,32 @@
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComment
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments
{
[Export(typeof(ITodoListProvider))]
[ExportWorkspaceServiceFactory(typeof(ITodoCommentService), ServiceLayer.Host), Shared]
internal class VisualStudioTodoCommentServiceFactory : IWorkspaceServiceFactory, ITodoListProvider
[ExportWorkspaceServiceFactory(typeof(ITodoCommentsService), ServiceLayer.Host), Shared]
internal class VisualStudioTodoCommentsServiceFactory : IWorkspaceServiceFactory
{
private readonly IThreadingContext _threadingContext;
private readonly EventListenerTracker<ITodoListProvider> _eventListenerTracker;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public VisualStudioTodoCommentServiceFactory(IThreadingContext threadingContext)
=> _threadingContext = threadingContext;
public VisualStudioTodoCommentsServiceFactory(
IThreadingContext threadingContext,
[ImportMany]IEnumerable<Lazy<IEventListener, EventListenerMetadata>> eventListeners)
{
_threadingContext = threadingContext;
_eventListenerTracker = new EventListenerTracker<ITodoListProvider>(eventListeners, WellKnownEventListeners.TodoListProvider);
}
public IWorkspaceService? CreateService(HostWorkspaceServices workspaceServices)
{
if (!(workspaceServices.Workspace is VisualStudioWorkspaceImpl workspace))
return null;
return new VisualStudioTodoCommentService(workspace, _threadingContext);
return new VisualStudioTodoCommentsService(
workspace, _threadingContext, _eventListenerTracker);
}
}
}
......@@ -146,7 +146,7 @@ public async Task TestTodoComments()
var comments = await client.TryRunRemoteAsync<IList<TodoComment>>(
WellKnownServiceHubServices.CodeAnalysisService,
nameof(IRemoteTodoCommentService.GetTodoCommentsAsync),
nameof(IRemoteTodoCommentsService.GetTodoCommentsAsync),
solution,
new object[] { solution.Projects.First().DocumentIds.First(), ImmutableArray.Create(new TodoCommentDescriptor("TODO", 0)) },
callbackTarget: null,
......
......@@ -37,7 +37,7 @@
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteDesignerAttributeService.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteProjectTelemetryService.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteSymbolSearchUpdateEngine.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteTodoCommentService.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteTodoCommentsService.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynLanguageServer.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteHost64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynSnapshot64.servicehub.service.json" />
......@@ -45,7 +45,7 @@
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteDesignerAttributeService64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteProjectTelemetryService64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteSymbolSearchUpdateEngine64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteTodoCommentService64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynRemoteTodoCommentsService64.servicehub.service.json" />
<Asset Type="Microsoft.ServiceHub.Service" d:Source="File" Path="roslynLanguageServer64.servicehub.service.json" />
<Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="BasicVisualStudio" Path="|BasicVisualStudio|" />
<Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="CSharpVisualStudio" Path="|CSharpVisualStudio|" />
......
......@@ -15,7 +15,7 @@ public static void Set64bit(bool x64)
RemoteDesignerAttributeService = "roslynRemoteDesignerAttributeService" + bit;
RemoteProjectTelemetryService = "roslynRemoteProjectTelemetryService" + bit;
RemoteSymbolSearchUpdateEngine = "roslynRemoteSymbolSearchUpdateEngine" + bit;
RemoteTodoCommentService = "roslynRemoteTodoCommentService" + bit;
RemoteTodoCommentsService = "roslynRemoteTodoCommentsService" + bit;
LanguageServer = "roslynLanguageServer" + bit;
}
......@@ -24,7 +24,7 @@ public static void Set64bit(bool x64)
public static string RemoteDesignerAttributeService { get; private set; } = "roslynRemoteDesignerAttributeService";
public static string RemoteProjectTelemetryService { get; private set; } = "roslynRemoteProjectTelemetryService";
public static string RemoteSymbolSearchUpdateEngine { get; private set; } = "roslynRemoteSymbolSearchUpdateEngine";
public static string RemoteTodoCommentService { get; private set; } = "roslynRemoteTodoCommentService";
public static string RemoteTodoCommentsService { get; private set; } = "roslynRemoteTodoCommentsService";
public static string LanguageServer { get; private set; } = "roslynLanguageServer";
// these are OOP implementation itself should care. not features that consume OOP care
......
......@@ -7,13 +7,13 @@
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.TodoComment
namespace Microsoft.CodeAnalysis.TodoComments
{
/// <summary>
/// Interface to allow host (VS) to inform the OOP service to start incrementally analyzing and
/// reporting results back to the host.
/// </summary>
internal interface IRemoteTodoCommentService
internal interface IRemoteTodoCommentsService
{
Task ComputeTodoCommentsAsync(CancellationToken cancellation);
}
......
......@@ -8,13 +8,13 @@
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.TodoComment
namespace Microsoft.CodeAnalysis.TodoComments
{
/// <summary>
/// Callback the host (VS) passes to the OOP service to allow it to send batch notifications
/// about todo comments.
/// </summary>
internal interface ITodoCommentServiceCallback
internal interface ITodoCommentsServiceCallback
{
Task ReportTodoCommentsAsync(List<TodoCommentInfo> infos, CancellationToken cancellationToken);
}
......
......@@ -7,7 +7,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.TodoComment
namespace Microsoft.CodeAnalysis.TodoComments
{
/// <summary>
/// Serialization typed used to pass information to/from OOP and VS.
......
......@@ -15,7 +15,7 @@
namespace Microsoft.CodeAnalysis.Remote
{
// root level service for all Roslyn services
internal partial class CodeAnalysisService : IRemoteTodoCommentService
internal partial class CodeAnalysisService : IRemoteTodoCommentsService
{
/// <summary>
/// This is top level entry point for TodoComments service from client (VS).
......
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.TodoComments;
namespace Microsoft.CodeAnalysis.Remote.Services.TodoComment
{
internal class DescriptorInfo
{
public readonly string OptionText;
public readonly ImmutableArray<TodoCommentDescriptor> Descriptors;
public DescriptorInfo(string optionText, ImmutableArray<TodoCommentDescriptor> descriptors)
{
this.OptionText = optionText;
this.Descriptors = descriptors;
}
}
}
// 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.
#nullable enable
using System.Collections.Immutable;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.TodoComments;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
{
internal partial class RemoteTodoCommentIncrementalAnalyzer
{
private const string SerializationFormat = "1";
private async Task<PersistedTodoCommentInfo?> TryReadExistingCommentInfoAsync(
IPersistentStorage storage, Document document, CancellationToken cancellationToken)
{
using var stream = await storage.ReadStreamAsync(document, DataKey, cancellationToken).ConfigureAwait(false);
using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken);
return TryReadPersistedInfo(reader);
}
private async Task PersistTodoCommentsAsync(
IPersistentStorage storage,
Document document,
PersistedTodoCommentInfo info,
CancellationToken cancellationToken)
{
using var memoryStream = new MemoryStream();
using var writer = new ObjectWriter(memoryStream);
writer.WriteString(SerializationFormat);
info.Version.WriteTo(writer);
writer.WriteString(info.OptionText);
writer.WriteInt32(info.TodoComments.Length);
foreach (var comment in info.TodoComments)
comment.WriteTo(writer);
memoryStream.Position = 0;
await storage.WriteStreamAsync(
document, DataKey, memoryStream, cancellationToken).ConfigureAwait(false);
}
private static PersistedTodoCommentInfo? TryReadPersistedInfo(ObjectReader reader)
{
if (reader == null)
return null;
try
{
var serializationFormat = reader.ReadString();
if (serializationFormat != SerializationFormat)
return null;
var version = VersionStamp.ReadFrom(reader);
var optionText = reader.ReadString();
var count = reader.ReadInt32();
using var _ = ArrayBuilder<TodoCommentInfo>.GetInstance(out var comments);
for (int i = 0; i < count; i++)
comments.Add(TodoCommentInfo.ReadFrom(reader));
return new PersistedTodoCommentInfo
{
Version = version,
OptionText = optionText,
TodoComments = comments.ToImmutable(),
};
}
catch
{
}
return null;
}
private class PersistedTodoCommentInfo
{
public VersionStamp Version;
public string? OptionText;
public ImmutableArray<TodoCommentInfo> TodoComments;
}
}
}
//// 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.
//#nullable enable
//using System.Collections.Immutable;
//using System.IO;
//using System.Threading;
//using System.Threading.Tasks;
//using Microsoft.CodeAnalysis.Host;
//using Microsoft.CodeAnalysis.PooledObjects;
//using Microsoft.CodeAnalysis.TodoComments;
//using Roslyn.Utilities;
//namespace Microsoft.CodeAnalysis.Remote
//{
// internal partial class RemoteTodoCommentIncrementalAnalyzer
// {
// private const string SerializationFormat = "1";
// private async Task<PersistedTodoCommentInfo?> TryReadExistingCommentInfoAsync(
// IPersistentStorage storage, Document document, CancellationToken cancellationToken)
// {
// using var stream = await storage.ReadStreamAsync(document, DataKey, cancellationToken).ConfigureAwait(false);
// using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken);
// return TryReadPersistedInfo(reader);
// }
// private async Task PersistTodoCommentsAsync(
// IPersistentStorage storage,
// Document document,
// PersistedTodoCommentInfo info,
// CancellationToken cancellationToken)
// {
// using var memoryStream = new MemoryStream();
// using var writer = new ObjectWriter(memoryStream);
// writer.WriteString(SerializationFormat);
// info.Version.WriteTo(writer);
// writer.WriteString(info.OptionText);
// writer.WriteInt32(info.TodoComments.Length);
// foreach (var comment in info.TodoComments)
// comment.WriteTo(writer);
// memoryStream.Position = 0;
// await storage.WriteStreamAsync(
// document, DataKey, memoryStream, cancellationToken).ConfigureAwait(false);
// }
// private static PersistedTodoCommentInfo? TryReadPersistedInfo(ObjectReader reader)
// {
// if (reader == null)
// return null;
// try
// {
// var serializationFormat = reader.ReadString();
// if (serializationFormat != SerializationFormat)
// return null;
// var version = VersionStamp.ReadFrom(reader);
// var optionText = reader.ReadString();
// var count = reader.ReadInt32();
// using var _ = ArrayBuilder<TodoCommentInfo>.GetInstance(out var comments);
// for (int i = 0; i < count; i++)
// comments.Add(TodoCommentInfo.ReadFrom(reader));
// return new PersistedTodoCommentInfo
// {
// Version = version,
// OptionText = optionText,
// TodoComments = comments.ToImmutable(),
// };
// }
// catch
// {
// }
// return null;
// }
// private class PersistedTodoCommentInfo
// {
// public VersionStamp Version;
// public string? OptionText;
// public ImmutableArray<TodoCommentInfo> TodoComments;
// }
// }
//}
......@@ -5,29 +5,22 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.TextFormatting;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Implementation.TodoComments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.ProjectTelemetry;
using Microsoft.CodeAnalysis.Remote.Services.TodoComment;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.TodoComments;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
{
internal partial class RemoteTodoCommentIncrementalAnalyzer : IncrementalAnalyzerBase
internal partial class RemoteTodoCommentsIncrementalAnalyzer : IncrementalAnalyzerBase
{
private const string DataKey = "TodoComment";
......@@ -39,9 +32,9 @@ internal partial class RemoteTodoCommentIncrementalAnalyzer : IncrementalAnalyze
private readonly IPersistentStorageService _storageService;
private readonly object _gate = new object();
private DescriptorInfo? _lastDescriptorInfo;
private ParsedTodoCommentDescriptors? _lastDescriptorInfo;
public RemoteTodoCommentIncrementalAnalyzer(Workspace workspace, RemoteEndPoint endPoint)
public RemoteTodoCommentsIncrementalAnalyzer(Workspace workspace, RemoteEndPoint endPoint)
{
_endPoint = endPoint;
_storageService = workspace.Services.GetRequiredService<IPersistentStorageService>();
......@@ -50,16 +43,16 @@ public RemoteTodoCommentIncrementalAnalyzer(Workspace workspace, RemoteEndPoint
public override bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
=> e.Option == TodoCommentOptions.TokenList;
private DescriptorInfo GetDescriptorInfo(Document document)
private ParsedTodoCommentDescriptors GetParsedTodoCommentDescriptors(Document document)
{
var optionText = document.Project.Solution.Options.GetOption(TodoCommentOptions.TokenList);
lock (_gate)
{
if (_lastDescriptorInfo == null || _lastDescriptorInfo.OptionText != optionText)
_lastDescriptorInfo = new DescriptorInfo(optionText, TodoCommentDescriptor.Parse(optionText));
if (_lastDescriptorInfo == null || _lastDescriptorInfo.Value.OptionText != optionText)
_lastDescriptorInfo = ParsedTodoCommentDescriptors.Parse(optionText);
return _lastDescriptorInfo;
return _lastDescriptorInfo.Value;
}
}
......@@ -69,20 +62,20 @@ public override async Task AnalyzeSyntaxAsync(Document document, InvocationReaso
if (todoCommentService == null)
return;
using var storage = _storageService.GetStorage(document.Project.Solution);
//using var storage = _storageService.GetStorage(document.Project.Solution);
var version = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
var descriptorInfo = GetDescriptorInfo(document);
//var version = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
var descriptorInfo = GetParsedTodoCommentDescriptors(document);
var persistedInfo = await TryReadExistingCommentInfoAsync(
storage, document, cancellationToken).ConfigureAwait(false);
if (persistedInfo != null &&
persistedInfo.Version == version &&
persistedInfo.OptionText == descriptorInfo.OptionText)
{
// Our info for this file is up to date.
return;
}
//var persistedInfo = await TryReadExistingCommentInfoAsync(
// storage, document, cancellationToken).ConfigureAwait(false);
//if (persistedInfo != null &&
// persistedInfo.Version == version &&
// persistedInfo.OptionText == descriptorInfo.OptionText)
//{
// // Our info for this file is up to date.
// return;
//}
// We're out of date. Recompute this info.
var todoComments = await todoCommentService.GetTodoCommentsAsync(
......@@ -99,16 +92,16 @@ public override async Task AnalyzeSyntaxAsync(Document document, InvocationReaso
new object[] { converted },
cancellationToken).ConfigureAwait(false);
persistedInfo = new PersistedTodoCommentInfo
{
Version = version,
OptionText = descriptorInfo.OptionText,
TodoComments = converted.ToImmutable(),
};
//persistedInfo = new PersistedTodoCommentInfo
//{
// Version = version,
// OptionText = descriptorInfo.OptionText,
// TodoComments = converted.ToImmutable(),
//};
// now that we've informed VS, save this information for the future.
await PersistTodoCommentsAsync(
storage, document, persistedInfo, cancellationToken).ConfigureAwait(false);
//// now that we've informed VS, save this information for the future.
//await PersistTodoCommentsAsync(
// storage, document, persistedInfo, cancellationToken).ConfigureAwait(false);
}
private async Task ConvertAsync(
......
......@@ -13,16 +13,16 @@ namespace Microsoft.CodeAnalysis.Remote
/// and then calls into OOP to tell it to start analyzing the solution. At that point we'll get
/// created and added to the solution crawler.
/// </remarks>
internal class RemoteTodoCommentIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider
internal class RemoteTodoCommentsIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider
{
private readonly RemoteEndPoint _endPoint;
public RemoteTodoCommentIncrementalAnalyzerProvider(RemoteEndPoint endPoint)
public RemoteTodoCommentsIncrementalAnalyzerProvider(RemoteEndPoint endPoint)
{
_endPoint = endPoint;
}
public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
=> new RemoteTodoCommentIncrementalAnalyzer(_endPoint);
=> new RemoteTodoCommentsIncrementalAnalyzer(workspace, _endPoint);
}
}
......@@ -10,12 +10,13 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ProjectTelemetry;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.TodoComments;
namespace Microsoft.CodeAnalysis.Remote
{
internal partial class RemoteTodoCommentService : ServiceBase, IRemoteTodoCommentService
internal partial class RemoteTodoCommentsService : ServiceBase, IRemoteTodoCommentsService
{
public RemoteTodoCommentService(
public RemoteTodoCommentsService(
Stream stream, IServiceProvider serviceProvider)
: base(serviceProvider, stream)
{
......@@ -29,12 +30,12 @@ public Task ComputeTodoCommentsAsync(CancellationToken cancellation)
var workspace = SolutionService.PrimaryWorkspace;
var endpoint = this.EndPoint;
var registrationService = workspace.Services.GetRequiredService<ISolutionCrawlerRegistrationService>();
var analyzerProvider = new RemoteTodoCommentIncrementalAnalyzerProvider(endpoint);
var analyzerProvider = new RemoteTodoCommentsIncrementalAnalyzerProvider(endpoint);
registrationService.AddAnalyzerProvider(
analyzerProvider,
new IncrementalAnalyzerProviderMetadata(
nameof(RemoteTodoCommentIncrementalAnalyzerProvider),
nameof(RemoteTodoCommentsIncrementalAnalyzerProvider),
highPriorityForActiveFile: false,
workspaceKinds: WorkspaceKind.RemoteWorkspace));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册