<commit_before>/* @flow */

import os from 'os';
import path from 'path';
import readline from 'readline';
import tty from 'tty';
import * as cljs from './cljs';
import replHistory from './replHistory';
import { currentTimeMicros, isWhitespace } from './util';
import { close as socketServerClose } from './socketRepl';

import type { CLIOptsType } from './cli';

type KeyType = {
  name: string,
  ctrl?: boolean,
  shift?: boolean,
  meta?: boolean,
  code?: string,
};

export type REPLSession = {
  id: number,
  rl: readline$Interface,
  isMain: boolean,
  isReverseSearch: boolean,
  reverseSearchBuffer: string,
  input: string,
  searchPos: number,
  previousPrompt: string,
  previousLine: string,
  previousCursor: number,
};

const exitCommands = new Set([':cljs/quit', 'exit']);
let sessionCount = 0;
let lastKeypressTime: number;
let isPasting: boolean;
const pendingHighlights = [];

const sessions: { [key: number]: REPLSession } = {};

export function prompt(
  rl: readline$Interface,
  isSecondary: boolean = false,
  p: string = cljs.getCurrentNamespace(),
): void {
  let promptText;

  if (isSecondary) {
    const spaces = ' '.repeat(p.length - 2);
    promptText = `${spaces}#_=> `;
  } else {
    promptText = `${p}=> `;
  }
  rl.setPrompt(promptText);
  rl.prompt();
}

// eslint-disable-next-line import/no-mutable-exports
export let currentREPLInterface: ?readline$Interface;

export function deleteSession(session: REPLSession): void {
  cljs.clearREPLSessionState(session.id);
  delete sessions[session.id];
}

function stopREPL(): void {
  socketServerClose();

  const keys = Object.keys(sessions);
  keys.forEach((id: string) => deleteSession(sessions[parseInt(id, 10)]));

  process.exit();
}

export function processLine(replSession: REPLSession, line: string): void {
  const session = replSession;
  const { input, rl, isMain } = session;

  let extraForms = false;

  if (exitCommands.has(line.trim())) {
    // $FlowIssue - use of rl.output
    return isMain ? stopREPL() : rl.output.destroy();
  }

  if (isWhitespace(input)) {
    session.input = line;
  } else {
    session.input = `${input}\n${line}`;
  }

  for (;;) {
    const currentInput = session.input;
    extraForms = cljs.isReadable(currentInput);

    if (extraForms !== false) {
      session.input = currentInput.substring(
        0,
        currentInput.length - extraForms.length,
      );

      if (!isWhitespace(session.input)) {
        cljs.setPrintFns(rl.output);
        currentREPLInterface = rl;

        cljs.execute(session.input, 'text', true, true, session.id);

        currentREPLInterface = null;
        cljs.setPrintFns();
        // If *print-newline* is off, we need to emit a newline now, otherwise
        // the prompt and line editing will overwrite any printed output on the
        // current line.
        if (!cljs.isPrintingNewline()) {
          rl.output.write('\n');
        }
      } else {
        prompt(rl);
        break;
      }

      session.input = extraForms;
    } else {
      // partially entered form, prepare for processing the next line.
      prompt(rl, true);

      if (!isPasting) {
        const spaceCount = cljs.indentSpaceCount(currentInput);
        if (spaceCount !== -1) {
          rl.write(' '.repeat(spaceCount));
        }
      }
      break;
    }
  }

  return undefined;
}

function stopReverseSearch(
  replSession: REPLSession,
  clear: boolean = true,
): void {
  const session = replSession;
  session.isReverseSearch = false;
  session.reverseSearchBuffer = '';

  if (clear) {
    session.rl.write(null, {
      ctrl: true,
      name: 'u',
    });
  }
}

function handleSIGINT(replSession: REPLSession): void {
  const session = replSession;
  const lineLength = session.rl.line.length + session.rl._prompt.length;
  // $FlowIssue: columns exists in stream$Writable
  const numberOfLines = Math.floor(lineLength / session.rl.output.columns);
  session.input = '';

  prompt(session.rl);
  session.rl.output.write('\n'.repeat(numberOfLines + 2));

  stopReverseSearch(replSession);
}

