public Task<object> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Note: We don't use the original buffer that is associated with oldDocument // (and currently open in the editor) for oldBuffer below. This is because oldBuffer // will be used inside a projection buffer inside our inline diff preview below // and platform's implementation currently has a bug where projection buffers // are being leaked. This leak means that if we use the original buffer that is // currently visible in the editor here, the projection buffer span calculation // would be triggered every time user changes some code in this buffer (even though // the diff view would long have been dismissed by the time user edits the code) // resulting in crashes. Instead we create a new buffer from the same content. // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. var oldBuffer = CreateNewBuffer(oldDocument, cancellationToken); var newBuffer = CreateNewBuffer(newDocument, cancellationToken); // Convert the diffs to be line based. // Compute the diffs between the old text and the new. var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); // Need to show the spans in the right that are different. // We also need to show the spans that are in conflict. var originalSpans = GetOriginalSpans(diffResult, cancellationToken); var changedSpans = GetChangedSpans(diffResult, cancellationToken); var description = default(string); var allSpans = default(NormalizedSpanCollection); if (newDocument.SupportsSyntaxTree) { var newRoot = newDocument.GetSyntaxRootSynchronously(cancellationToken); var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) .Select(a => ConflictAnnotation.GetDescription(a)) .Distinct(); var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) .Select(a => WarningAnnotation.GetDescription(a)) .Distinct(); var suppressDiagnosticsNodes = newRoot.GetAnnotatedNodesAndTokens(SuppressDiagnosticsAnnotation.Kind); var suppressDiagnosticsSpans = suppressDiagnosticsNodes.Select(n => n.Span.ToSpan()).ToList(); AttachAnnotationsToBuffer(newBuffer, conflictSpans, warningSpans, suppressDiagnosticsSpans); description = conflictSpans.Count == 0 && warningSpans.Count == 0 ? null : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); } else { allSpans = new NormalizedSpanCollection(changedSpans); } var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); if (!originalLineSpans.Any()) { // This means that we have no differences (likely because of conflicts). // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. originalLineSpans = changedLineSpans; } // Create PreviewWorkspaces around the buffers to be displayed on the left and right // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. var leftDocument = oldDocument.Project .RemoveDocument(oldDocument.Id) .AddDocument(oldDocument.Name, oldBuffer.AsTextContainer().CurrentText, oldDocument.Folders, oldDocument.FilePath); var leftWorkspace = new PreviewWorkspace(leftDocument.Project.Solution); leftWorkspace.OpenDocument(leftDocument.Id); var rightWorkspace = new PreviewWorkspace( newDocument.WithText(newBuffer.AsTextContainer().CurrentText).Project.Solution); rightWorkspace.OpenDocument(newDocument.Id); return CreateChangedDocumentViewAsync( oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); }
public Task<object> CreateAddedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var newBuffer = CreateNewBuffer(document, cancellationToken); // Create PreviewWorkspace around the buffer to be displayed in the diff preview // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. var rightWorkspace = new PreviewWorkspace( document.WithText(newBuffer.AsTextContainer().CurrentText).Project.Solution); rightWorkspace.OpenDocument(document.Id); return CreateAddedDocumentPreviewViewCoreAsync(newBuffer, rightWorkspace, document, zoomLevel, cancellationToken); }
public Task<object> CreateRemovedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Note: We don't use the original buffer that is associated with oldDocument // (and possibly open in the editor) for oldBuffer below. This is because oldBuffer // will be used inside a projection buffer inside our inline diff preview below // and platform's implementation currently has a bug where projection buffers // are being leaked. This leak means that if we use the original buffer that is // currently visible in the editor here, the projection buffer span calculation // would be triggered every time user changes some code in this buffer (even though // the diff view would long have been dismissed by the time user edits the code) // resulting in crashes. Instead we create a new buffer from the same content. // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. var oldBuffer = CreateNewBuffer(document, cancellationToken); // Create PreviewWorkspace around the buffer to be displayed in the diff preview // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. var leftDocument = document.Project .RemoveDocument(document.Id) .AddDocument(document.Name, oldBuffer.AsTextContainer().CurrentText); var leftWorkspace = new PreviewWorkspace(leftDocument.Project.Solution); leftWorkspace.OpenDocument(leftDocument.Id); return CreateRemovedDocumentPreviewViewCoreAsync(oldBuffer, leftWorkspace, leftDocument, zoomLevel, cancellationToken); }
public void TestPreviewDiagnosticTagger() { using (var workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines("class { }")) using (var previewWorkspace = new PreviewWorkspace(workspace.CurrentSolution)) { // set up to listen diagnostic changes so that we can wait until it happens var diagnosticService = workspace.ExportProvider.GetExportedValue<IDiagnosticService>() as DiagnosticService; var taskSource = new TaskCompletionSource<DiagnosticsUpdatedArgs>(); diagnosticService.DiagnosticsUpdated += (s, a) => taskSource.TrySetResult(a); // preview workspace and owner of the solution now share solution and its underlying text buffer var hostDocument = workspace.Projects.First().Documents.First(); var buffer = hostDocument.GetTextBuffer(); // enable preview diagnostics previewWorkspace.OpenDocument(hostDocument.Id); previewWorkspace.EnableDiagnostic(); var foregroundService = new TestForegroundNotificationService(); var optionsService = workspace.Services.GetService<IOptionService>(); var squiggleWaiter = new ErrorSquiggleWaiter(); // create a tagger for preview workspace var taggerSource = new DiagnosticsSquiggleTaggerProvider.TagSource(buffer, foregroundService, diagnosticService, optionsService, squiggleWaiter); // wait up to 20 seconds for diagnostic service taskSource.Task.Wait(20000); if (!taskSource.Task.IsCompleted) { // something is wrong FatalError.Report(new System.Exception("not finished after 20 seconds")); } // wait for tagger squiggleWaiter.CreateWaitTask().PumpingWait(); var snapshot = buffer.CurrentSnapshot; var intervalTree = taggerSource.GetTagIntervalTreeForBuffer(buffer); var spans = intervalTree.GetIntersectingSpans(new SnapshotSpan(snapshot, 0, snapshot.Length)); taggerSource.TestOnly_Dispose(); Assert.Equal(1, spans.Count); } }
public void TestPreviewDiagnostic() { var diagnosticService = TestExportProvider.ExportProviderWithCSharpAndVisualBasic.GetExportedValue<IDiagnosticAnalyzerService>() as IDiagnosticUpdateSource; var taskSource = new TaskCompletionSource<DiagnosticsUpdatedArgs>(); diagnosticService.DiagnosticsUpdated += (s, a) => taskSource.TrySetResult(a); using (var previewWorkspace = new PreviewWorkspace(MefV1HostServices.Create(TestExportProvider.ExportProviderWithCSharpAndVisualBasic.AsExportProvider()))) { var solution = previewWorkspace.CurrentSolution .AddProject("project", "project.dll", LanguageNames.CSharp) .AddDocument("document", "class { }") .Project .Solution; Assert.True(previewWorkspace.TryApplyChanges(solution)); previewWorkspace.OpenDocument(previewWorkspace.CurrentSolution.Projects.First().DocumentIds[0]); previewWorkspace.EnableDiagnostic(); // wait 20 seconds taskSource.Task.Wait(20000); if (!taskSource.Task.IsCompleted) { // something is wrong FatalError.Report(new System.Exception("not finished after 20 seconds")); } var args = taskSource.Task.Result; Assert.True(args.Diagnostics.Length > 0); } }
public void TestPreviewOpenCloseFile() { using (var previewWorkspace = new PreviewWorkspace()) { var solution = previewWorkspace.CurrentSolution; var project = solution.AddProject("project", "project.dll", LanguageNames.CSharp); var document = project.AddDocument("document", ""); Assert.True(previewWorkspace.TryApplyChanges(document.Project.Solution)); previewWorkspace.OpenDocument(document.Id); Assert.Equal(1, previewWorkspace.GetOpenDocumentIds().Count()); Assert.True(previewWorkspace.IsDocumentOpen(document.Id)); previewWorkspace.CloseDocument(document.Id); Assert.Equal(0, previewWorkspace.GetOpenDocumentIds().Count()); Assert.False(previewWorkspace.IsDocumentOpen(document.Id)); } }
private DisposableToolTip CreateDisposableToolTip() { Presenter.AssertIsForeground(); // Create a new buffer that we'll show a preview for. We can't search for an // existing buffer because: // 1. the file may not be open. // 2. our results may not be in sync with what's actually in the editor. var textBuffer = CreateNewBuffer(); // Create the actual tooltip around the region of that text buffer we want to show. var toolTip = new ToolTip { Content = CreateToolTipContent(textBuffer), Background = (Brush)Application.Current.Resources[EnvironmentColors.ToolWindowBackgroundBrushKey] }; // Create a preview workspace for this text buffer and open it's corresponding // document. That way we'll get nice things like classification as well as the // reference highlight span. var newDocument = Document.WithText(textBuffer.AsTextContainer().CurrentText); var workspace = new PreviewWorkspace(newDocument.Project.Solution); workspace.OpenDocument(newDocument.Id); return new DisposableToolTip(toolTip, workspace); }
public void TestPreviewDiagnostic() { var diagnosticService = EditorServicesUtil.ExportProvider.GetExportedValue<IDiagnosticAnalyzerService>() as IDiagnosticUpdateSource; var taskSource = new TaskCompletionSource<DiagnosticsUpdatedArgs>(); diagnosticService.DiagnosticsUpdated += (s, a) => taskSource.TrySetResult(a); using (var previewWorkspace = new PreviewWorkspace(MefV1HostServices.Create(EditorServicesUtil.ExportProvider.AsExportProvider()))) { var solution = previewWorkspace.CurrentSolution .AddProject("project", "project.dll", LanguageNames.CSharp) .AddDocument("document", "class { }") .Project .Solution; Assert.True(previewWorkspace.TryApplyChanges(solution)); previewWorkspace.OpenDocument(previewWorkspace.CurrentSolution.Projects.First().DocumentIds[0]); previewWorkspace.EnableDiagnostic(); // wait 20 seconds taskSource.Task.Wait(20000); Assert.True(taskSource.Task.IsCompleted); var args = taskSource.Task.Result; Assert.True(args.Diagnostics.Length > 0); } }