<commit_before>import { Channels } from "@nteract/messaging";
import { Media, RichMedia } from "@nteract/outputs";
import CodeMirror, {
  Doc,
  Editor,
  EditorChangeLinkedList,
  EditorFromTextArea,
  Position,
  Token
} from "codemirror";

import {
  configurableCodeMirrorOptions,
  FullEditorConfiguration,
  isConfigurable
} from "./configurable";

import { debounce } from "lodash";
import * as React from "react";
import ReactDOM from "react-dom";
import {
  empty,
  fromEvent,
  merge,
  Observable,
  of,
  Subject,
  Subscription
} from "rxjs";
import {
  catchError,
  debounceTime,
  map,
  partition,
  repeat,
  switchMap,
  takeUntil
} from "rxjs/operators";

import { excludedIntelliSenseTriggerKeys } from "./excludedIntelliSenseKeys";
import { codeComplete, pick } from "./jupyter/complete";
import { tool } from "./jupyter/tooltip";

import { InitialTextArea } from "./components/initial-text-area";

import CodeMirrorCSS from "./vendored/codemirror";
import ShowHintCSS from "./vendored/show-hint";

export { CodeMirrorCSS, ShowHintCSS };

import styled, { StyledComponent } from "styled-components";

const TipButton: StyledComponent<"button", never> = styled.button`
  float: right;
  display: inline-block;
  position: absolute;
  top: 0px;
  right: 0px;
  font-size: 11.5px;
`;

const Tip: StyledComponent<"div", never> = styled.div`
  padding: 20px 20px 50px 20px;
  margin: 30px 20px 50px 20px;
  box-shadow: 2px 2px 50px rgba(0, 0, 0, 0.2);
  white-space: pre-wrap;
  background-color: var(--theme-app-bg);
  z-index: 9999999;
`;

function normalizeLineEndings(str: string): string {
  if (!str) {
    return str;
  }
  return str.replace(/\r\n|\r/g, "\n");
}

export interface EditorKeyEvent {
  editor: Editor;
  ev: KeyboardEvent;
}

export type CodeMirrorEditorProps = {
  preserveScrollPosition: boolean;
  editorFocused: boolean;
  completion: boolean;
  tip?: boolean;
  focusAbove?: (instance: Editor) => void;
  focusBelow?: (instance: Editor) => void;
  // _Our_ theme, not the codemirror one we use
  theme: string;
  channels?: Channels | null;
  // TODO: We only check if this is idle, so the completion provider should only
  //       care about this when kernelStatus === idle _and_ we're the active cell
  //       could instead call it `canTriggerCompletion` and reduce our current re-renders
  kernelStatus: string;
  onChange?: (value: string, change: EditorChangeLinkedList) => void;
  onFocusChange?: (focused: boolean) => void;
  value: string;
} & Partial<FullEditorConfiguration>;

interface CodeMirrorEditorState {
  isFocused: boolean;
  tipElement?: any;
}

interface CodeCompletionEvent {
  editor: Editor & Doc;
  callback: (completionResult: any) => {};
  debounce: boolean;
}

export default class CodeMirrorEditor extends React.PureComponent<
  CodeMirrorEditorProps,
  CodeMirrorEditorState
