<commit_before>/**
 * Copyright (c) 2019 The xterm.js authors. All rights reserved.
 * @license MIT
 *
 * (EXPERIMENTAL) This Addon is still under development
 */

import { Terminal, ITerminalAddon, IBuffer, IBufferCell } from 'xterm';
import { MyBufferCell } from './MyBufferCell';

function constrain(value: number, low: number, high: number): number {
  return Math.max(low, Math.min(value, high));
}

// TODO: Refine this template class later
abstract class BaseSerializeHandler {
  constructor(private _buffer: IBuffer) { }

  public serialize(startRow: number, endRow: number): string {
    // we need two of them to flip between old and new cell
    const cell1 = this._buffer.getNullCell();
    const cell2 = this._buffer.getNullCell();
    let oldCell = cell1;

    this._beforeSerialize(endRow - startRow, startRow, endRow);

    for (let row = startRow; row < endRow; row++) {
      const line = this._buffer.getLine(row);
      if (line) {
        for (let col = 0; col < line.length; col++) {
          const c = line.getCell(col, oldCell === cell1 ? cell2 : cell1);
          if (!c) {
            console.warn(`Can't get cell at row=${row}, col=${col}`);
            continue;
          }
          this._nextCell(c, oldCell, row, col);
          oldCell = c;
        }
      }
      this._rowEnd(row, row === endRow - 1);
    }

    this._afterSerialize();

    return this._serializeString();
  }

  protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { }
  protected _rowEnd(row: number, isLastRow: boolean): void { }
  protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { }
  protected _afterSerialize(): void { }
  protected _serializeString(): string { return ''; }
}

function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.getFgColorMode() === cell2.getFgColorMode()
    && cell1.getFgColor() === cell2.getFgColor();
}

function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.getBgColorMode() === cell2.getBgColorMode()
    && cell1.getBgColor() === cell2.getBgColor();
}

function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.isInverse() === cell2.isInverse()
    && cell1.isBold() === cell2.isBold()
    && cell1.isUnderline() === cell2.isUnderline()
    && cell1.isBlink() === cell2.isBlink()
    && cell1.isInvisible() === cell2.isInvisible()
    && cell1.isItalic() === cell2.isItalic()
    && cell1.isDim() === cell2.isDim();
}



class StringSerializeHandler extends BaseSerializeHandler {
  private _rowIndex: number = 0;
  private _allRows: string[] = new Array<string>();
  private _currentRow: string = '';
  private _nullCellCount: number = 0;

  // we can see a full colored cell and a null cell that only have background the same style
  // but the information isn't preserved by null cell itself
  // so wee need to record it when required.
  private _cursorStyle: IBufferCell = this._buffer1.getNullCell();

  // this is a null cell for reference for checking whether background is empty or not
  private _backgroundCell: MyBufferCell = MyBufferCell.from(this._cursorStyle);

  private _firstRow: number = 0;
  private _lastCursorRow: number = 0;
  private _lastCursorCol: number = 0;

  constructor(private _buffer1: IBuffer,private _terminal: Terminal) {
    super(_buffer1);
  }

  protected _beforeSerialize(rows: number, start: number, end: number): void {
    this._allRows = new Array<string>(rows);
    this._lastCursorRow = start;
    this._firstRow = start;
  }

  protected _rowEnd(row: number, isLastRow: boolean): void {
    // if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing
    if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
      // use clear right to set background.
      // use move right to move cursor.
      this._currentRow += `\x1b[${this._nullCellCount}X`;
    }

    if (!isLastRow) {
      if (row - this._firstRow >= this._terminal.rows) {
        this._backgroundCell = MyBufferCell.from(this._cursorStyle);
      }
    }

