xterm
[VSoRC/.git] / node_modules / xterm / src / browser / renderer / SelectionRenderLayer.ts
diff --git a/node_modules/xterm/src/browser/renderer/SelectionRenderLayer.ts b/node_modules/xterm/src/browser/renderer/SelectionRenderLayer.ts
new file mode 100644 (file)
index 0000000..5cd2fd9
--- /dev/null
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+import { IRenderDimensions } from 'browser/renderer/Types';
+import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
+import { IColorSet } from 'browser/Types';
+import { IBufferService, IOptionsService } from 'common/services/Services';
+
+interface ISelectionState {
+  start?: [number, number];
+  end?: [number, number];
+  columnSelectMode?: boolean;
+  ydisp?: number;
+}
+
+export class SelectionRenderLayer extends BaseRenderLayer {
+  private _state!: ISelectionState;
+
+  constructor(
+    container: HTMLElement,
+    zIndex: number,
+    colors: IColorSet,
+    rendererId: number,
+    readonly bufferService: IBufferService,
+    readonly optionsService: IOptionsService
+  ) {
+    super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService);
+    this._clearState();
+  }
+
+  private _clearState(): void {
+    this._state = {
+      start: undefined,
+      end: undefined,
+      columnSelectMode: undefined,
+      ydisp: undefined
+    };
+  }
+
+  public resize(dim: IRenderDimensions): void {
+    super.resize(dim);
+    // Resizing the canvas discards the contents of the canvas so clear state
+    this._clearState();
+  }
+
+  public reset(): void {
+    if (this._state.start && this._state.end) {
+      this._clearState();
+      this._clearAll();
+    }
+  }
+
+  public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void {
+    // Selection has not changed
+    if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
+      return;
+    }
+
+    // Remove all selections
+    this._clearAll();
+
+    // Selection does not exist
+    if (!start || !end) {
+      this._clearState();
+      return;
+    }
+
+    // Translate from buffer position to viewport position
+    const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
+    const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
+    const viewportCappedStartRow = Math.max(viewportStartRow, 0);
+    const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
+
+    // No need to draw the selection
+    if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
+      return;
+    }
+
+    this._ctx.fillStyle = this._colors.selection.css;
+
+    if (columnSelectMode) {
+      const startCol = start[0];
+      const width = end[0] - startCol;
+      const height = viewportCappedEndRow - viewportCappedStartRow + 1;
+      this._fillCells(startCol, viewportCappedStartRow, width, height);
+    } else {
+      // Draw first row
+      const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
+      const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
+      this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
+
+      // Draw middle rows
+      const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
+      this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount);
+
+      // Draw final row
+      if (viewportCappedStartRow !== viewportCappedEndRow) {
+        // Only draw viewportEndRow if it's not the same as viewportStartRow
+        const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
+        this._fillCells(0, viewportCappedEndRow, endCol, 1);
+      }
+    }
+
+    // Save state for next render
+    this._state.start = [start[0], start[1]];
+    this._state.end = [end[0], end[1]];
+    this._state.columnSelectMode = columnSelectMode;
+    this._state.ydisp = this._bufferService.buffer.ydisp;
+  }
+
+  private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean {
+    return !this._areCoordinatesEqual(start, this._state.start) ||
+      !this._areCoordinatesEqual(end, this._state.end) ||
+      columnSelectMode !== this._state.columnSelectMode ||
+      ydisp !== this._state.ydisp;
+  }
+
+  private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean {
+    if (!coord1 || !coord2) {
+      return false;
+    }
+
+    return coord1[0] === coord2[0] && coord1[1] === coord2[1];
+  }
+}