public print()

in src/InputHandler.ts [328:462]


  public print(data: string, start: number, end: number): void {
    let char: string;
    let code: number;
    let low: number;
    let chWidth: number;
    const buffer: IBuffer = this._terminal.buffer;
    const charset: ICharset = this._terminal.charset;
    const screenReaderMode: boolean = this._terminal.options.screenReaderMode;
    const cols: number = this._terminal.cols;
    const wraparoundMode: boolean = this._terminal.wraparoundMode;
    const insertMode: boolean = this._terminal.insertMode;
    const curAttr: number = this._terminal.curAttr;
    let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);

    this._terminal.updateRange(buffer.y);
    for (let stringPosition = start; stringPosition < end; ++stringPosition) {
      char = data.charAt(stringPosition);
      code = data.charCodeAt(stringPosition);

      // surrogate pair handling
      if (0xD800 <= code && code <= 0xDBFF) {
        // we got a surrogate high
        // get surrogate low (next 2 bytes)
        low = data.charCodeAt(stringPosition + 1);
        if (isNaN(low)) {
          // end of data stream, save surrogate high
          this._surrogateHigh = char;
          continue;
        }
        code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
        char += data.charAt(stringPosition + 1);
      }
      // surrogate low - already handled above
      if (0xDC00 <= code && code <= 0xDFFF) {
        continue;
      }

      // calculate print space
      // expensive call, therefore we save width in line buffer
      chWidth = wcwidth(code);

      // get charset replacement character
      if (charset) {
        char = charset[char] || char;
        code = char.charCodeAt(0);
      }

      if (screenReaderMode) {
        this._terminal.emit('a11y.char', char);
      }

      // insert combining char at last cursor position
      // FIXME: needs handling after cursor jumps
      // buffer.x should never be 0 for a combining char
      // since they always follow a cell consuming char
      // therefore we can test for buffer.x to avoid overflow left
      if (!chWidth && buffer.x) {
        const chMinusOne = bufferRow.get(buffer.x - 1);
        if (chMinusOne) {
          if (!chMinusOne[CHAR_DATA_WIDTH_INDEX]) {
            // found empty cell after fullwidth, need to go 2 cells back
            // it is save to step 2 cells back here
            // since an empty cell is only set by fullwidth chars
            const chMinusTwo = bufferRow.get(buffer.x - 2);
            if (chMinusTwo) {
              chMinusTwo[CHAR_DATA_CHAR_INDEX] += char;
              chMinusTwo[CHAR_DATA_CODE_INDEX] = code;
            }
          } else {
            chMinusOne[CHAR_DATA_CHAR_INDEX] += char;
            chMinusOne[CHAR_DATA_CODE_INDEX] = code;
          }
        }
        continue;
      }

      // goto next line if ch would overflow
      // TODO: needs a global min terminal width of 2
      if (buffer.x + chWidth - 1 >= cols) {
        // autowrap - DECAWM
        // automatically wraps to the beginning of the next line
        if (wraparoundMode) {
          buffer.x = 0;
          buffer.y++;
          if (buffer.y > buffer.scrollBottom) {
            buffer.y--;
            this._terminal.scroll(true);
          } else {
            // The line already exists (eg. the initial viewport), mark it as a
            // wrapped line
            buffer.lines.get(buffer.y).isWrapped = true;
          }
          // row changed, get it again
          bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
        } else {
          if (chWidth === 2) {
            // FIXME: check for xterm behavior
            // What to do here? We got a wide char that does not fit into last cell
            continue;
          }
          // FIXME: Do we have to set buffer.x to cols - 1, if not wrapping?
        }
      }

      // insert mode: move characters to right
      // To achieve insert, we remove cells from the right
      // and insert empty ones at cursor position
      if (insertMode) {
        // do this twice for a fullwidth char
        for (let moves = 0; moves < chWidth; ++moves) {
          // remove last cell
          // if it's width is 0, we have to adjust the second last cell as well
          const removed = bufferRow.pop();
          const chMinusTwo = bufferRow.get(buffer.x - 2);
          if (removed[CHAR_DATA_WIDTH_INDEX] === 0
              && chMinusTwo
              && chMinusTwo[CHAR_DATA_WIDTH_INDEX] === 2) {
                bufferRow.set(this._terminal.cols - 2, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
          }

          // insert empty cell at cursor
          bufferRow.splice(buffer.x, 0, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
        }
      }

      // write current char to buffer and advance cursor
      bufferRow.set(buffer.x++, [curAttr, char, chWidth, code]);

      // fullwidth char - also set next cell to placeholder stub and advance cursor
      if (chWidth === 2) {
        bufferRow.set(buffer.x++, [curAttr, '', 0, undefined]);
      }
    }
    this._terminal.updateRange(buffer.y);
  }