public static IEnumerable <LineInfo> SplitLines(string text) { NewLineLocation nextLine; List <LineInfo> lines = new List <LineInfo>(); int lastLineEnd = 0; while ((nextLine = NewLineLocation.FindNewLine(text, lastLineEnd)).EndIndex != lastLineEnd) { yield return(new LineInfo( lastLineEnd, nextLine.EndIndex - lastLineEnd - nextLine.Kind.GetSize(), lines.Count, nextLine.Kind )); lastLineEnd = nextLine.EndIndex; } if (lastLineEnd != text.Length) { yield return(new LineInfo( lastLineEnd, text.Length - lastLineEnd, lines.Count, NewLineKind.None )); } }
public static IEnumerable <LineInfo> SplitLines(string text, int firstLineNumber = 0) { NewLineLocation nextLine; var lineNo = firstLineNumber; var lastLineEnd = 0; while ((nextLine = NewLineLocation.FindNewLine(text, lastLineEnd)).EndIndex != lastLineEnd) { yield return(new LineInfo( lastLineEnd, nextLine.EndIndex - lastLineEnd - nextLine.Kind.GetSize(), lineNo++, nextLine.Kind )); lastLineEnd = nextLine.EndIndex; } if (lastLineEnd != text.Length) { yield return(new LineInfo( lastLineEnd, text.Length - lastLineEnd, lineNo++, NewLineKind.None )); } }
private static IEnumerable<NewLineLocation> SplitLines(StringBuilder text) { NewLineLocation nextLine; // TODO: Avoid string allocation by operating directly on StringBuilder var str = text.ToString(); int lastLineEnd = 0; while ((nextLine = NewLineLocation.FindNewLine(str, lastLineEnd)).EndIndex != lastLineEnd) { yield return nextLine; lastLineEnd = nextLine.EndIndex; } if (lastLineEnd != str.Length) { yield return nextLine; } }
internal NewLineLocation[] GetLineLocations(int version) { var ver = _snapshots.Peek().Version; NewLineLocation[] initial; lock (_lineCache) { // Precalculated for this version if (_lineCache.TryGetValue(version, out initial)) { return(initial); } // We simply can't provide these lines any more if (ver.VersionNumber > version) { throw new NotSupportedException($"version {version} is no longer in memory"); } int fromVersion = version; // Get the last available set of newlines while (--fromVersion >= ver.VersionNumber) { if (_lineCache.TryGetValue(fromVersion, out initial)) { break; } } // Create the initial set if it wasn't cached if (initial == null) { fromVersion = ver.VersionNumber; _lineCache[fromVersion] = initial = LinesToLineEnds(_snapshots.Peek().Lines).ToArray(); } while (ver.Next != null && ver.VersionNumber < fromVersion) { ver = ver.Next; } #if DEBUG var appliedVersions = new List <ITextVersion>(); #endif List <NewLineLocation> asLengths = null; while (ver.Changes != null && ver.VersionNumber < version) { #if DEBUG appliedVersions.Add(ver); #endif if (asLengths == null) { asLengths = LineEndsToLineLengths(initial).ToList(); } // Apply the changes from this version to the line lengths foreach (var c in ver.Changes.Reverse()) { var oldLoc = NewLineLocation.IndexToLocation(initial, c.OldPosition); int lineNo = oldLoc.Line - 1; while (asLengths.Count <= lineNo) { asLengths.Add(new NewLineLocation(0, NewLineKind.None)); } if (c.OldLength > 0) { var line = asLengths[lineNo]; // Deletion may span lines, so combine them until we can delete int cutAtCol = oldLoc.Column - 1; for (int toRemove = c.OldLength; lineNo < asLengths.Count; lineNo += 1) { line = asLengths[lineNo]; int lineLen = line.EndIndex - cutAtCol; cutAtCol = 0; if (line.Kind == NewLineKind.CarriageReturnLineFeed && toRemove == lineLen - 1) { // Special case of deleting just the '\r' from a '\r\n' ending asLengths[lineNo] = new NewLineLocation(line.EndIndex - toRemove, NewLineKind.LineFeed); break; } else if (toRemove < lineLen) { asLengths[lineNo] = new NewLineLocation(line.EndIndex - toRemove, line.Kind); break; } else { asLengths[lineNo] = new NewLineLocation(line.EndIndex - lineLen, NewLineKind.None); toRemove -= lineLen; if (toRemove <= 0) { break; } } } } lineNo = oldLoc.Line - 1; if (!string.IsNullOrEmpty(c.NewText)) { var line = asLengths[lineNo]; NewLineLocation addedLine = new NewLineLocation(0, NewLineKind.None); int lastLineEnd = 0, cutAtCol = oldLoc.Column - 1; if (cutAtCol > line.EndIndex - line.Kind.GetSize() && lineNo + 1 < asLengths.Count) { cutAtCol = 0; lineNo += 1; line = asLengths[lineNo]; } while ((addedLine = NewLineLocation.FindNewLine(c.NewText, lastLineEnd)).Kind != NewLineKind.None) { if (cutAtCol > 0) { asLengths[lineNo] = new NewLineLocation(line.EndIndex - cutAtCol, line.Kind); lastLineEnd -= cutAtCol; cutAtCol = 0; } line = new NewLineLocation(addedLine.EndIndex - lastLineEnd, addedLine.Kind); asLengths.Insert(lineNo++, line); lastLineEnd = addedLine.EndIndex; } if (addedLine.EndIndex > lastLineEnd) { if (lineNo < asLengths.Count) { line = asLengths[lineNo]; asLengths[lineNo] = line = new NewLineLocation(line.EndIndex + addedLine.EndIndex - lastLineEnd, line.Kind); } else { asLengths.Add(new NewLineLocation(addedLine.EndIndex - lastLineEnd, NewLineKind.None)); } } } } initial = LineLengthsToLineEnds(asLengths).ToArray(); if (asLengths.Count != initial.Length) { asLengths = null; } _lineCache[ver.VersionNumber + 1] = initial; #if DEBUG if (System.Diagnostics.Debugger.IsAttached && initial.Length > 0 && ver.Next != null && initial.Last().EndIndex != ver.Next.Length) { // Line calculations were wrong // Asserts do not work properly here, so we just break if the debugger is attached System.Diagnostics.Debugger.Break(); } #endif ver = ver.Next; } return(initial); } }