    this._allRows[this._rowIndex++] = this._currentRow;
    this._currentRow = '';
    this._nullCellCount = 0;
  }

  private _diffStyle (cell: IBufferCell, oldCell: IBufferCell): number[] {
    const sgrSeq: number[] = [];
    const fgChanged = !equalFg(cell, oldCell);
    const bgChanged = !equalBg(cell, oldCell);
    const flagsChanged = !equalFlags(cell, oldCell);

    if (fgChanged || bgChanged || flagsChanged) {
      if (cell.isAttributeDefault()) {
        if (!oldCell.isAttributeDefault()) {
          sgrSeq.push(0);
        }
      } else {
        if (fgChanged) {
          const color = cell.getFgColor();
          if (cell.isFgRGB()) { sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
          else if (cell.isFgPalette()) {
            if (color >= 16) { sgrSeq.push(38, 5, color); }
            else { sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); }
          }
          else { sgrSeq.push(39); }
        }
        if (bgChanged) {
          const color = cell.getBgColor();
          if (cell.isBgRGB()) { sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
          else if (cell.isBgPalette()) {
            if (color >= 16) { sgrSeq.push(48, 5, color); }
            else { sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); }
          }
          else { sgrSeq.push(49); }
        }
        if (flagsChanged) {
          if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); }
          if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); }
          if (cell.isUnderline() !== oldCell.isUnderline()) { sgrSeq.push(cell.isUnderline() ? 4 : 24); }
          if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); }
          if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); }
          if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); }
          if (cell.isDim() !== oldCell.isDim()) { sgrSeq.push(cell.isDim() ? 2 : 22); }
        }
      }
    }

    return sgrSeq;
  }

  protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
    // a width 0 cell don't need to be count because it is just a placeholder after a CJK character;
    const isPlaceHolderCell = cell.getWidth() === 0;

    if (isPlaceHolderCell) {
      return;
    }

    // this cell don't have content
    const isEmptyCell = cell.getChars() === '';

    const sgrSeq = this._diffStyle(cell, this._cursorStyle);

    // the empty cell style is only assumed to be changed when background changed, because foreground is always 0.
    const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0;

    /**
     *  handles style change
     */
    if (styleChanged) {
      // before update the style, we need to fill empty cell back
      if (this._nullCellCount > 0) {
        // use clear right to set background.
        // use move right to move cursor.
        if (equalBg(this._cursorStyle, this._backgroundCell)) {
          this._currentRow += `\x1b[${this._nullCellCount}C`;
        } else {
          this._currentRow += `\x1b[${this._nullCellCount}X`;
          this._currentRow += `\x1b[${this._nullCellCount}C`;
        }
        this._nullCellCount = 0;
      }

      this._lastCursorRow = row;
      this._lastCursorCol = col;

      this._currentRow += `\x1b[${sgrSeq.join(';')}m`;

      // update the last cursor style
      this._buffer1.getLine(row)?.getCell(col, this._cursorStyle);
    }

    /**
     *  handles actual content
     */
    if (isEmptyCell) {
      this._nullCellCount += cell.getWidth();
    } else {
      if (this._nullCellCount > 0) {
        // we can just assume we have same style with previous one here
        // because style change is handled by previous stage
        // use move right when background is empty, use clear right when there is background.
        if (equalBg(this._cursorStyle, this._backgroundCell)) {
          this._currentRow += `\x1b[${this._nullCellCount}C`;
        } else {
          this._currentRow += `\x1b[${this._nullCellCount}X`;
          this._currentRow += `\x1b[${this._nullCellCount}C`;<commit_msg>CLearup comment<commit_after>/**
 * Copyright (c) 2019 The xterm.js authors. All rights reserved.
 * @license MIT
 *
 * (EXPERIMENTAL) This Addon is still under development
 */

import { Terminal, ITerminalAddon, IBuffer, IBufferCell } from 'xterm';
import { MyBufferCell } from './MyBufferCell';

function constrain(value: number, low: number, high: number): number {
  return Math.max(low, Math.min(value, high));
}

// TODO: Refine this template class later
abstract class BaseSerializeHandler {
  constructor(private _buffer: IBuffer) { }

  public serialize(startRow: number, endRow: number): string {
    // we need two of them to flip between old and new cell
    const cell1 = this._buffer.getNullCell();
    const cell2 = this._buffer.getNullCell();
    let oldCell = cell1;

    this._beforeSerialize(endRow - startRow, startRow, endRow);

    for (let row = startRow; row < endRow; row++) {
      const line = this._buffer.getLine(row);
      if (line) {
        for (let col = 0; col < line.length; col++) {
          const c = line.getCell(col, oldCell === cell1 ? cell2 : cell1);
          if (!c) {
            console.warn(`Can't get cell at row=${row}, col=${col}`);
            continue;
          }
          this._nextCell(c, oldCell, row, col);
          oldCell = c;
        }
      }
      this._rowEnd(row, row === endRow - 1);
    }

    this._afterSerialize();

    return this._serializeString();
  }

  protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { }
  protected _rowEnd(row: number, isLastRow: boolean): void { }
  protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { }
  protected _afterSerialize(): void { }
  protected _serializeString(): string { return ''; }
}

function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.getFgColorMode() === cell2.getFgColorMode()
    && cell1.getFgColor() === cell2.getFgColor();
}

function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.getBgColorMode() === cell2.getBgColorMode()
    && cell1.getBgColor() === cell2.getBgColor();
}

function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
  return cell1.isInverse() === cell2.isInverse()
    && cell1.isBold() === cell2.isBold()
    && cell1.isUnderline() === cell2.isUnderline()
    && cell1.isBlink() === cell2.isBlink()
    && cell1.isInvisible() === cell2.isInvisible()
    && cell1.isItalic() === cell2.isItalic()
    && cell1.isDim() === cell2.isDim();
}



class StringSerializeHandler extends BaseSerializeHandler {
  private _rowIndex: number = 0;
  private _allRows: string[] = new Array<string>();
  private _currentRow: string = '';
  private _nullCellCount: number = 0;

