瀏覽代碼

First untested "blind port" of reflow larger logic

devmil 4 年之前
父節點
當前提交
9e3992545e
共有 4 個文件被更改,包括 288 次插入2 次删除
  1. 14 0
      lib/buffer/buffer.dart
  2. 28 0
      lib/buffer/buffer_line.dart
  3. 242 0
      lib/buffer/buffer_reflow.dart
  4. 4 2
      lib/terminal/terminal.dart

+ 14 - 0
lib/buffer/buffer.dart

@@ -346,6 +346,15 @@ class Buffer {
     charset.save();
     charset.save();
   }
   }
 
 
+  void adjustSavedCursor(int diffX, int diffY) {
+    if(_savedCursorX != null) {
+      _savedCursorX = _savedCursorX! + diffX;
+    }
+    if(_savedCursorY != null) {
+      _savedCursorY = _savedCursorY! + diffY;
+    }
+  }
+
   void restoreCursor() {
   void restoreCursor() {
     if (_savedCellAttr != null) {
     if (_savedCellAttr != null) {
       terminal.cellAttr.use(_savedCellAttr!);
       terminal.cellAttr.use(_savedCellAttr!);
@@ -463,4 +472,9 @@ class Buffer {
 
 
     lines.removeAt(index);
     lines.removeAt(index);
   }
   }
+
+  void resize(int width, int height) {
+    
+  }
+
 }
 }

+ 28 - 0
lib/buffer/buffer_line.dart

@@ -5,6 +5,10 @@ class BufferLine {
   final _cells = <Cell>[];
   final _cells = <Cell>[];
   bool _isWrapped = false;
   bool _isWrapped = false;
 
 
+  BufferLine({int numOfCells = 0, attr}) {
+    _cells.addAll(List<Cell>.generate(numOfCells, (index) => Cell(codePoint: 0, attr: attr)));
+  }
+
   bool get isWrapped {
   bool get isWrapped {
     return _isWrapped;
     return _isWrapped;
   }
   }
@@ -25,6 +29,17 @@ class BufferLine {
     _cells.clear();
     _cells.clear();
   }
   }
 
 
+  int getTrimmedLength () {
+    for (int i = _cells.length - 1; i >= 0; --i)
+      if (_cells[i].codePoint != 0) {
+        int width = 0;
+        for (int j = 0; j <= i; j++)
+          width += _cells[i].width;
+        return width;
+      }
+    return 0;
+  }
+
   void erase(CellAttr attr, int start, int end) {
   void erase(CellAttr attr, int start, int end) {
     for (var i = start; i < end; i++) {
     for (var i = start; i < end; i++) {
       if (i >= length) {
       if (i >= length) {
@@ -45,4 +60,17 @@ class BufferLine {
     end = end.clamp(start, _cells.length);
     end = end.clamp(start, _cells.length);
     _cells.removeRange(start, end);
     _cells.removeRange(start, end);
   }
   }
+
+  void copyCellsFrom (BufferLine src, int srcCol, int dstCol, int len)
+  {
+    List.copyRange(_cells, dstCol, src._cells, srcCol, srcCol + len);
+  }
+
+  int getWidthAt(int col) {
+    return _cells[col].width;
+  }
+
+  bool hasContentAt(int col) {
+    return _cells[col].codePoint != 0;
+  }
 }
 }

+ 242 - 0
lib/buffer/buffer_reflow.dart

@@ -0,0 +1,242 @@
+import 'dart:math';
+
+import 'package:xterm/buffer/cell_attr.dart';
+import 'package:dart_numeric'
+
+import 'buffer.dart';
+import 'buffer_line.dart';
+
+class LayoutResult {
+
+  LayoutResult(this.layout, this.removedCount);
+
+  final List<int> layout;
+  final int removedCount;
+}
+
+class BufferReflow {
+  BufferReflow(this._buffer, this._emptyCellAttr);
+
+  final Buffer _buffer;
+  final CellAttr _emptyCellAttr;
+
+  void doReflow(int colsBefore, int colsAfter) {
+    if(colsBefore == colsAfter) {
+      return;
+    }
+
+    if(colsAfter > colsBefore) {
+      //got larger
+      _reflowLarger(colsBefore, colsAfter);
+    } else {
+      //got smaller
+      _reflowSmaller(colsBefore, colsAfter);
+    }
+  }
+
+  void _reflowLarger(int colsBefore, int colsAfter) {
+    var toRemove = _reflowLargerGetLinesToRemove(colsBefore, colsAfter);
+    if (toRemove.length > 0) {
+      var newLayoutResult = _reflowLargerCreateNewLayout(_buffer.lines, toRemove);
+      _reflowLargerApplyNewLayout(_buffer.lines, newLayoutResult.layout);
+      _reflowLargerAdjustViewport(colsBefore, colsAfter, newLayoutResult.removedCount);
+    }
+  }
+
+  void _reflowSmaller(int colsBefore, int colsAfter) {
+
+  }
+
+  void _reflowLargerAdjustViewport(int colsBefore, int colsAfter, int countRemoved) {
+    // Adjust viewport based on number of items removed
+    var viewportAdjustments = countRemoved;
+    while (viewportAdjustments-- > 0) {
+      //viewport is at the top
+      if (_buffer.lines.length <= _buffer.terminal.viewHeight) {
+        //cursor is not at the top
+        if (_buffer.cursorY > 0) {
+          _buffer.moveCursorY(-1);
+        }
+        //buffer doesn't have enough lines
+        if (_buffer.lines.length < _buffer.terminal.viewHeight) {
+          // Add an extra row at the bottom of the viewport
+          _buffer.lines.add(new BufferLine(numOfCells: colsAfter, attr: _emptyCellAttr));
+        }
+      } else {
+        //Nothing to do here due to the way scrolling is handled
+
+        // //user didn't scroll
+        // if (this.ydisp === this.ybase) {
+        //   //scroll viewport according to...
+        //   this.ydisp--;
+        // }
+        // //base window
+        // this.ybase--;
+      }
+    }
+    //TODO: adjust buffer content to max length
+    _buffer.adjustSavedCursor(0, -countRemoved);
+  }
+
+  void _reflowLargerApplyNewLayout(List<BufferLine> lines, List<int> newLayout) {
+    var newLayoutLines = List<BufferLine>.empty();
+
+    for (int i = 0; i < newLayout.length; i++) {
+      newLayoutLines.add(lines[newLayout [i]]);
+    }
+
+    // Rearrange the list
+    for (int i = 0; i < newLayoutLines.length; i++) {
+      lines[i] = newLayoutLines[i];
+    }
+
+    lines.removeRange(newLayoutLines.length, lines.length - 1);
+  }
+
+  LayoutResult _reflowLargerCreateNewLayout(List<BufferLine> lines, List<int> toRemove) {
+    var layout = List<int>.empty();
+
+    // First iterate through the list and get the actual indexes to use for rows
+    int nextToRemoveIndex = 0;
+    int nextToRemoveStart = toRemove [nextToRemoveIndex];
+    int countRemovedSoFar = 0;
+
+    for (int i = 0; i < lines.length; i++) {
+      if (nextToRemoveStart == i) {
+        int countToRemove = toRemove [++nextToRemoveIndex];
+
+        // Tell markers that there was a deletion
+        //lines.onDeleteEmitter.fire ({
+        //	index: i - countRemovedSoFar,
+        //	amount: countToRemove
+        //});
+
+        i += countToRemove - 1;
+        countRemovedSoFar += countToRemove;
+
+        nextToRemoveStart = lines.length + 1;
+        if (nextToRemoveIndex < toRemove.length - 1)
+          nextToRemoveStart = toRemove [++nextToRemoveIndex];
+      } else {
+        layout.add(i);
+      }
+    }
+
+    return LayoutResult (layout, countRemovedSoFar);
+  }
+
+  List<int> _reflowLargerGetLinesToRemove(int colsBefore, int colsAfter) {
+    List<int> toRemove = List<int>.empty();
+
+    
+
+    for (int y = 0; y < _buffer.lines.length - 1; y++) {
+      // Check if this row is wrapped
+      int i = y;
+      BufferLine nextLine = _buffer.lines[++i];
+      if (!nextLine.isWrapped) {
+        continue;
+      }
+
+      // Check how many lines it's wrapped for
+      List<BufferLine> wrappedLines = List<BufferLine>.empty();
+      wrappedLines.add(_buffer.lines[y]);
+      while (i < _buffer.lines.length && nextLine.isWrapped) {
+        wrappedLines.add(nextLine);
+        nextLine = _buffer.lines[++i];
+      }
+
+      final bufferAbsoluteY = _buffer.cursorY - _buffer.scrollOffsetFromBottom;
+
+      // 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.length - 1;
+        continue;
+      }
+
+      // Copy buffer data to new locations
+      int destLineIndex = 0;
+      int destCol = _getWrappedLineTrimmedLengthRow(_buffer.lines, destLineIndex, colsBefore);
+      int srcLineIndex = 1;
+      int srcCol = 0;
+      while (srcLineIndex < wrappedLines.length) {
+        int srcTrimmedTineLength = _getWrappedLineTrimmedLengthRow(wrappedLines, srcLineIndex, colsBefore);
+        int srcRemainingCells = srcTrimmedTineLength - srcCol;
+        int destRemainingCells = colsAfter - destCol;
+        int cellsToCopy = min(srcRemainingCells, destRemainingCells);
+
+        wrappedLines [destLineIndex].copyCellsFrom (wrappedLines [srcLineIndex], srcCol, destCol, cellsToCopy);
+
+        destCol += cellsToCopy;
+        if (destCol == colsAfter) {
+          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].getWidthAt(colsAfter - 1) == 2) {
+            wrappedLines [destLineIndex].copyCellsFrom (wrappedLines [destLineIndex - 1], colsAfter - 1, destCol++, 1);
+            // Null out the end of the last row
+            wrappedLines [destLineIndex - 1].erase (_emptyCellAttr, colsAfter - 1, colsAfter);
+          }
+        }
+      }
+
+      // Clear out remaining cells or fragments could remain;
+      wrappedLines [destLineIndex].erase(_emptyCellAttr, destCol, colsAfter);
+
+      // Work backwards and remove any rows at the end that only contain null cells
+      int countToRemove = 0;
+      for (int ix = wrappedLines.length - 1; ix > 0; ix--) {
+        if (ix > destLineIndex || wrappedLines [ix].getTrimmedLength () == 0) {
+          countToRemove++;
+        } else {
+          break;
+        }
+      }
+
+      if (countToRemove > 0) {
+        toRemove.add (y + wrappedLines.length - countToRemove); // index
+        toRemove.add (countToRemove);
+      }
+
+      y += wrappedLines.length - 1;
+    }
+
+    return toRemove;
+  }
+
+
+  int _getWrappedLineTrimmedLengthRow(List<BufferLine> lines, int row, int cols)
+  {
+    return _getWrappedLineTrimmedLength (lines[row], row == lines.length - 1 ? null : lines[row + 1], cols);
+  }
+
+  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.hasContentAt(cols - 1)) && line.getWidthAt(cols - 1) == 1;
+    bool followingLineStartsWithWide = nextLine.getWidthAt(0) == 2;
+
+    if (endsInNull && followingLineStartsWithWide) {
+      return cols - 1;
+    }
+
+    return cols;
+  }
+}

+ 4 - 2
lib/terminal/terminal.dart

@@ -286,11 +286,13 @@ class Terminal with Observable {
     return _buffer == _altBuffer;
     return _buffer == _altBuffer;
   }
   }
 
 
-  void resize(int width, int heigth) {
+  void resize(int width, int height) {
+    _altBuffer.resize(width, height);
+    _mainBuffer.resize(width, height);
     final cursorY = buffer.convertViewLineToRawLine(buffer.cursorY);
     final cursorY = buffer.convertViewLineToRawLine(buffer.cursorY);
 
 
     _viewWidth = max(width, 1);
     _viewWidth = max(width, 1);
-    _viewHeight = max(heigth, 1);
+    _viewHeight = max(height, 1);
 
 
     buffer.setCursorY(buffer.convertRawLineToViewLine(cursorY));
     buffer.setCursorY(buffer.convertRawLineToViewLine(cursorY));