buffer_reflow.dart 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import 'dart:math';
  2. import 'package:xterm/buffer/cell_attr.dart';
  3. import 'package:dart_numeric'
  4. import 'buffer.dart';
  5. import 'buffer_line.dart';
  6. class LayoutResult {
  7. LayoutResult(this.layout, this.removedCount);
  8. final List<int> layout;
  9. final int removedCount;
  10. }
  11. class BufferReflow {
  12. BufferReflow(this._buffer, this._emptyCellAttr);
  13. final Buffer _buffer;
  14. final CellAttr _emptyCellAttr;
  15. void doReflow(int colsBefore, int colsAfter) {
  16. if(colsBefore == colsAfter) {
  17. return;
  18. }
  19. if(colsAfter > colsBefore) {
  20. //got larger
  21. _reflowLarger(colsBefore, colsAfter);
  22. } else {
  23. //got smaller
  24. _reflowSmaller(colsBefore, colsAfter);
  25. }
  26. }
  27. void _reflowLarger(int colsBefore, int colsAfter) {
  28. var toRemove = _reflowLargerGetLinesToRemove(colsBefore, colsAfter);
  29. if (toRemove.length > 0) {
  30. var newLayoutResult = _reflowLargerCreateNewLayout(_buffer.lines, toRemove);
  31. _reflowLargerApplyNewLayout(_buffer.lines, newLayoutResult.layout);
  32. _reflowLargerAdjustViewport(colsBefore, colsAfter, newLayoutResult.removedCount);
  33. }
  34. }
  35. void _reflowSmaller(int colsBefore, int colsAfter) {
  36. }
  37. void _reflowLargerAdjustViewport(int colsBefore, int colsAfter, int countRemoved) {
  38. // Adjust viewport based on number of items removed
  39. var viewportAdjustments = countRemoved;
  40. while (viewportAdjustments-- > 0) {
  41. //viewport is at the top
  42. if (_buffer.lines.length <= _buffer.terminal.viewHeight) {
  43. //cursor is not at the top
  44. if (_buffer.cursorY > 0) {
  45. _buffer.moveCursorY(-1);
  46. }
  47. //buffer doesn't have enough lines
  48. if (_buffer.lines.length < _buffer.terminal.viewHeight) {
  49. // Add an extra row at the bottom of the viewport
  50. _buffer.lines.add(new BufferLine(numOfCells: colsAfter, attr: _emptyCellAttr));
  51. }
  52. } else {
  53. //Nothing to do here due to the way scrolling is handled
  54. // //user didn't scroll
  55. // if (this.ydisp === this.ybase) {
  56. // //scroll viewport according to...
  57. // this.ydisp--;
  58. // }
  59. // //base window
  60. // this.ybase--;
  61. }
  62. }
  63. //TODO: adjust buffer content to max length
  64. _buffer.adjustSavedCursor(0, -countRemoved);
  65. }
  66. void _reflowLargerApplyNewLayout(List<BufferLine> lines, List<int> newLayout) {
  67. var newLayoutLines = List<BufferLine>.empty();
  68. for (int i = 0; i < newLayout.length; i++) {
  69. newLayoutLines.add(lines[newLayout [i]]);
  70. }
  71. // Rearrange the list
  72. for (int i = 0; i < newLayoutLines.length; i++) {
  73. lines[i] = newLayoutLines[i];
  74. }
  75. lines.removeRange(newLayoutLines.length, lines.length - 1);
  76. }
  77. LayoutResult _reflowLargerCreateNewLayout(List<BufferLine> lines, List<int> toRemove) {
  78. var layout = List<int>.empty();
  79. // First iterate through the list and get the actual indexes to use for rows
  80. int nextToRemoveIndex = 0;
  81. int nextToRemoveStart = toRemove [nextToRemoveIndex];
  82. int countRemovedSoFar = 0;
  83. for (int i = 0; i < lines.length; i++) {
  84. if (nextToRemoveStart == i) {
  85. int countToRemove = toRemove [++nextToRemoveIndex];
  86. // Tell markers that there was a deletion
  87. //lines.onDeleteEmitter.fire ({
  88. // index: i - countRemovedSoFar,
  89. // amount: countToRemove
  90. //});
  91. i += countToRemove - 1;
  92. countRemovedSoFar += countToRemove;
  93. nextToRemoveStart = lines.length + 1;
  94. if (nextToRemoveIndex < toRemove.length - 1)
  95. nextToRemoveStart = toRemove [++nextToRemoveIndex];
  96. } else {
  97. layout.add(i);
  98. }
  99. }
  100. return LayoutResult (layout, countRemovedSoFar);
  101. }
  102. List<int> _reflowLargerGetLinesToRemove(int colsBefore, int colsAfter) {
  103. List<int> toRemove = List<int>.empty();
  104. for (int y = 0; y < _buffer.lines.length - 1; y++) {
  105. // Check if this row is wrapped
  106. int i = y;
  107. BufferLine nextLine = _buffer.lines[++i];
  108. if (!nextLine.isWrapped) {
  109. continue;
  110. }
  111. // Check how many lines it's wrapped for
  112. List<BufferLine> wrappedLines = List<BufferLine>.empty();
  113. wrappedLines.add(_buffer.lines[y]);
  114. while (i < _buffer.lines.length && nextLine.isWrapped) {
  115. wrappedLines.add(nextLine);
  116. nextLine = _buffer.lines[++i];
  117. }
  118. final bufferAbsoluteY = _buffer.cursorY - _buffer.scrollOffsetFromBottom;
  119. // If these lines contain the cursor don't touch them, the program will handle fixing up wrapped
  120. // lines with the cursor
  121. if (bufferAbsoluteY >= y && bufferAbsoluteY < i) {
  122. y += wrappedLines.length - 1;
  123. continue;
  124. }
  125. // Copy buffer data to new locations
  126. int destLineIndex = 0;
  127. int destCol = _getWrappedLineTrimmedLengthRow(_buffer.lines, destLineIndex, colsBefore);
  128. int srcLineIndex = 1;
  129. int srcCol = 0;
  130. while (srcLineIndex < wrappedLines.length) {
  131. int srcTrimmedTineLength = _getWrappedLineTrimmedLengthRow(wrappedLines, srcLineIndex, colsBefore);
  132. int srcRemainingCells = srcTrimmedTineLength - srcCol;
  133. int destRemainingCells = colsAfter - destCol;
  134. int cellsToCopy = min(srcRemainingCells, destRemainingCells);
  135. wrappedLines [destLineIndex].copyCellsFrom (wrappedLines [srcLineIndex], srcCol, destCol, cellsToCopy);
  136. destCol += cellsToCopy;
  137. if (destCol == colsAfter) {
  138. destLineIndex++;
  139. destCol = 0;
  140. }
  141. srcCol += cellsToCopy;
  142. if (srcCol == srcTrimmedTineLength) {
  143. srcLineIndex++;
  144. srcCol = 0;
  145. }
  146. // Make sure the last cell isn't wide, if it is copy it to the current dest
  147. if (destCol == 0 && destLineIndex != 0) {
  148. if (wrappedLines [destLineIndex - 1].getWidthAt(colsAfter - 1) == 2) {
  149. wrappedLines [destLineIndex].copyCellsFrom (wrappedLines [destLineIndex - 1], colsAfter - 1, destCol++, 1);
  150. // Null out the end of the last row
  151. wrappedLines [destLineIndex - 1].erase (_emptyCellAttr, colsAfter - 1, colsAfter);
  152. }
  153. }
  154. }
  155. // Clear out remaining cells or fragments could remain;
  156. wrappedLines [destLineIndex].erase(_emptyCellAttr, destCol, colsAfter);
  157. // Work backwards and remove any rows at the end that only contain null cells
  158. int countToRemove = 0;
  159. for (int ix = wrappedLines.length - 1; ix > 0; ix--) {
  160. if (ix > destLineIndex || wrappedLines [ix].getTrimmedLength () == 0) {
  161. countToRemove++;
  162. } else {
  163. break;
  164. }
  165. }
  166. if (countToRemove > 0) {
  167. toRemove.add (y + wrappedLines.length - countToRemove); // index
  168. toRemove.add (countToRemove);
  169. }
  170. y += wrappedLines.length - 1;
  171. }
  172. return toRemove;
  173. }
  174. int _getWrappedLineTrimmedLengthRow(List<BufferLine> lines, int row, int cols)
  175. {
  176. return _getWrappedLineTrimmedLength (lines[row], row == lines.length - 1 ? null : lines[row + 1], cols);
  177. }
  178. int _getWrappedLineTrimmedLength (BufferLine line, BufferLine? nextLine, int cols)
  179. {
  180. // If this is the last row in the wrapped line, get the actual trimmed length
  181. if (nextLine == null) {
  182. return line.getTrimmedLength ();
  183. }
  184. // Detect whether the following line starts with a wide character and the end of the current line
  185. // is null, if so then we can be pretty sure the null character should be excluded from the line
  186. // length]
  187. bool endsInNull = !(line.hasContentAt(cols - 1)) && line.getWidthAt(cols - 1) == 1;
  188. bool followingLineStartsWithWide = nextLine.getWidthAt(0) == 2;
  189. if (endsInNull && followingLineStartsWithWide) {
  190. return cols - 1;
  191. }
  192. return cols;
  193. }
  194. }