  // we can see a full colored cell and a null cell that only have background the same style
  // but the information isn't preserved by null cell itself
  // so wee need to record it when required.
  private _cursorStyle: IBufferCell = this._buffer1.getNullCell();

  // this is a null cell for reference for checking whether background is empty or not
  private _backgroundCell: MyBufferCell = MyBufferCell.from(this._cursorStyle);

  private _firstRow: number = 0;
  private _lastCursorRow: number = 0;
  private _lastCursorCol: number = 0;

  constructor(private _buffer1: IBuffer,private _terminal: Terminal) {
    super(_buffer1);
  }

  protected _beforeSerialize(rows: number, start: number, end: number): void {
    this._allRows = new Array<string>(rows);
    this._lastCursorRow = start;
    this._firstRow = start;
  }

  protected _rowEnd(row: number, isLastRow: boolean): void {
    // if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing
    if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
      // use clear right to set background.
      this._currentRow += `\x1b[${this._nullCellCount}X`;
    }

    if (!isLastRow) {
      if (row - this._firstRow >= this._terminal.rows) {
        this._backgroundCell = MyBufferCell.from(this._cursorStyle);
      }
    }

    this._allRows[this._rowIndex++] = this._currentRow;
    this._currentRow = '';
    this._nullCellCount = 0;
  }

  private _diffStyle (cell: IBufferCell, oldCell: IBufferCell): number[] {
    const sgrSeq: number[] = [];
    const fgChanged = !equalFg(cell, oldCell);
    const bgChanged = !equalBg(cell, oldCell);
    const flagsChanged = !equalFlags(cell, oldCell);

    if (fgChanged || bgChanged || flagsChanged) {
      if (cell.isAttributeDefault()) {
        if (!oldCell.isAttributeDefault()) {
          sgrSeq.push(0);
        }
      } else {
        if (fgChanged) {
          const color = cell.getFgColor();
          if (cell.isFgRGB()) { sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
          else if (cell.isFgPalette()) {
            if (color >= 16) { sgrSeq.push(38, 5, color); }
            else { sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); }
          }
          else { sgrSeq.push(39); }
        }
        if (bgChanged) {
          const color = cell.getBgColor();
          if (cell.isBgRGB()) { sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
          else if (cell.isBgPalette()) {
            if (color >= 16) { sgrSeq.push(48, 5, color); }
            else { sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); }
          }
          else { sgrSeq.push(49); }
        }
        if (flagsChanged) {
          if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); }
          if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); }
          if (cell.isUnderline() !== oldCell.isUnderline()) { sgrSeq.push(cell.isUnderline() ? 4 : 24); }
          if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); }
          if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); }
          if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); }
          if (cell.isDim() !== oldCell.isDim()) { sgrSeq.push(cell.isDim() ? 2 : 22); }
        }
      }
    }

    return sgrSeq;
  }

  protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
    // a width 0 cell don't need to be count because it is just a placeholder after a CJK character;
    const isPlaceHolderCell = cell.getWidth() === 0;

    if (isPlaceHolderCell) {
      return;
    }

    // this cell don't have content
    const isEmptyCell = cell.getChars() === '';

    const sgrSeq = this._diffStyle(cell, this._cursorStyle);

    // the empty cell style is only assumed to be changed when background changed, because foreground is always 0.
    const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0;

    /**
     *  handles style change
     */
    if (styleChanged) {
      // before update the style, we need to fill empty cell back
      if (this._nullCellCount > 0) {
        // use clear right to set background.
        if (!equalBg(this._cursorStyle, this._backgroundCell)) {
          this._currentRow += `\x1b[${this._nullCellCount}X`;
        }
        // use move right to move cursor.
        this._currentRow += `\x1b[${this._nullCellCount}C`;
        this._nullCellCount = 0;
      }

      this._lastCursorRow = row;
      this._lastCursorCol = col;

      this._currentRow += `\x1b[${sgrSeq.join(';')}m`;

      // update the last cursor style
      this._buffer1.getLine(row)?.getCell(col, this._cursorStyle);
    }

    /**
     *  handles actual content
     */
    if (isEmptyCell) {
      this._nullCellCount += cell.getWidth();
    } else {
      if (this._nullCellCount > 0) {
        // we can just assume we have same style with previous one here
        // because style change is handled by previous stage
        // use move right when background is empty, use clear right when there is background.
        if (equalBg(this._cursorStyle, this._backgroundCell)) {
          this._currentRow += `\x1b[${this._nullCellCount}C`;
        } else {
          this._currentRow += `\x1b[${this._nullCellCount}X`;
          this._currentRow += `\x1b[${this._nullCellCount}C`;
{'fraction_non_alphanumeric': 0.13875312909762785, 'fraction_numerical': 0.016032900226487068, 'mean_word_length': 2.8229665071770333, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4933282', 'n_tokens_mistral': 5542, 'n_tokens_neox': 5288, 'n_words': 1417}