<commit_before>/**
 * Copyright 2015-2018 <NAME> <<EMAIL>>
 */

import {WebComponent} from 'extraterm-web-component-decorators';
import {BulkFileHandle, ViewerMetadata, ViewerPosture} from 'extraterm-extension-api';

import {BlobBulkFileHandle} from '../bulk_file_handling/BlobBulkFileHandle';
import {Commandable, CommandEntry, COMMAND_OPEN_COMMAND_PALETTE, dispatchCommandPaletteRequest}
from '../CommandPaletteRequestTypes';
import {doLater, doLaterFrame, DebouncedDoLater} from '../../utils/DoLater';
import * as DomUtils from '../DomUtils';
import { ExtraEditCommands } from './ExtraAceEditCommands';
import * as GeneralEvents from '../GeneralEvents';
import * as keybindingmanager from '../keybindings/KeyBindingsManager';
import {Logger, getLogger} from "extraterm-logging";
import { log } from "extraterm-logging";
import * as ResizeRefreshElementBase from '../ResizeRefreshElementBase';
import * as SupportsClipboardPaste from '../SupportsClipboardPaste';
import * as Term from '../emulator/Term';
import * as TermApi from 'term-api';
import { BookmarkRef } from './TerminalViewerTypes';
import * as ThemeTypes from '../../theme/Theme';
import {ThemeableElementBase} from '../ThemeableElementBase';
import {ViewerElement} from './ViewerElement';
import * as ViewerElementTypes from './ViewerElementTypes';
import * as VirtualScrollArea from '../VirtualScrollArea';
import { Disposable } from 'extraterm-extension-api';

import { TerminalAceEditor, TerminalDocument, TerminalEditSession, TerminalRenderer } from "extraterm-ace-terminal-renderer";
import { Anchor, Command, DefaultCommands, Editor, MultiSelectCommands, Origin, Renderer, Position, SelectionChangeEvent, UndoManager } from "ace-ts";


type KeyBindingManager = keybindingmanager.KeyBindingsManager;

type SetterState = VirtualScrollArea.SetterState;
type CursorMoveDetail = ViewerElementTypes.CursorMoveDetail;
const VisualState = ViewerElementTypes.VisualState;
type VisualState = ViewerElementTypes.VisualState;

const ID = "EtTerminalAceViewerTemplate";
const ID_CONTAINER = "ID_CONTAINER";
const ID_MAIN_STYLE = "ID_MAIN_STYLE";
const ID_CSS_VARS = "ID_CSS_VARS";
const CLASS_HIDE_CURSOR = "hide-cursor";
const CLASS_FOCUSED = "terminal-focused";
const CLASS_UNFOCUSED = "terminal-unfocused";
const CLASS_HAS_TERMINAL = "CLASS_HAS_TERMINAL";

const KEYBINDINGS_TERMINAL_VIEWER = "terminal-viewer";
const PALETTE_GROUP = "terminalviewer";
const COMMAND_TYPE_AND_CR_SELECTION = "typeSelectionAndCr";
const COMMAND_TYPE_SELECTION = "typeSelection";

const NO_STYLE_HACK = "NO_STYLE_HACK";

const DEBUG_RESIZE = false;

let cssText: string = null;

function getCssText(): string {
  return cssText;
}