> {
  static defaultProps: Partial<CodeMirrorEditorProps> = {
    value: "",
    channels: null,
    completion: false,
    editorFocused: false,
    kernelStatus: "not connected",
    theme: "light",
    tip: false,
    autofocus: false,

    // CodeMirror specific options for defaults
    matchBrackets: true,
    indentUnit: 4,
    lineNumbers: false,
    cursorBlinkRate: 530
  };

  textarea?: HTMLTextAreaElement | null;
  cm!: EditorFromTextArea;
  defaultOptions: FullEditorConfiguration;
  keyupEventsSubscriber!: Subscription;
  completionSubject!: Subject<CodeCompletionEvent>;
  completionEventsSubscriber!: Subscription;
  debounceNextCompletionRequest: boolean;

  textareaRef: React.RefObject<HTMLTextAreaElement> = React.createRef<
    HTMLTextAreaElement
  >();

  constructor(props: CodeMirrorEditorProps) {
    super(props);
    this.hint = this.hint.bind(this);
    (this.hint as any).async = true;
    this.tips = this.tips.bind(this);
    this.deleteTip = this.deleteTip.bind(this);
    this.debounceNextCompletionRequest = true;
    this.state = { isFocused: true, tipElement: null };

    this.fullOptions = this.fullOptions.bind(this);
    this.cleanMode = this.cleanMode.bind(this);

    // Bind our events to codemirror
    const extraKeys = {
      "Cmd-.": this.tips,
      "Cmd-/": "toggleComment",
      "Ctrl-.": this.tips,
      "Ctrl-/": "toggleComment",
      "Ctrl-Space": (editor: Editor) => {
        this.debounceNextCompletionRequest = false;
        return editor.execCommand("autocomplete");
      },
      Down: this.goLineDownOrEmit,
      "Shift-Tab": (editor: Editor) => editor.execCommand("indentLess"),
      Tab: this.executeTab,
      Up: this.goLineUpOrEmit
    };

    const hintOptions = {
      // In automatic autocomplete mode we don't want override
      completeSingle: false,
      extraKeys: {
        Right: pick
      },
      hint: this.hint
    };

    this.defaultOptions = Object.assign({
      extraKeys,
      hintOptions,
      // This sets the class on the codemirror <div> that gets created to
      // cm-s-composition
      theme: "composition"
    });
  }

  fullOptions(defaults: FullEditorConfiguration = {}) {
    // Only pass to codemirror the options we support
    return Object.keys(this.props)
      .filter(isConfigurable)
      .reduce((obj, key) => {
        obj[key] = this.props[key];
        return obj;
      }, defaults);
  }

  cleanMode(): string | object {
    if (!this.props.mode) {
      return "text/plain";
    }
    if (typeof this.props.mode === "string") {
      return this.props.mode;
    }<commit_msg>Enable lineWrapping by default in editor<commit_after>import { Channels } from "@nteract/messaging";
import { Media, RichMedia } from "@nteract/outputs";
import CodeMirror, {
  Doc,
  Editor,
  EditorChangeLinkedList,
  EditorFromTextArea,
  Position,
  Token
} from "codemirror";

import {
  configurableCodeMirrorOptions,
  FullEditorConfiguration,
  isConfigurable
} from "./configurable";

import { debounce } from "lodash";
import * as React from "react";
import ReactDOM from "react-dom";
import {
  empty,
  fromEvent,
  merge,
  Observable,
  of,
  Subject,
  Subscription
} from "rxjs";
import {
  catchError,
  debounceTime,
  map,
  partition,
  repeat,
  switchMap,
  takeUntil
} from "rxjs/operators";

import { excludedIntelliSenseTriggerKeys } from "./excludedIntelliSenseKeys";
import { codeComplete, pick } from "./jupyter/complete";
import { tool } from "./jupyter/tooltip";

import { InitialTextArea } from "./components/initial-text-area";

import CodeMirrorCSS from "./vendored/codemirror";
import ShowHintCSS from "./vendored/show-hint";

export { CodeMirrorCSS, ShowHintCSS };

import styled, { StyledComponent } from "styled-components";

const TipButton: StyledComponent<"button", never> = styled.button`
  float: right;
  display: inline-block;
  position: absolute;
  top: 0px;
  right: 0px;
  font-size: 11.5px;
`;

const Tip: StyledComponent<"div", never> = styled.div`
  padding: 20px 20px 50px 20px;
  margin: 30px 20px 50px 20px;
  box-shadow: 2px 2px 50px rgba(0, 0, 0, 0.2);
  white-space: pre-wrap;
  background-color: var(--theme-app-bg);
  z-index: 9999999;
`;

function normalizeLineEndings(str: string): string {
  if (!str) {
    return str;
  }
  return str.replace(/\r\n|\r/g, "\n");
}

export interface EditorKeyEvent {
  editor: Editor;
  ev: KeyboardEvent;
}

export type CodeMirrorEditorProps = {
  preserveScrollPosition: boolean;
  editorFocused: boolean;
  completion: boolean;
  tip?: boolean;
  focusAbove?: (instance: Editor) => void;
  focusBelow?: (instance: Editor) => void;
  // _Our_ theme, not the codemirror one we use
  theme: string;
  channels?: Channels | null;
  // TODO: We only check if this is idle, so the completion provider should only
  //       care about this when kernelStatus === idle _and_ we're the active cell
  //       could instead call it `canTriggerCompletion` and reduce our current re-renders
  kernelStatus: string;
  onChange?: (value: string, change: EditorChangeLinkedList) => void;
  onFocusChange?: (focused: boolean) => void;
  value: string;
} & Partial<FullEditorConfiguration>;

interface CodeMirrorEditorState {
  isFocused: boolean;
  tipElement?: any;
}

interface CodeCompletionEvent {
  editor: Editor & Doc;
  callback: (completionResult: any) => {};
  debounce: boolean;
}

export default class CodeMirrorEditor extends React.PureComponent<
  CodeMirrorEditorProps,
  CodeMirrorEditorState
> {
  static defaultProps: Partial<CodeMirrorEditorProps> = {
    value: "",
    channels: null,
    completion: false,
    editorFocused: false,
    kernelStatus: "not connected",
    theme: "light",
    tip: false,
    autofocus: false,

    // CodeMirror specific options for defaults
    matchBrackets: true,
    indentUnit: 4,
    lineNumbers: false,
    cursorBlinkRate: 530
  };

  textarea?: HTMLTextAreaElement | null;
  cm!: EditorFromTextArea;
  defaultOptions: FullEditorConfiguration;
  keyupEventsSubscriber!: Subscription;
  completionSubject!: Subject<CodeCompletionEvent>;
  completionEventsSubscriber!: Subscription;
  debounceNextCompletionRequest: boolean;

  textareaRef: React.RefObject<HTMLTextAreaElement> = React.createRef<
    HTMLTextAreaElement
  >();

  constructor(props: CodeMirrorEditorProps) {
    super(props);
    this.hint = this.hint.bind(this);
    (this.hint as any).async = true;
    this.tips = this.tips.bind(this);
    this.deleteTip = this.deleteTip.bind(this);
    this.debounceNextCompletionRequest = true;
    this.state = { isFocused: true, tipElement: null };

    this.fullOptions = this.fullOptions.bind(this);
    this.cleanMode = this.cleanMode.bind(this);

    // Bind our events to codemirror
    const extraKeys = {
      "Cmd-.": this.tips,
      "Cmd-/": "toggleComment",
      "Ctrl-.": this.tips,
      "Ctrl-/": "toggleComment",
      "Ctrl-Space": (editor: Editor) => {
        this.debounceNextCompletionRequest = false;
        return editor.execCommand("autocomplete");
      },
      Down: this.goLineDownOrEmit,
      "Shift-Tab": (editor: Editor) => editor.execCommand("indentLess"),
      Tab: this.executeTab,
      Up: this.goLineUpOrEmit
    };

    const hintOptions = {
      // In automatic autocomplete mode we don't want override
      completeSingle: false,
      extraKeys: {
        Right: pick
      },
      hint: this.hint
    };

    this.defaultOptions = Object.assign({
      extraKeys,
      hintOptions,
      // This sets the class on the codemirror <div> that gets created to
      // cm-s-composition
      theme: "composition",
      lineWrapping: true
    });
  }

  fullOptions(defaults: FullEditorConfiguration = {}) {
    // Only pass to codemirror the options we support
    return Object.keys(this.props)
      .filter(isConfigurable)
      .reduce((obj, key) => {
        obj[key] = this.props[key];
        return obj;
      }, defaults);
  }

  cleanMode(): string | object {
    if (!this.props.mode) {
      return "text/plain";
    }
    if (typeof this.props.mode === "string") {
      return this.props.mode;
    }
{'fraction_non_alphanumeric': 0.11885880958891623, 'fraction_numerical': 0.00747425029623553, 'mean_word_length': 3.504105090311987, 'pattern_counts': {'":': 12, '<': 21, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, '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': '4985406', 'n_tokens_mistral': 3547, 'n_tokens_neox': 3391, 'n_words': 881}