<commit_before>// @flow

import buildQuery from "./build-query";
import findIndex from "lodash/findIndex";

import type { SearchModifiers } from "../../types";

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchCursor(cm, query: string, pos, modifiers: SearchModifiers) {
  const regexQuery = buildQuery(query, modifiers, { isGlobal: true });
  return cm.getSearchCursor(regexQuery, pos);
}

/**
 * @memberof utils/source-search
 * @static
 */
function SearchState() {
  this.posFrom = this.posTo = this.query = null;
  this.overlay = null;
  this.results = [];
  this.matchIndex = -1;
}

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchState(cm: any, query, modifiers) {
  let state = cm.state.search || (cm.state.search = new SearchState());

  // avoid generating a cursor and iterating over the results for an empty query
  if (query) {
    let cursor = getSearchCursor(cm, query, null, modifiers);

    state.results = [];
    while (cursor.findNext()) {
      state.results.push(cursor.pos);
    }
  }

  return state;
}

function isWhitespace(query) {
  return !query.match(/\S/);
}

/**
 * This returns a mode object used by CoeMirror's addOverlay function
 * to parse and style tokens in the file.
 * The mode object contains a tokenizer function (token) which takes
 * a character stream as input, advances it a character at a time,
 * and returns style(s) for that token. For more details see
 * https://codemirror.net/doc/manual.html#modeapi
 *
 * Also the token function code is mainly based of work done
 * by the chrome devtools team. Thanks guys! :)
 *
 * @memberof utils/source-search
 * @static
 */
function searchOverlay(query, modifiers) {
  const regexQuery = buildQuery(query, modifiers, {
    ignoreSpaces: true
  });

  let matchLength = null;

  return {
    token: function(stream) {
      if (stream.column() === 0) {
        matchLength = null;
      }
      if (matchLength !== null) {
        if (matchLength > 2) {
          for (let i = 0; i < matchLength - 2; ++i) {
            stream.next();
          }
          matchLength = 1;
          return "highlight";
        }
        stream.next();
        matchLength = null;
        return "highlight highlight-end";
      }

      const match = stream.match(regexQuery, false);
      if (match) {
        stream.next();
        const len = match[0].length;
        if (len === 1) {
          return "highlight highlight-full";
        }
        matchLength = len;
        return "highlight highlight-start";
      }
      while (!stream.match(regexQuery, false) && stream.peek()) {
        stream.next();
      }
    }
  };
}

/**
 * @memberof utils/source-search
 * @static
 */
function updateOverlay(cm, state, query, modifiers) {
  cm.removeOverlay(state.overlay);
  state.overlay = searchOverlay(query, modifiers);
  cm.addOverlay(state.overlay, { opaque: false });
}

function updateCursor(cm, state, keepSelection) {
  state.posTo = cm.getCursor("anchor");
  state.posFrom = cm.getCursor("head");

  if (!keepSelection) {
    state.posTo = { line: 0, ch: 0 };
    state.posFrom = { line: 0, ch: 0 };
  }
}

function getMatchIndex(count: number, currentIndex: number, rev: boolean) {
  if (!rev) {
    if (currentIndex == count - 1) {
      return 0;
    }

    return currentIndex + 1;
  }

  if (currentIndex == 0) {
    return count - 1;
  }

  return currentIndex - 1;
}

/**
 * If there's a saved search, selects the next results.
 * Otherwise, creates a new search and selects the first
 * result.
 *
 * @memberof utils/source-search
 * @static
 */
function doSearch(ctx, rev, query, keepSelection, modifiers: SearchModifiers) {
  let { cm } = ctx;
  let matchIndex = 0;
  cm.operation(function() {
    if (!query || isWhitespace(query)) {
      return;
    }

    let state = getSearchState(cm, query, modifiers);
    const newQuery = state.query != query;
    state.query = query;

    updateOverlay(cm, state, query, modifiers);
    updateCursor(cm, state, keepSelection);

    const nextMatch = searchNext(ctx, rev, query, newQuery, modifiers);
    if (nextMatch) {
      if (state.matchIndex === -1) {
        matchIndex = findIndex(state.results, nextMatch);
      } else {
        const count = state.results.length;
        const currentIndex = state.matchIndex;
        matchIndex = getMatchIndex(count, currentIndex, rev);
      }

      state.matchIndex = matchIndex;
    }
  });

  return matchIndex;
}

function getCursorPos(newQuery, rev, state) {
  if (newQuery) {
    return rev ? state.posFrom : state.posTo;
  }

  return rev ? state.posTo : state.posFrom;
}