@WebComponent({tag: "et-terminal-ace-viewer"})
export class TerminalViewer extends ViewerElement implements Commandable, keybindingmanager.AcceptsKeyBindingsManager,
    SupportsClipboardPaste.SupportsClipboardPaste, Disposable {

  static TAG_NAME = "ET-TERMINAL-ACE-VIEWER";
  
  static EVENT_KEYBOARD_ACTIVITY = "keyboard-activity";

  /**
   * Type guard for detecting a EtTerminalViewer instance.
   * 
   * @param  node the node to test
   * @return      True if the node is a EtTerminalViewer.
   */
  static is(node: Node): node is TerminalViewer {
    return node !== null && node !== undefined && node instanceof TerminalViewer;
  }

  private _log: Logger;
  private _keyBindingManager: KeyBindingManager = null;
  private _emulator: Term.Emulator = null;

  // The line number of the top row of the emulator screen (i.e. after the scrollback  part).
  private _terminalFirstRow = 0;
  private _metadataEventDoLater: DebouncedDoLater = null;
  private _commandLine: string = null;
  private _returnCode: string = null;

  private _aceEditor: TerminalAceEditor = null;
  private _aceEditSession: TerminalEditSession = null;
  private _height = 0;
  private _isEmpty = true;
  private _mode: ViewerElementTypes.Mode = ViewerElementTypes.Mode.DEFAULT;
  private _editable = false;
  private _useVPad = true;
  private _visualState: VisualState = VisualState.AUTO;

  private _mainStyleLoaded: boolean = false;
  private _resizePollHandle: Disposable = null;
  private _needEmulatorResize: boolean = false;
  
  private _operationRunning = false; // True if we are currently running an operation with the operation() method.
  private _operationEmitResize = false;  // True if a resize evnet shuld be emitted once the operation is finished.
  
  // Emulator dimensions
  private _rows = -1;
  private _columns = -1;
  private _realizedRows = -1;

  private _documentHeightRows= -1;  // Used to detect changes in the viewport size when in Cursor mode.
  private _fontUnitWidth = 10;  // slightly bogus defaults
  private _fontUnitHeight = 10;

  // The current element height. This is a cached value used to prevent touching the DOM.  
  private _currentElementHeight = -1;
  private _currentVPad = true;
  private _renderEventListener: TermApi.RenderEventHandler = this._handleRenderEvent.bind(this);

  private _bookmarkCounter = 0;
  private _bookmarkIndex = new Map<BookmarkRef, Anchor>();

  constructor() {
    super();
    this._log = getLogger(TerminalViewer.TAG_NAME, this);
    this._renderEventListener = this._handleRenderEvent.bind(this);

    this._metadataEventDoLater = new DebouncedDoLater(() => {
      const event = new CustomEvent(ViewerElement.EVENT_METADATA_CHANGE, { bubbles: true });
      this.dispatchEvent(event);
    });
  }

  dispose(): void {
  }

  getMetadata(): ViewerMetadata {
    const metadata = super.getMetadata();
    metadata.title = this._commandLine !== null ? this._commandLine : "Terminal Command";
    metadata.icon = this._returnCode === "0" ? "fa fa-check" : "fa fa-times";

    switch(this._returnCode) {
      case null:
        metadata.posture = ViewerPosture.RUNNING;
        break;
      case "0":
        metadata.posture = ViewerPosture.SUCCESS;
        break;
      default:
        metadata.posture = ViewerPosture.FAILURE;
        break;
    }

    if (this._returnCode != null) {
      metadata.toolTip = `Return code: ${this._returnCode}`;
    }

    return metadata;
  }
  
  connectedCallback(): void {
    super.connectedCallback();
    if (DomUtils.getShadowRoot(this) === null) {
      this.tabIndex = 0;
      const shadow = this.attachShadow({ mode: 'open', delegatesFocus: true });
      const clone = this.createClone();
      shadow.appendChild(clone);
      
      this._initFontLoading();
      this.installThemeCss();

      const containerDiv = DomUtils.getShadowId(this, ID_CONTAINER);

      this.style.height = "0px";
      this._exitCursorMode();
      this._mode = ViewerElementTypes.Mode.DEFAULT;

      this._aceEditSession = new TerminalEditSession(new TerminalDocument(""));
      this._aceEditSession.setUndoManager(new UndoManager());

      const aceRenderer = new TerminalRenderer(containerDiv);
      aceRenderer.setShowGutter(false);
      aceRenderer.setShowLineNumbers(false);
      aceRenderer.setPadding(0);

      this._aceEditor = new TerminalAceEditor(aceRenderer, this._aceEditSession);
      this._aceEditor.setReadOnly(true);

      this.__addCommands(DefaultCommands);
      this.__addCommands(MultiSelectCommands);
      this.__addCommands(ExtraEditCommands);

      this.__updateHasTerminalClass();

      this._aceEditor.on("change", (data, editor) => {
        if (this._mode !== ViewerElementTypes.Mode.CURSOR) {
          return;<commit_msg>Disable any indent guide feature in the TerminalAceViewer. It messes up the display.<commit_after>/**
 * Copyright 2015-2018 <NAME> <<EMAIL>>
 */

import {WebComponent} from 'extraterm-web-component-decorators';
import {BulkFileHandle, ViewerMetadata, ViewerPosture} from 'extraterm-extension-api';

import {BlobBulkFileHandle} from '../bulk_file_handling/BlobBulkFileHandle';
import {Commandable, CommandEntry, COMMAND_OPEN_COMMAND_PALETTE, dispatchCommandPaletteRequest}
from '../CommandPaletteRequestTypes';
import {doLater, doLaterFrame, DebouncedDoLater} from '../../utils/DoLater';
import * as DomUtils from '../DomUtils';
import { ExtraEditCommands } from './ExtraAceEditCommands';
import * as GeneralEvents from '../GeneralEvents';
import * as keybindingmanager from '../keybindings/KeyBindingsManager';
import {Logger, getLogger} from "extraterm-logging";
import { log } from "extraterm-logging";
import * as ResizeRefreshElementBase from '../ResizeRefreshElementBase';
import * as SupportsClipboardPaste from '../SupportsClipboardPaste';
import * as Term from '../emulator/Term';
import * as TermApi from 'term-api';
import { BookmarkRef } from './TerminalViewerTypes';
import * as ThemeTypes from '../../theme/Theme';
import {ThemeableElementBase} from '../ThemeableElementBase';
import {ViewerElement} from './ViewerElement';
import * as ViewerElementTypes from './ViewerElementTypes';
import * as VirtualScrollArea from '../VirtualScrollArea';
import { Disposable } from 'extraterm-extension-api';

import { TerminalAceEditor, TerminalDocument, TerminalEditSession, TerminalRenderer } from "extraterm-ace-terminal-renderer";
import { Anchor, Command, DefaultCommands, Editor, MultiSelectCommands, Origin, Renderer, Position, SelectionChangeEvent, UndoManager } from "ace-ts";


type KeyBindingManager = keybindingmanager.KeyBindingsManager;

type SetterState = VirtualScrollArea.SetterState;
type CursorMoveDetail = ViewerElementTypes.CursorMoveDetail;
const VisualState = ViewerElementTypes.VisualState;
type VisualState = ViewerElementTypes.VisualState;

const ID = "EtTerminalAceViewerTemplate";
const ID_CONTAINER = "ID_CONTAINER";
const ID_MAIN_STYLE = "ID_MAIN_STYLE";
const ID_CSS_VARS = "ID_CSS_VARS";
const CLASS_HIDE_CURSOR = "hide-cursor";
const CLASS_FOCUSED = "terminal-focused";
const CLASS_UNFOCUSED = "terminal-unfocused";
const CLASS_HAS_TERMINAL = "CLASS_HAS_TERMINAL";

const KEYBINDINGS_TERMINAL_VIEWER = "terminal-viewer";
const PALETTE_GROUP = "terminalviewer";
const COMMAND_TYPE_AND_CR_SELECTION = "typeSelectionAndCr";
const COMMAND_TYPE_SELECTION = "typeSelection";

const NO_STYLE_HACK = "NO_STYLE_HACK";

const DEBUG_RESIZE = false;

let cssText: string = null;

function getCssText(): string {
  return cssText;
}


@WebComponent({tag: "et-terminal-ace-viewer"})
export class TerminalViewer extends ViewerElement implements Commandable, keybindingmanager.AcceptsKeyBindingsManager,
    SupportsClipboardPaste.SupportsClipboardPaste, Disposable {

  static TAG_NAME = "ET-TERMINAL-ACE-VIEWER";
  
  static EVENT_KEYBOARD_ACTIVITY = "keyboard-activity";

  /**
   * Type guard for detecting a EtTerminalViewer instance.
   * 
   * @param  node the node to test
   * @return      True if the node is a EtTerminalViewer.
   */
  static is(node: Node): node is TerminalViewer {
    return node !== null && node !== undefined && node instanceof TerminalViewer;
  }

  private _log: Logger;
  private _keyBindingManager: KeyBindingManager = null;
  private _emulator: Term.Emulator = null;

  // The line number of the top row of the emulator screen (i.e. after the scrollback  part).
  private _terminalFirstRow = 0;
  private _metadataEventDoLater: DebouncedDoLater = null;
  private _commandLine: string = null;
  private _returnCode: string = null;

  private _aceEditor: TerminalAceEditor = null;
  private _aceEditSession: TerminalEditSession = null;
  private _height = 0;
  private _isEmpty = true;
  private _mode: ViewerElementTypes.Mode = ViewerElementTypes.Mode.DEFAULT;
  private _editable = false;
  private _useVPad = true;
  private _visualState: VisualState = VisualState.AUTO;

  private _mainStyleLoaded: boolean = false;
  private _resizePollHandle: Disposable = null;
  private _needEmulatorResize: boolean = false;
  
  private _operationRunning = false; // True if we are currently running an operation with the operation() method.
  private _operationEmitResize = false;  // True if a resize evnet shuld be emitted once the operation is finished.
  
  // Emulator dimensions
  private _rows = -1;
  private _columns = -1;
  private _realizedRows = -1;

  private _documentHeightRows= -1;  // Used to detect changes in the viewport size when in Cursor mode.
  private _fontUnitWidth = 10;  // slightly bogus defaults
  private _fontUnitHeight = 10;

  // The current element height. This is a cached value used to prevent touching the DOM.  
  private _currentElementHeight = -1;
  private _currentVPad = true;
  private _renderEventListener: TermApi.RenderEventHandler = this._handleRenderEvent.bind(this);

  private _bookmarkCounter = 0;
  private _bookmarkIndex = new Map<BookmarkRef, Anchor>();

  constructor() {
    super();
    this._log = getLogger(TerminalViewer.TAG_NAME, this);
    this._renderEventListener = this._handleRenderEvent.bind(this);

    this._metadataEventDoLater = new DebouncedDoLater(() => {
      const event = new CustomEvent(ViewerElement.EVENT_METADATA_CHANGE, { bubbles: true });
      this.dispatchEvent(event);
    });
  }

  dispose(): void {
  }

  getMetadata(): ViewerMetadata {
    const metadata = super.getMetadata();
    metadata.title = this._commandLine !== null ? this._commandLine : "Terminal Command";
    metadata.icon = this._returnCode === "0" ? "fa fa-check" : "fa fa-times";

    switch(this._returnCode) {
      case null:
        metadata.posture = ViewerPosture.RUNNING;
        break;
      case "0":
        metadata.posture = ViewerPosture.SUCCESS;
        break;
      default:
        metadata.posture = ViewerPosture.FAILURE;
        break;
    }

    if (this._returnCode != null) {
      metadata.toolTip = `Return code: ${this._returnCode}`;
    }

    return metadata;
  }
  
  connectedCallback(): void {
    super.connectedCallback();
    if (DomUtils.getShadowRoot(this) === null) {
      this.tabIndex = 0;
      const shadow = this.attachShadow({ mode: 'open', delegatesFocus: true });
      const clone = this.createClone();
      shadow.appendChild(clone);
      
      this._initFontLoading();
      this.installThemeCss();

      const containerDiv = DomUtils.getShadowId(this, ID_CONTAINER);

      this.style.height = "0px";
      this._exitCursorMode();
      this._mode = ViewerElementTypes.Mode.DEFAULT;

      this._aceEditSession = new TerminalEditSession(new TerminalDocument(""));
      this._aceEditSession.setUndoManager(new UndoManager());

      const aceRenderer = new TerminalRenderer(containerDiv);
      aceRenderer.setShowGutter(false);
      aceRenderer.setShowLineNumbers(false);
      aceRenderer.setDisplayIndentGuides(false);
      aceRenderer.setPadding(0);

      this._aceEditor = new TerminalAceEditor(aceRenderer, this._aceEditSession);
      this._aceEditor.setReadOnly(true);

      this.__addCommands(DefaultCommands);
      this.__addCommands(MultiSelectCommands);
      this.__addCommands(ExtraEditCommands);

      this.__updateHasTerminalClass();

      this._aceEditor.on("change", (data, editor) => {
        if (this._mode !== ViewerElementTypes.Mode.CURSOR) {
          return;
{'fraction_non_alphanumeric': 0.1020026702269693, 'fraction_numerical': 0.0033377837116154874, 'mean_word_length': 4.402452217814641, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 15, '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': '933774', 'n_tokens_mistral': 4512, 'n_tokens_neox': 4345, 'n_words': 1114}