function highlight(
  replSession: REPLSession,
  char: string,
  line: string,
  cursor: number,
): void {
  const session = replSession;
  const { rl, input } = session;
  const pos = cursor - 1;

  if (char === ')' || char === ']' || char === '}') {
    // $FlowIssue: columns exists in stream$Writable
    const terminalColumns = rl.output.columns;
    const promptLength = rl._prompt.length;
    const lines = input === '' ? [] : input.split('\n');
    lines.push(line);
    const [cursorX, linesUp] = cljs.getHighlightCoordinates(lines, pos);
    let dx = cursor - cursorX;
    let dy = linesUp;

    if (cursor + promptLength > terminalColumns) {
      dx = (dx - terminalColumns) % terminalColumns;
      dy += Math.floor(cursor / (terminalColumns - promptLength));
    }<commit_msg>follow-up fix on brace highlighting<commit_after>/* @flow */

import os from 'os';
import path from 'path';
import readline from 'readline';
import tty from 'tty';
import * as cljs from './cljs';
import replHistory from './replHistory';
import { currentTimeMicros, isWhitespace } from './util';
import { close as socketServerClose } from './socketRepl';

import type { CLIOptsType } from './cli';

type KeyType = {
  name: string,
  ctrl?: boolean,
  shift?: boolean,
  meta?: boolean,
  code?: string,
};

export type REPLSession = {
  id: number,
  rl: readline$Interface,
  isMain: boolean,
  isReverseSearch: boolean,
  reverseSearchBuffer: string,
  input: string,
  searchPos: number,
  previousPrompt: string,
  previousLine: string,
  previousCursor: number,
};

const exitCommands = new Set([':cljs/quit', 'exit']);
let sessionCount = 0;
let lastKeypressTime: number;
let isPasting: boolean;
const pendingHighlights = [];

const sessions: { [key: number]: REPLSession } = {};

export function prompt(
  rl: readline$Interface,
  isSecondary: boolean = false,
  p: string = cljs.getCurrentNamespace(),
): void {
  let promptText;

  if (isSecondary) {
    const spaces = ' '.repeat(p.length - 2);
    promptText = `${spaces}#_=> `;
  } else {
    promptText = `${p}=> `;
  }
  rl.setPrompt(promptText);
  rl.prompt();
}

// eslint-disable-next-line import/no-mutable-exports
export let currentREPLInterface: ?readline$Interface;

export function deleteSession(session: REPLSession): void {
  cljs.clearREPLSessionState(session.id);
  delete sessions[session.id];
}

function stopREPL(): void {
  socketServerClose();

  const keys = Object.keys(sessions);
  keys.forEach((id: string) => deleteSession(sessions[parseInt(id, 10)]));

  process.exit();
}

export function processLine(replSession: REPLSession, line: string): void {
  const session = replSession;
  const { input, rl, isMain } = session;

  let extraForms = false;

  if (exitCommands.has(line.trim())) {
    // $FlowIssue - use of rl.output
    return isMain ? stopREPL() : rl.output.destroy();
  }

  if (isWhitespace(input)) {
    session.input = line;
  } else {
    session.input = `${input}\n${line}`;
  }

  for (;;) {
    const currentInput = session.input;
    extraForms = cljs.isReadable(currentInput);

    if (extraForms !== false) {
      session.input = currentInput.substring(
        0,
        currentInput.length - extraForms.length,
      );

      if (!isWhitespace(session.input)) {
        cljs.setPrintFns(rl.output);
        currentREPLInterface = rl;

        cljs.execute(session.input, 'text', true, true, session.id);

        currentREPLInterface = null;
        cljs.setPrintFns();
        // If *print-newline* is off, we need to emit a newline now, otherwise
        // the prompt and line editing will overwrite any printed output on the
        // current line.
        if (!cljs.isPrintingNewline()) {
          rl.output.write('\n');
        }
      } else {
        prompt(rl);
        break;
      }

      session.input = extraForms;
    } else {
      // partially entered form, prepare for processing the next line.
      prompt(rl, true);

      if (!isPasting) {
        const spaceCount = cljs.indentSpaceCount(currentInput);
        if (spaceCount !== -1) {
          rl.write(' '.repeat(spaceCount));
        }
      }
      break;
    }
  }

  return undefined;
}

function stopReverseSearch(
  replSession: REPLSession,
  clear: boolean = true,
): void {
  const session = replSession;
  session.isReverseSearch = false;
  session.reverseSearchBuffer = '';

  if (clear) {
    session.rl.write(null, {
      ctrl: true,
      name: 'u',
    });
  }
}

function handleSIGINT(replSession: REPLSession): void {
  const session = replSession;
  const lineLength = session.rl.line.length + session.rl._prompt.length;
  // $FlowIssue: columns exists in stream$Writable
  const numberOfLines = Math.floor(lineLength / session.rl.output.columns);
  session.input = '';

  prompt(session.rl);
  session.rl.output.write('\n'.repeat(numberOfLines + 2));

  stopReverseSearch(replSession);
}

function highlight(
  replSession: REPLSession,
  char: string,
  line: string,
  cursor: number,
): void {
  const session = replSession;
  const { rl, input } = session;

  if (char === ')' || char === ']' || char === '}') {
    // $FlowIssue: columns exists in stream$Writable
    const terminalColumns = rl.output.columns;
    const promptLength = rl._prompt.length;
    const cursorAbsolutePos = cursor + promptLength;
    const lines = input === '' ? [] : input.split('\n');
    lines.push(line);
    const [cursorX, linesUp] = cljs.getHighlightCoordinates(lines, cursor - 1);

    let dx = cursor - cursorX;
    let dy = linesUp;

    if (
      cursorAbsolutePos > terminalColumns &&
      dx > cursorAbsolutePos % terminalColumns
    ) {
      dx = (dx - terminalColumns) % terminalColumns;
      dy += Math.floor(cursorAbsolutePos / terminalColumns);
    }
{'fraction_non_alphanumeric': 0.1359796437659033, 'fraction_numerical': 0.0016284987277353689, 'mean_word_length': 3.1670907548770146, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 12, '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': '2304292', 'n_tokens_mistral': 3139, 'n_tokens_neox': 3064, 'n_words': 776}