/**
 * Selects the next result of a saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function searchNext(ctx, rev, query, newQuery, modifiers) {
  let { cm, ed } = ctx;
  let nextMatch;
  cm.operation(function() {
    let state = getSearchState(cm, query, modifiers);
    const pos = getCursorPos(newQuery, rev, state);

    if (!state.query) {
      return;
    }

    let cursor = getSearchCursor(cm, state.query, pos, modifiers);

    const location = rev
      ? { line: cm.lastLine(), ch: null }
      : { line: cm.firstLine(), ch: 0 };

    if (!cursor.find(rev) && state.query) {
      cursor = getSearchCursor(cm, state.query, location, modifiers);
      if (!cursor.find(rev)) {
        return;
      }
    }

    // We don't want to jump the editor
    // when we're selecting text
    if (!cm.state.selectingText) {
      ed.alignLine(cursor.from().line, "center");
      cm.setSelection(cursor.from(), cursor.to());
    }

    nextMatch = { from: cursor.from(), to: cursor.to() };
  });

  return nextMatch;
}

/**
 * Remove overlay.
 *
 * @memberof utils/source-search
 * @static
 */
function removeOverlay(ctx: any, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(ctx.cm, query, modifiers);
  ctx.cm.removeOverlay(state.overlay);
  const { line, ch } = ctx.cm.getCursor();
  ctx.cm.doc.setSelection({ line, ch }, { line, ch }, { scroll: false });
}

/**
 * Clears the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function clearSearch(cm, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(cm, query, modifiers);

  state.results = [];
  state.matchIndex = -1;

  if (!state.query) {
    return;
  }
  cm.removeOverlay(state.overlay);
  state.query = null;
}

/**
 * Starts a new search.
 *
 * @memberof utils/source-search
 * @static
 */
