// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // // This file contain implementations details that are subject to change without notice. // Use at your own risk. // using System; using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.Utilities; namespace Microsoft.VisualStudio.Text.Differencing.Implementation { internal interface ITokenizedStringListInternal : ITokenizedStringList { string OriginalSubstring(int startIndex, int length); } /// /// Tokenizes the string into abutting and non-overlapping segments. /// /// /// This class implements IList so that it can be used with /// , which finds the differences between two sequences represented /// as ILists. /// Most of the members of the IList interface are unimplemented. The only /// implemented methods are the array accessor getter (operator []), Count, /// and IsReadOnly. /// internal abstract class TokenizedStringList : ITokenizedStringListInternal { protected List Tokens = new List(); private readonly string original; private readonly SnapshotSpan originalSpan; /// /// Creates a tokenized string list from the original string. /// Any derived class must initialize the Tokens list in its own constructor. /// /// The original string. protected TokenizedStringList(string original) { if (original == null) throw new ArgumentNullException(nameof(original)); this.original = original; } protected TokenizedStringList(SnapshotSpan originalSpan) { this.originalSpan = originalSpan; } /// /// The original string that was tokenized. /// public string Original { get { // A call to GetText() here could be very expensive in memory. Be careful! return original ?? originalSpan.GetText(); } } internal int OriginalLength { get { return (original != null) ? original.Length : originalSpan.Length; } } public string OriginalSubstring(int startIndex, int length) { if (original != null) { return original.Substring(startIndex, length); } else { ITextSnapshot snap = originalSpan.Snapshot; return snap.GetText(originalSpan.Start.Position + startIndex, length); } } /// /// Maps the index of an element to its span in the original list. /// /// The index of the element in the element list. /// The span of the element. /// The specified index is either negative or exceeds the list's Count property. /// This method returns a zero-length span at the end of the string if index /// is equal to the list's Count property. public Span GetElementInOriginal(int index) { //Pure support for backward compatibility if (index == this.Count) { return new Span(this.OriginalLength, 0); } return this.Tokens[index]; } /// /// Maps a span of elements in this list to the span in the original list. /// /// The span of elements in the elements list. /// The span mapped onto the original list. public Span GetSpanInOriginal(Span span) { //Pure support for backward compatibility if (span.Start == this.Tokens.Count) { return new Span(this.OriginalLength, 0); } int start = this.Tokens[span.Start].Start; int end = (span.Length == 0) ? start : this.Tokens[span.End - 1].End; return Span.FromBounds(start, end); } /// /// Gets a string of the given element. /// /// The index into the list of elements. /// The element, as a string. /// The setter of this property throws a NotImplementedException. public string this[int index] { get { // The out of range check will happen in GetElementInOriginal Span span = GetElementInOriginal(index); return this.OriginalSubstring(span.Start, span.Length); } set { throw new NotSupportedException(); } } internal char CharacterAt(int offset) { return (original != null) ? original[offset] : originalSpan.Snapshot[originalSpan.Start.Position + offset]; } /// /// The number of elements in the list. /// public int Count { get { return this.Tokens.Count; } } /// /// Determines whether this list is read-only. It always returnes true. /// public bool IsReadOnly { get { return true; } } #region Not Implemented /// /// Not implemented /// /// /// public int IndexOf(string item) { throw new NotSupportedException(); } /// /// Not implemented. /// /// /// public void Insert(int index, string item) { throw new NotSupportedException(); } /// /// Not implemented. /// /// public void RemoveAt(int index) { throw new NotSupportedException(); } /// /// Not implemented. /// /// public void Add(string item) { throw new NotSupportedException(); } /// /// Not implemented. /// public void Clear() { throw new NotSupportedException(); } /// /// Not implemented. /// public bool Contains(string item) { throw new NotSupportedException(); } /// /// Not implemented. /// public void CopyTo(string[] array, int arrayIndex) { throw new NotSupportedException(); } /// /// Not implemented. /// public bool Remove(string item) { throw new NotSupportedException(); } #endregion #region IEnumerable Members /// /// Gets the enumerator of type string. /// /// The enumerator of type string. public IEnumerator GetEnumerator() { for (int i = 0; i < Count; i++) yield return this[i]; } #endregion #region IEnumerable Members /// /// Gets the untyped enumerator. /// /// The untyped enumerator. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } }