public IProjectionBuffer CreateProjectionBuffer(IProjectionEditResolver projectionEditResolver, IList <object> trackingSpans, ProjectionBufferOptions options) { // projectionEditResolver is allowed to be null. if (trackingSpans == null) { throw new ArgumentNullException(nameof(trackingSpans)); } IProjectionBuffer buffer = new ProjectionBuffer(this, projectionEditResolver, ProjectionContentType, trackingSpans, _differenceService, _textDifferencingSelectorService.DefaultTextDifferencingService, options, _guardedOperations); RaiseProjectionBufferCreatedEvent(buffer); return(buffer); }
/// <summary> /// Creates a TestHostDocument backed by a projection buffer. The surface buffer is /// described by a markup string with {|name:|} style pointers to annotated spans that can /// be found in one of a set of provided documents. Unnamed spans in the documents (which /// must have both endpoints inside an annotated spans) and in the surface buffer markup are /// mapped and included in the resulting document. /// /// If the markup string has the caret indicator "$$", then the caret will be placed at the /// corresponding position. If it does not, then the first span mapped into the projection /// buffer that contains the caret from its document is used. /// /// The result is a new TestHostDocument backed by a projection buffer including tracking /// spans from any number of documents and inert text from the markup itself. /// /// As an example, consider surface buffer markup /// ABC [|DEF|] [|GHI[|JKL|]|]{|S1:|} [|MNO{|S2:|}PQR S$$TU|] {|S4:|}{|S5:|}{|S3:|} /// /// This contains 4 unnamed spans and references to 5 spans that should be found and /// included. Consider an included base document created from the following markup: /// /// public class C /// { /// public void M1() /// { /// {|S1:int [|abc[|d$$ef|]|] = goo;|} /// int y = goo; /// {|S2:int [|def|] = goo;|} /// int z = {|S3:123|} + {|S4:456|} + {|S5:789|}; /// } /// } /// /// The resulting projection buffer (with unnamed span markup preserved) would look like: /// ABC [|DEF|] [|GHI[|JKL|]|]int [|abc[|d$$ef|]|] = goo; [|MNOint [|def|] = goo;PQR S$$TU|] 456789123 /// /// The union of unnamed spans from the surface buffer markup and each of the projected /// spans is sorted as it would have been sorted by MarkupTestFile had it parsed the entire /// projection buffer as one file, which it would do in a stack-based manner. In our example, /// the order of the unnamed spans would be as follows: /// /// ABC [|DEF|] [|GHI[|JKL|]|]int [|abc[|d$$ef|]|] = goo; [|MNOint [|def|] = goo;PQR S$$TU|] 456789123 /// -----1 -----2 -------4 -----6 /// ------------3 --------------5 --------------------------------7 /// </summary> /// <param name="markup">Describes the surface buffer, and contains a mix of inert text, /// named spans and unnamed spans. Any named spans must contain only the name portion /// (e.g. {|Span1:|} which must match the name of a span in one of the baseDocuments. /// Annotated spans cannot be nested but they can be adjacent, in which case order will be /// preserved. The markup may also contain the caret indicator.</param> /// <param name="baseDocuments">The set of documents from which the projection buffer /// document will be composed.</param> /// <returns></returns> public TestHostDocument CreateProjectionBufferDocument( string markup, IList <TestHostDocument> baseDocuments, string path = "projectionbufferdocumentpath", ProjectionBufferOptions options = ProjectionBufferOptions.None, IProjectionEditResolver?editResolver = null) { GetSpansAndCaretFromSurfaceBufferMarkup(markup, baseDocuments, out var projectionBufferSpans, out var mappedSpans, out var mappedCaretLocation); var projectionBufferFactory = this.GetService <IProjectionBufferFactoryService>(); var projectionBuffer = projectionBufferFactory.CreateProjectionBuffer(editResolver, projectionBufferSpans, options); // Add in mapped spans from each of the base documents foreach (var document in baseDocuments) { mappedSpans[string.Empty] = mappedSpans.ContainsKey(string.Empty) ? mappedSpans[string.Empty] : ImmutableArray <TextSpan> .Empty; foreach (var span in document.SelectedSpans) { var snapshotSpan = span.ToSnapshotSpan(document.GetTextBuffer().CurrentSnapshot); var mappedSpan = projectionBuffer.CurrentSnapshot.MapFromSourceSnapshot(snapshotSpan).Single(); mappedSpans[string.Empty] = mappedSpans[string.Empty].Add(mappedSpan.ToTextSpan()); } // Order unnamed spans as they would be ordered by the normal span finding // algorithm in MarkupTestFile mappedSpans[string.Empty] = mappedSpans[string.Empty].OrderBy(s => s.End).ThenBy(s => - s.Start).ToImmutableArray(); foreach (var(key, spans) in document.AnnotatedSpans) { mappedSpans[key] = mappedSpans.ContainsKey(key) ? mappedSpans[key] : ImmutableArray <TextSpan> .Empty; foreach (var span in spans) { var snapshotSpan = span.ToSnapshotSpan(document.GetTextBuffer().CurrentSnapshot); var mappedSpan = projectionBuffer.CurrentSnapshot.MapFromSourceSnapshot(snapshotSpan).Cast <Span?>().SingleOrDefault(); if (mappedSpan == null) { // not all span on subject buffer needs to exist on surface buffer continue; } // but if they do, it must be only 1 mappedSpans[key] = mappedSpans[key].Add(mappedSpan.Value.ToTextSpan()); } } } var projectionDocument = new TestHostDocument( ExportProvider, languageServiceProvider: null, projectionBuffer.CurrentSnapshot.GetText(), path, mappedCaretLocation, mappedSpans, textBuffer: projectionBuffer); this.ProjectionDocuments.Add(projectionDocument); return(projectionDocument); }
/// <summary> /// Creates a TestHostDocument backed by a projection buffer. The surface buffer is /// described by a markup string with {|name:|} style pointers to annotated spans that can /// be found in one of a set of provided documents. Unnamed spans in the documents (which /// must have both endpoints inside an annotated spans) and in the surface buffer markup are /// mapped and included in the resulting document. /// /// If the markup string has the caret indicator "$$", then the caret will be placed at the /// corresponding position. If it does not, then the first span mapped into the projection /// buffer that contains the caret from its document is used. /// /// The result is a new TestHostDocument backed by a projection buffer including tracking /// spans from any number of documents and inert text from the markup itself. /// /// As an example, consider surface buffer markup /// ABC [|DEF|] [|GHI[|JKL|]|]{|S1:|} [|MNO{|S2:|}PQR S$$TU|] {|S4:|}{|S5:|}{|S3:|} /// /// This contains 4 unnamed spans and references to 5 spans that should be found and /// included. Consider an included base document created from the following markup: /// /// public class C /// { /// public void M1() /// { /// {|S1:int [|abc[|d$$ef|]|] = foo;|} /// int y = foo; /// {|S2:int [|def|] = foo;|} /// int z = {|S3:123|} + {|S4:456|} + {|S5:789|}; /// } /// } /// /// The resulting projection buffer (with unnamed span markup preserved) would look like: /// ABC [|DEF|] [|GHI[|JKL|]|]int [|abc[|d$$ef|]|] = foo; [|MNOint [|def|] = foo;PQR S$$TU|] 456789123 /// /// The union of unnamed spans from the surface buffer markup and each of the projected /// spans is sorted as it would have been sorted by MarkupTestFile had it parsed the entire /// projection buffer as one file, which it would do in a stack-based manner. In our example, /// the order of the unnamed spans would be as follows: /// /// ABC [|DEF|] [|GHI[|JKL|]|]int [|abc[|d$$ef|]|] = foo; [|MNOint [|def|] = foo;PQR S$$TU|] 456789123 /// -----1 -----2 -------4 -----6 /// ------------3 --------------5 --------------------------------7 /// </summary> /// <param name="markup">Describes the surface buffer, and contains a mix of inert text, /// named spans and unnamed spans. Any named spans must contain only the name portion /// (e.g. {|Span1:|} which must match the name of a span in one of the baseDocuments. /// Annotated spans cannot be nested but they can be adjacent, in which case order will be /// preserved. The markup may also contain the caret indicator.</param> /// <param name="baseDocuments">The set of documents from which the projection buffer /// document will be composed.</param> /// <returns></returns> public TestHostDocument CreateProjectionBufferDocument(string markup, IList <TestHostDocument> baseDocuments, string languageName, string path = "projectionbufferdocumentpath", ProjectionBufferOptions options = ProjectionBufferOptions.None, IProjectionEditResolver editResolver = null) { IList <object> projectionBufferSpans; Dictionary <string, IList <TextSpan> > mappedSpans; int?mappedCaretLocation; GetSpansAndCaretFromSurfaceBufferMarkup(markup, baseDocuments, out projectionBufferSpans, out mappedSpans, out mappedCaretLocation); var projectionBufferFactory = this.GetService <IProjectionBufferFactoryService>(); var projectionBuffer = projectionBufferFactory.CreateProjectionBuffer(editResolver, projectionBufferSpans, options); // Add in mapped spans from each of the base documents foreach (var document in baseDocuments) { mappedSpans[string.Empty] = mappedSpans.ContainsKey(string.Empty) ? mappedSpans[string.Empty] : new List <TextSpan>(); foreach (var span in document.SelectedSpans) { var snapshotSpan = span.ToSnapshotSpan(document.TextBuffer.CurrentSnapshot); var mappedSpan = projectionBuffer.CurrentSnapshot.MapFromSourceSnapshot(snapshotSpan).Single(); mappedSpans[string.Empty].Add(mappedSpan.ToTextSpan()); } // Order unnamed spans as they would be ordered by the normal span finding // algorithm in MarkupTestFile mappedSpans[string.Empty] = mappedSpans[string.Empty].OrderBy(s => s.End).ThenBy(s => - s.Start).ToList(); foreach (var kvp in document.AnnotatedSpans) { mappedSpans[kvp.Key] = mappedSpans.ContainsKey(kvp.Key) ? mappedSpans[kvp.Key] : new List <TextSpan>(); foreach (var span in kvp.Value) { var snapshotSpan = span.ToSnapshotSpan(document.TextBuffer.CurrentSnapshot); var mappedSpan = projectionBuffer.CurrentSnapshot.MapFromSourceSnapshot(snapshotSpan).Single(); mappedSpans[kvp.Key].Add(mappedSpan.ToTextSpan()); } } } var languageServices = this.Services.GetLanguageServices(languageName); var projectionDocument = new TestHostDocument( TestExportProvider.ExportProviderWithCSharpAndVisualBasic, languageServices, projectionBuffer, path, mappedCaretLocation, mappedSpans); this.ProjectionDocuments.Add(projectionDocument); return(projectionDocument); }