reflow_strategy_wider.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import 'dart:math';
  2. import 'package:xterm/buffer/buffer.dart';
  3. import 'package:xterm/buffer/buffer_line.dart';
  4. import 'package:xterm/buffer/reflow_strategy.dart';
  5. import 'package:xterm/utli/circular_list.dart';
  6. class ReflowStrategyWider extends ReflowStrategy {
  7. ReflowStrategyWider(Buffer buffer) : super(buffer);
  8. @override
  9. void reflow(int newCols, int newRows, int oldCols, int oldRows) {
  10. final toRemove = _getLinesToRemove(buffer.lines, oldCols, newCols);
  11. if (toRemove.length > 0) {
  12. final newLayoutResult = _createNewLayout(buffer.lines, toRemove);
  13. _applyNewLayout(buffer.lines, newLayoutResult.layout);
  14. _adjustViewport(newCols, newRows, newLayoutResult.removedCount);
  15. }
  16. }
  17. /// <summary>
  18. /// Evaluates and returns indexes to be removed after a reflow larger occurs. Lines will be removed
  19. /// when a wrapped line unwraps.
  20. /// </summary>
  21. /// <param name="lines">The buffer lines</param>
  22. /// <param name="oldCols">The columns before resize</param>
  23. /// <param name="newCols">The columns after resize</param>
  24. /// <param name="bufferAbsoluteY"></param>
  25. /// <param name="nullCharacter"></param>
  26. List<int> _getLinesToRemove(
  27. CircularList<BufferLine> lines, int oldCols, int newCols) {
  28. // Gather all BufferLines that need to be removed from the Buffer here so that they can be
  29. // batched up and only committed once
  30. final toRemove = List<int>.empty(growable: true);
  31. for (int y = 0; y < lines.length - 1; y++) {
  32. // Check if this row is wrapped
  33. int i = y;
  34. BufferLine nextLine = lines[++i]!;
  35. if (!nextLine.isWrapped) {
  36. continue;
  37. }
  38. // Check how many lines it's wrapped for
  39. final wrappedLines = List<BufferLine>.empty(growable: true);
  40. wrappedLines.add(lines[y]!);
  41. while (i < lines.length && nextLine.isWrapped) {
  42. wrappedLines.add(nextLine);
  43. nextLine = lines[++i]!;
  44. }
  45. final bufferAbsoluteY = buffer.cursorY + buffer.scrollOffsetFromTop;
  46. // If these lines contain the cursor don't touch them, the program will handle fixing up wrapped
  47. // lines with the cursor
  48. if (bufferAbsoluteY >= y && bufferAbsoluteY < i) {
  49. y += wrappedLines.length - 1;
  50. continue;
  51. }
  52. // Copy buffer data to new locations
  53. int destLineIndex = 0;
  54. int destCol = ReflowStrategy.getWrappedLineTrimmedLengthFromCircularList(
  55. buffer.lines, destLineIndex, oldCols);
  56. int srcLineIndex = 1;
  57. int srcCol = 0;
  58. while (srcLineIndex < wrappedLines.length) {
  59. int srcTrimmedTineLength =
  60. ReflowStrategy.getWrappedLineTrimmedLengthFromLines(
  61. wrappedLines, srcLineIndex, oldCols);
  62. int srcRemainingCells = srcTrimmedTineLength - srcCol;
  63. int destRemainingCells = newCols - destCol;
  64. int cellsToCopy = min(srcRemainingCells, destRemainingCells);
  65. wrappedLines[destLineIndex].copyCellsFrom(
  66. wrappedLines[srcLineIndex], srcCol, destCol, cellsToCopy);
  67. destCol += cellsToCopy;
  68. if (destCol == newCols) {
  69. destLineIndex++;
  70. destCol = 0;
  71. }
  72. srcCol += cellsToCopy;
  73. if (srcCol == srcTrimmedTineLength) {
  74. srcLineIndex++;
  75. srcCol = 0;
  76. }
  77. // Make sure the last cell isn't wide, if it is copy it to the current dest
  78. if (destCol == 0 && destLineIndex != 0) {
  79. if (wrappedLines[destLineIndex - 1].cellGetWidth(newCols - 1) == 2) {
  80. wrappedLines[destLineIndex].copyCellsFrom(
  81. wrappedLines[destLineIndex - 1], newCols - 1, destCol++, 1);
  82. // Null out the end of the last row
  83. wrappedLines[destLineIndex - 1]
  84. .erase(buffer.terminal.cursor, newCols - 1, newCols, false);
  85. }
  86. }
  87. }
  88. // Clear out remaining cells or fragments could remain;
  89. wrappedLines[destLineIndex]
  90. .erase(buffer.terminal.cursor, destCol, newCols);
  91. // Work backwards and remove any rows at the end that only contain null cells
  92. int countToRemove = 0;
  93. for (int ix = wrappedLines.length - 1; ix > 0; ix--) {
  94. if (ix > destLineIndex ||
  95. wrappedLines[ix].getTrimmedLength(oldCols) == 0) {
  96. countToRemove++;
  97. } else {
  98. break;
  99. }
  100. }
  101. if (countToRemove > 0) {
  102. toRemove.add(y + wrappedLines.length - countToRemove); // index
  103. toRemove.add(countToRemove);
  104. }
  105. y += wrappedLines.length - 1;
  106. }
  107. return toRemove;
  108. }
  109. LayoutResult _createNewLayout(
  110. CircularList<BufferLine> lines, List<int> toRemove) {
  111. var layout = new CircularList<int>(lines.length);
  112. // First iterate through the list and get the actual indexes to use for rows
  113. int nextToRemoveIndex = 0;
  114. int nextToRemoveStart = toRemove[nextToRemoveIndex];
  115. int countRemovedSoFar = 0;
  116. for (int i = 0; i < lines.length; i++) {
  117. if (nextToRemoveStart == i) {
  118. int countToRemove = toRemove[++nextToRemoveIndex];
  119. // Tell markers that there was a deletion
  120. //lines.onDeleteEmitter.fire ({
  121. // index: i - countRemovedSoFar,
  122. // amount: countToRemove
  123. //});
  124. i += countToRemove - 1;
  125. countRemovedSoFar += countToRemove;
  126. nextToRemoveStart = lines.length + 1; //was: int.max
  127. if (nextToRemoveIndex < toRemove.length - 1)
  128. nextToRemoveStart = toRemove[++nextToRemoveIndex];
  129. } else {
  130. layout.push(i);
  131. }
  132. }
  133. return new LayoutResult(layout, countRemovedSoFar);
  134. }
  135. void _applyNewLayout(
  136. CircularList<BufferLine> lines, CircularList<int> newLayout) {
  137. var newLayoutLines = new CircularList<BufferLine>(lines.length);
  138. for (int i = 0; i < newLayout.length; i++) {
  139. newLayoutLines.push(lines[newLayout[i]!]!);
  140. }
  141. // Rearrange the list
  142. for (int i = 0; i < newLayoutLines.length; i++) {
  143. lines[i] = newLayoutLines[i];
  144. }
  145. lines.length = newLayout.length;
  146. }
  147. void _adjustViewport(int newCols, int newRows, int countRemoved) {
  148. int viewportAdjustments = countRemoved;
  149. while (viewportAdjustments-- > 0) {
  150. if (buffer.lines.length <= buffer.terminal.viewHeight) {
  151. //cursor is not at the top
  152. if (buffer.cursorY > 0) {
  153. buffer.moveCursorY(-1);
  154. }
  155. //buffer doesn't have enough lines
  156. if (buffer.lines.length < buffer.terminal.viewHeight) {
  157. // Add an extra row at the bottom of the viewport
  158. buffer.lines.push(BufferLine());
  159. }
  160. }
  161. }
  162. buffer.adjustSavedCursor(0, -countRemoved);
  163. }
  164. }
  165. class LayoutResult {
  166. CircularList<int> layout;
  167. int removedCount;
  168. LayoutResult(this.layout, this.removedCount);
  169. }