function find(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  clearSearch(ctx.cm, query, modifiers);
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the next item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findNext(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the previous item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findPrev(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  return doSearch(ctx, true, query, keepSelection, modifiers);
}

function clearIndex(ctx: any, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(ctx.cm, query, modifiers);
  state.matchIndex = -1;
}

function countMatches(
  query: string,
  text: string,
  modifiers: SearchModifiers
): number {
  const regexQuery = buildQuery(query, modifiers, {
    isGlobal: true
  });
  const match = text.match(regexQuery);
  return match ? match.length : 0;
}

export {
  buildQuery,
  clearIndex,
  countMatches,
  find,
  findNext,
  findPrev,
  removeOverlay,
  getMatchIndex
};
<commit_msg>Disable match index (#3008)<commit_after>// @flow

import buildQuery from "./build-query";

import type { SearchModifiers } from "../../types";

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchCursor(cm, query: string, pos, modifiers: SearchModifiers) {
  const regexQuery = buildQuery(query, modifiers, { isGlobal: true });
  return cm.getSearchCursor(regexQuery, pos);
}

/**
 * @memberof utils/source-search
 * @static
 */
function SearchState() {
  this.posFrom = this.posTo = this.query = null;
  this.overlay = null;
  this.results = [];
  this.matchIndex = -1;
}

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchState(cm: any, query, modifiers) {
  let state = cm.state.search || (cm.state.search = new SearchState());
  return state;
}

function isWhitespace(query) {
  return !query.match(/\S/);
}

/**
 * This returns a mode object used by CoeMirror's addOverlay function
 * to parse and style tokens in the file.
 * The mode object contains a tokenizer function (token) which takes
 * a character stream as input, advances it a character at a time,
 * and returns style(s) for that token. For more details see
 * https://codemirror.net/doc/manual.html#modeapi
 *
 * Also the token function code is mainly based of work done
 * by the chrome devtools team. Thanks guys! :)
 *
 * @memberof utils/source-search
 * @static
 */
function searchOverlay(query, modifiers) {
  const regexQuery = buildQuery(query, modifiers, {
    ignoreSpaces: true
  });

  let matchLength = null;

  return {
    token: function(stream) {
      if (stream.column() === 0) {
        matchLength = null;
      }
      if (matchLength !== null) {
        if (matchLength > 2) {
          for (let i = 0; i < matchLength - 2; ++i) {
            stream.next();
          }
          matchLength = 1;
          return "highlight";
        }
        stream.next();
        matchLength = null;
        return "highlight highlight-end";
      }

      const match = stream.match(regexQuery, false);
      if (match) {
        stream.next();
        const len = match[0].length;
        if (len === 1) {
          return "highlight highlight-full";
        }
        matchLength = len;
        return "highlight highlight-start";
      }
      while (!stream.match(regexQuery, false) && stream.peek()) {
        stream.next();
      }
    }
  };
}

/**
 * @memberof utils/source-search
 * @static
 */
function updateOverlay(cm, state, query, modifiers) {
  cm.removeOverlay(state.overlay);
  state.overlay = searchOverlay(query, modifiers);
  cm.addOverlay(state.overlay, { opaque: false });
}

function updateCursor(cm, state, keepSelection) {
  state.posTo = cm.getCursor("anchor");
  state.posFrom = cm.getCursor("head");

  if (!keepSelection) {
    state.posTo = { line: 0, ch: 0 };
    state.posFrom = { line: 0, ch: 0 };
  }
}

function getMatchIndex(count: number, currentIndex: number, rev: boolean) {
  if (!rev) {
    if (currentIndex == count - 1) {
      return 0;
    }

    return currentIndex + 1;
  }

  if (currentIndex == 0) {
    return count - 1;
  }

  return currentIndex - 1;
}

/**
 * If there's a saved search, selects the next results.
 * Otherwise, creates a new search and selects the first
 * result.
 *
 * @memberof utils/source-search
 * @static
 */
function doSearch(ctx, rev, query, keepSelection, modifiers: SearchModifiers) {
  let { cm } = ctx;
  const matchIndex = -1;

  cm.operation(function() {
    if (!query || isWhitespace(query)) {
      return;
    }

    let state = getSearchState(cm, query, modifiers);
    const newQuery = state.query != query;
    state.query = query;

    updateOverlay(cm, state, query, modifiers);
    updateCursor(cm, state, keepSelection);
    searchNext(ctx, rev, query, newQuery, modifiers);

    // NOTE: We would like to find the correct match index based on where the
    // match is in the document.
    state.matchIndex = matchIndex;
  });

  return matchIndex;
}

function getCursorPos(newQuery, rev, state) {
  if (newQuery) {
    return rev ? state.posFrom : state.posTo;
  }

  return rev ? state.posTo : state.posFrom;
}

/**
 * Selects the next result of a saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function searchNext(ctx, rev, query, newQuery, modifiers) {
  let { cm, ed } = ctx;
  let nextMatch;
  cm.operation(function() {
    let state = getSearchState(cm, query, modifiers);
    const pos = getCursorPos(newQuery, rev, state);

    if (!state.query) {
      return;
    }

    let cursor = getSearchCursor(cm, state.query, pos, modifiers);

    const location = rev
      ? { line: cm.lastLine(), ch: null }
      : { line: cm.firstLine(), ch: 0 };

    if (!cursor.find(rev) && state.query) {
      cursor = getSearchCursor(cm, state.query, location, modifiers);
      if (!cursor.find(rev)) {
        return;
      }
    }

    // We don't want to jump the editor
    // when we're selecting text
    if (!cm.state.selectingText) {
      ed.alignLine(cursor.from().line, "center");
      cm.setSelection(cursor.from(), cursor.to());
    }

    nextMatch = { from: cursor.from(), to: cursor.to() };
  });

  return nextMatch;
}

/**
 * Remove overlay.
 *
 * @memberof utils/source-search
 * @static
 */
function removeOverlay(ctx: any, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(ctx.cm, query, modifiers);
  ctx.cm.removeOverlay(state.overlay);
  const { line, ch } = ctx.cm.getCursor();
  ctx.cm.doc.setSelection({ line, ch }, { line, ch }, { scroll: false });
}

/**
 * Clears the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function clearSearch(cm, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(cm, query, modifiers);

  state.results = [];
  state.matchIndex = -1;

  if (!state.query) {
    return;
  }
  cm.removeOverlay(state.overlay);
  state.query = null;
}

/**
 * Starts a new search.
 *
 * @memberof utils/source-search
 * @static
 */
function find(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  clearSearch(ctx.cm, query, modifiers);
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the next item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findNext(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the previous item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findPrev(
  ctx: any,
  query: string,
  keepSelection: boolean,
  modifiers: SearchModifiers
) {
  return doSearch(ctx, true, query, keepSelection, modifiers);
}

function clearIndex(ctx: any, query: string, modifiers: SearchModifiers) {
  let state = getSearchState(ctx.cm, query, modifiers);
  state.matchIndex = -1;
}

function countMatches(
  query: string,
  text: string,
  modifiers: SearchModifiers
): number {
  const regexQuery = buildQuery(query, modifiers, {
    isGlobal: true
  });
  const match = text.match(regexQuery);
  return match ? match.length : 0;
}

export {
  buildQuery,
  clearIndex,
  countMatches,
  find,
  findNext,
  findPrev,
  removeOverlay,
  getMatchIndex
};

{'fraction_non_alphanumeric': 0.13340398754719482, 'fraction_numerical': 0.0033781546002517055, 'mean_word_length': 3.045551982851018, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, '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': '7038422', 'n_tokens_mistral': 4845, 'n_tokens_neox': 4773, 'n_words': 1322}