<commit_before>/**
 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
 * @license MIT
 */

import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';

export interface ISearchOptions {
  regex?: boolean;
  wholeWord?: boolean;
  caseSensitive?: boolean;
  incremental?: boolean;
  decorations?: IDecorationColor;
}

interface IDecorationColor {
  matchColor: string;
  selectedColor: string;
}

export interface ISearchPosition {
  startCol: number;
  startRow: number;
}

export interface ISearchResult {
  term: string;
  col: number;
  row: number;
  size: number;
}

type LineCacheEntry = [
  /**
   * The string representation of a line (as opposed to the buffer cell representation).
   */
  lineAsString: string,
  /**
   * The offsets where each line starts when the entry describes a wrapped line.
   */
  lineOffsets: number[]
];

const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs

export class SearchAddon implements ITerminalAddon {
  private _terminal: Terminal | undefined;
  private _result: ISearchResult | undefined;
  private _dataChanged: boolean = false;
  private _cachedSearchTerm: string | undefined;
  private _selectedDecoration: IDecoration | undefined;
  private _resultDecorations: Map<number, IDecoration[]> = new Map<number, IDecoration[]>();
  private _searchResults:  Map<string, ISearchResult> = new Map();
  private _onDataDisposable: IDisposable | undefined;
  /**
   * translateBufferLineToStringWithWrap is a fairly expensive call.
   * We memoize the calls into an array that has a time based ttl.
   * _linesCache is also invalidated when the terminal cursor moves.
   */
  private _linesCache: LineCacheEntry[] | undefined;
  private _linesCacheTimeoutId = 0;
  private _cursorMoveListener: IDisposable | undefined;
  private _resizeListener: IDisposable | undefined;

  public activate(terminal: Terminal): void {
    this._terminal = terminal;
    this._onDataDisposable = this._terminal.onData(() => this._dataChanged = true);
  }

  public dispose(): void {
    this.clear();
    this._onDataDisposable?.dispose();
  }

  public clear(): void {
    this._selectedDecoration?.dispose();
    this._terminal?.clearSelection();
    this._searchResults.clear();
    this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
    this._resultDecorations.clear();
    this._cachedSearchTerm = undefined;
    this._dataChanged = true;
  }

  /**
   * Find the next instance of the term, then scroll to and select it. If it
   * doesn't exist, do nothing.
   * @param term The search term.
   * @param searchOptions Search options.
   * @return Whether a result was found.
   */
  public findNext(term: string, searchOptions?: ISearchOptions): boolean {
    if (!this._terminal) {
      throw new Error('Cannot use addon until it has been loaded');
    }
    return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
  }

  private _highlightAllMatches(term: string, searchOptions: ISearchOptions, type: 'next' | 'previous'): boolean {
    if (!this._terminal) {
      throw new Error('cannot find all matches with no terminal');
    }
    if (!term || term.length === 0) {
      this.clear();
      return false;
    }
    searchOptions = searchOptions || {};
    if (term === this._cachedSearchTerm && !this._dataChanged) {
      return type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    }
    // new search, clear out the old decorations
    this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
    this._resultDecorations.clear();
    this._searchResults.clear();

    if (!this._terminal.options.overviewRulerWidth) {
      this._terminal.options.overviewRulerWidth = 10;
    }
    searchOptions.incremental = false;
    let found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
      if (this._result) {
        this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
      }
      found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    }
    this._searchResults.forEach(result => {
      const resultDecoration = this._createResultDecoration(result, searchOptions.decorations);
      if (resultDecoration) {
        const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
        decorationsForLine.push(resultDecoration);
        this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
      }
    });
    this._dataChanged = false;<commit_msg>set data changed to false if true<commit_after>/**
 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
 * @license MIT
 */

import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';

export interface ISearchOptions {
  regex?: boolean;
  wholeWord?: boolean;
  caseSensitive?: boolean;
  incremental?: boolean;
  decorations?: IDecorationColor;
}

interface IDecorationColor {
  matchColor: string;
  selectedColor: string;
}

export interface ISearchPosition {
  startCol: number;
  startRow: number;
}

export interface ISearchResult {
  term: string;
  col: number;
  row: number;
  size: number;
}

type LineCacheEntry = [
  /**
   * The string representation of a line (as opposed to the buffer cell representation).
   */
  lineAsString: string,
  /**
   * The offsets where each line starts when the entry describes a wrapped line.
   */
  lineOffsets: number[]
];

const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs

export class SearchAddon implements ITerminalAddon {
  private _terminal: Terminal | undefined;
  private _result: ISearchResult | undefined;
  private _dataChanged: boolean = false;
  private _cachedSearchTerm: string | undefined;
  private _selectedDecoration: IDecoration | undefined;
  private _resultDecorations: Map<number, IDecoration[]> = new Map<number, IDecoration[]>();
  private _searchResults:  Map<string, ISearchResult> = new Map();
  private _onDataDisposable: IDisposable | undefined;
  /**
   * translateBufferLineToStringWithWrap is a fairly expensive call.
   * We memoize the calls into an array that has a time based ttl.
   * _linesCache is also invalidated when the terminal cursor moves.
   */
  private _linesCache: LineCacheEntry[] | undefined;
  private _linesCacheTimeoutId = 0;
  private _cursorMoveListener: IDisposable | undefined;
  private _resizeListener: IDisposable | undefined;

  public activate(terminal: Terminal): void {
    this._terminal = terminal;
    this._onDataDisposable = this._terminal.onData(() => this._dataChanged = true);
  }

  public dispose(): void {
    this.clear();
    this._onDataDisposable?.dispose();
  }

  public clear(): void {
    this._selectedDecoration?.dispose();
    this._terminal?.clearSelection();
    this._searchResults.clear();
    this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
    this._resultDecorations.clear();
    this._cachedSearchTerm = undefined;
    this._dataChanged = true;
  }

  /**
   * Find the next instance of the term, then scroll to and select it. If it
   * doesn't exist, do nothing.
   * @param term The search term.
   * @param searchOptions Search options.
   * @return Whether a result was found.
   */
  public findNext(term: string, searchOptions?: ISearchOptions): boolean {
    if (!this._terminal) {
      throw new Error('Cannot use addon until it has been loaded');
    }
    return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
  }

  private _highlightAllMatches(term: string, searchOptions: ISearchOptions, type: 'next' | 'previous'): boolean {
    if (!this._terminal) {
      throw new Error('cannot find all matches with no terminal');
    }
    if (!term || term.length === 0) {
      this.clear();
      return false;
    }
    searchOptions = searchOptions || {};
    if (term === this._cachedSearchTerm && !this._dataChanged) {
      return type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    }
    // new search, clear out the old decorations
    this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
    this._resultDecorations.clear();
    this._searchResults.clear();

    if (!this._terminal.options.overviewRulerWidth) {
      this._terminal.options.overviewRulerWidth = 10;
    }
    searchOptions.incremental = false;
    let found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
      if (this._result) {
        this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
      }
      found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
    }
    this._searchResults.forEach(result => {
      const resultDecoration = this._createResultDecoration(result, searchOptions.decorations);
      if (resultDecoration) {
        const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
        decorationsForLine.push(resultDecoration);
        this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
      }
    });
    if (this._dataChanged) {
      this._dataChanged = false;
    }
{'fraction_non_alphanumeric': 0.11971901655795283, 'fraction_numerical': 0.0032112393376818866, 'mean_word_length': 4.023185483870968, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 23, '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': '3390383', 'n_tokens_mistral': 2956, 'n_tokens_neox': 2840, 'n_words': 739}