protected static int GetWrappedLineTrimmedLength(BufferLine line, BufferLine nextLine, int cols) { // If this is the last row in the wrapped line, get the actual trimmed length if (nextLine == null) { return(line.GetTrimmedLength()); } // Detect whether the following line starts with a wide character and the end of the current line // is null, if so then we can be pretty sure the null character should be excluded from the line // length] bool endsInNull = !(line.HasContent(cols - 1)) && line.GetWidth(cols - 1) == 1; bool followingLineStartsWithWide = nextLine.GetWidth(0) == 2; if (endsInNull && followingLineStartsWithWide) { return(cols - 1); } return(cols); }
public override void Reflow(int newCols, int newRows, int oldCols, int oldRows) { // Gather all BufferLines that need to be inserted into the Buffer here so that they can be // batched up and only committed once List <InsertionSet> toInsert = new List <InsertionSet> (); int countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them for (int y = Buffer.Lines.Length - 1; y >= 0; y--) { // Check whether this line is a problem or not, if not skip it BufferLine nextLine = Buffer.Lines [y]; int lineLength = nextLine.GetTrimmedLength(); if (!nextLine.IsWrapped && lineLength <= newCols) { continue; } // Gather wrapped lines and adjust y to be the starting line List <BufferLine> wrappedLines = new List <BufferLine> (); wrappedLines.Add(nextLine); while (nextLine.IsWrapped && y > 0) { nextLine = Buffer.Lines [--y]; wrappedLines.Insert(0, nextLine); } // If these lines contain the cursor don't touch them, the program will handle fixing up // wrapped lines with the cursor int absoluteY = Buffer.YBase + Buffer.Y; if (absoluteY >= y && absoluteY < y + wrappedLines.Count) { continue; } int lastLineLength = wrappedLines [wrappedLines.Count - 1].GetTrimmedLength(); int [] destLineLengths = GetNewLineLengths(wrappedLines, oldCols, newCols); int linesToAdd = destLineLengths.Length - wrappedLines.Count; int trimmedLines; if (Buffer.YBase == 0 && Buffer.Y != Buffer.Lines.Length - 1) { // If the top section of the buffer is not yet filled trimmedLines = Math.Max(0, Buffer.Y - Buffer.Lines.MaxLength + linesToAdd); } else { trimmedLines = Math.Max(0, Buffer.Lines.Length - Buffer.Lines.MaxLength + linesToAdd); } // Add the new lines List <BufferLine> newLines = new List <BufferLine> (); for (int i = 0; i < linesToAdd; i++) { BufferLine newLine = Buffer.GetBlankLine(CharData.DefaultAttr, true); newLines.Add(newLine); } if (newLines.Count > 0) { toInsert.Add(new InsertionSet { Start = y + wrappedLines.Count + countToInsert, Lines = newLines.ToArray() }); countToInsert += newLines.Count; } newLines.ForEach(l => wrappedLines.Add(l)); // Copy buffer data to new locations, this needs to happen backwards to do in-place int destLineIndex = destLineLengths.Length - 1; // Math.floor(cellsNeeded / newCols); int destCol = destLineLengths [destLineIndex]; // cellsNeeded % newCols; if (destCol == 0) { destLineIndex--; destCol = destLineLengths [destLineIndex]; } int srcLineIndex = wrappedLines.Count - linesToAdd - 1; int srcCol = lastLineLength; while (srcLineIndex >= 0) { int cellsToCopy = Math.Min(srcCol, destCol); wrappedLines [destLineIndex].CopyCellsFrom(wrappedLines [srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy); destCol -= cellsToCopy; if (destCol == 0) { destLineIndex--; if (destLineIndex >= 0) { destCol = destLineLengths [destLineIndex]; } } srcCol -= cellsToCopy; if (srcCol == 0) { srcLineIndex--; int wrappedLinesIndex = Math.Max(srcLineIndex, 0); srcCol = GetWrappedLineTrimmedLength(wrappedLines, wrappedLinesIndex, oldCols); } } // Null out the end of the line ends if a wide character wrapped to the following line for (int i = 0; i < wrappedLines.Count; i++) { if (destLineLengths [i] < newCols) { wrappedLines [i] [destLineLengths [i]] = CharData.Null; } } // Adjust viewport as needed int viewportAdjustments = linesToAdd - trimmedLines; while (viewportAdjustments-- > 0) { if (Buffer.YBase == 0) { if (Buffer.Y < newRows - 1) { Buffer.Y++; Buffer.Lines.Pop(); } else { Buffer.YBase++; Buffer.YDisp++; } } else { // Ensure ybase does not exceed its maximum value if (Buffer.YBase < Math.Min(Buffer.Lines.MaxLength, Buffer.Lines.Length + countToInsert) - newRows) { if (Buffer.YBase == Buffer.YDisp) { Buffer.YDisp++; } Buffer.YBase++; } } } Buffer.SavedY = Math.Min(Buffer.SavedY + linesToAdd, Buffer.YBase + newRows - 1); } Rearrange(toInsert, countToInsert); }
public BufferLine(BufferLine other) { data = new CharData [other.data.Length]; other.data.CopyTo(data, 0); IsWrapped = other.IsWrapped; }
/// <summary> /// Evaluates and returns indexes to be removed after a reflow larger occurs. Lines will be removed /// when a wrapped line unwraps. /// </summary> /// <param name="lines">The buffer lines</param> /// <param name="oldCols">The columns before resize</param> /// <param name="newCols">The columns after resize</param> /// <param name="bufferAbsoluteY"></param> /// <param name="nullCharacter"></param> int [] GetLinesToRemove(CircularList <BufferLine> lines, int oldCols, int newCols, int bufferAbsoluteY, CharData nullCharacter) { // Gather all BufferLines that need to be removed from the Buffer here so that they can be // batched up and only committed once List <int> toRemove = new List <int> (); for (int y = 0; y < lines.Length - 1; y++) { // Check if this row is wrapped int i = y; BufferLine nextLine = lines [++i]; if (!nextLine.IsWrapped) { continue; } // Check how many lines it's wrapped for List <BufferLine> wrappedLines = new List <BufferLine> (lines.Length - y); wrappedLines.Add(lines [y]); while (i < lines.Length && nextLine.IsWrapped) { wrappedLines.Add(nextLine); nextLine = lines [++i]; } // If these lines contain the cursor don't touch them, the program will handle fixing up wrapped // lines with the cursor if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { y += wrappedLines.Count - 1; continue; } // Copy buffer data to new locations int destLineIndex = 0; int destCol = GetWrappedLineTrimmedLength(Buffer.Lines, destLineIndex, oldCols); int srcLineIndex = 1; int srcCol = 0; while (srcLineIndex < wrappedLines.Count) { int srcTrimmedTineLength = GetWrappedLineTrimmedLength(wrappedLines, srcLineIndex, oldCols); int srcRemainingCells = srcTrimmedTineLength - srcCol; int destRemainingCells = newCols - destCol; int cellsToCopy = Math.Min(srcRemainingCells, destRemainingCells); wrappedLines [destLineIndex].CopyCellsFrom(wrappedLines [srcLineIndex], srcCol, destCol, cellsToCopy); destCol += cellsToCopy; if (destCol == newCols) { destLineIndex++; destCol = 0; } srcCol += cellsToCopy; if (srcCol == srcTrimmedTineLength) { srcLineIndex++; srcCol = 0; } // Make sure the last cell isn't wide, if it is copy it to the current dest if (destCol == 0 && destLineIndex != 0) { if (wrappedLines [destLineIndex - 1].GetWidth(newCols - 1) == 2) { wrappedLines [destLineIndex].CopyCellsFrom(wrappedLines [destLineIndex - 1], newCols - 1, destCol++, 1); // Null out the end of the last row wrappedLines [destLineIndex - 1].ReplaceCells(newCols - 1, 1, nullCharacter); } } } // Clear out remaining cells or fragments could remain; wrappedLines [destLineIndex].ReplaceCells(destCol, newCols, nullCharacter); // Work backwards and remove any rows at the end that only contain null cells int countToRemove = 0; for (int ix = wrappedLines.Count - 1; ix > 0; ix--) { if (ix > destLineIndex || wrappedLines [ix].GetTrimmedLength() == 0) { countToRemove++; } else { break; } } if (countToRemove > 0) { toRemove.Add(y + wrappedLines.Count - countToRemove); // index toRemove.Add(countToRemove); } y += wrappedLines.Count - 1; } return(toRemove.ToArray()); }