<commit_before>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

import { CodeEditor } from '@jupyterlab/codeeditor';

import { CodeMirrorEditor } from '@jupyterlab/codemirror';

import { ActivityMonitor } from '@jupyterlab/coreutils';

import { IObservableString } from '@jupyterlab/observables';

import { IDisposable } from '@phosphor/disposable';

import { Signal } from '@phosphor/signaling';

import { Editor } from 'codemirror';

import { Breakpoints } from '../breakpoints';

import { Callstack } from '../callstack';

import { Debugger } from '../debugger';

import { IDebugger } from '../tokens';

const LINE_HIGHLIGHT_CLASS = 'jp-breakpoint-line-highlight';

const EDITOR_CHANGED_TIMEOUT = 1000;

export class EditorHandler implements IDisposable {
  constructor(options: EditorHandler.IOptions) {
    this._id = options.debuggerService.session.client.path;
    this._debuggerService = options.debuggerService;
    this.onModelChanged();
    this._debuggerService.modelChanged.connect(() => this.onModelChanged());
    this._editor = options.editor;

    this._editorMonitor = new ActivityMonitor({
      signal: this._editor.model.value.changed,
      timeout: EDITOR_CHANGED_TIMEOUT
    });

    this._editorMonitor.activityStopped.connect(() => {
      this.sendEditorBreakpoints();
    }, this);

    this.setup();
  }

  isDisposed: boolean;

  private onModelChanged() {
    this._debuggerModel = this._debuggerService.model as Debugger.Model;
    if (!this._debuggerModel) {
      return;
    }
    this.breakpointsModel = this._debuggerModel.breakpointsModel;

    this._debuggerModel.callstackModel.currentFrameChanged.connect(() => {
      EditorHandler.clearHighlight(this._editor);
    });

    this.breakpointsModel.changed.connect(async () => {
      if (!this._editor || this._editor.isDisposed) {
        return;
      }
      this.addBreakpointsToEditor();
    });

    this.breakpointsModel.restored.connect(async () => {
      if (!this._editor || this._editor.isDisposed) {
        return;
      }
      this.addBreakpointsToEditor();
    });
  }

  dispose(): void {
    if (this.isDisposed) {
      return;
    }
    this._editorMonitor.dispose();
    this.removeGutterClick();
    EditorHandler.clearHighlight(this._editor);
    // EditorHandler.clearGutter(this._editor);
    Signal.clearData(this);
    this.isDisposed = true;
  }

  protected sendEditorBreakpoints() {
    if (this._editor.isDisposed) {
      return;
    }

    const breakpoints = this.getBreakpointsFromEditor().map(lineInfo => {
      return Private.createBreakpoint(
        this._debuggerService.session.client.name,
        this.getEditorId(),
        lineInfo.line + 1
      );
    });

    void this._debuggerService.updateBreakpoints(
      this._editor.model.value.text,
      breakpoints
    );
  }

  protected setup() {
    if (!this._editor || this._editor.isDisposed) {
      return;
    }

    this.addBreakpointsToEditor();

    const editor = this._editor as CodeMirrorEditor;
    editor.setOption('lineNumbers', true);
    editor.editor.setOption('gutters', [
      'CodeMirror-linenumbers',
      'breakpoints'
    ]);

    editor.editor.on('gutterClick', this.onGutterClick);
  }

  protected removeGutterClick() {
    if (!this._editor || this._editor.isDisposed) {
      return;
    }
    const editor = this._editor as CodeMirrorEditor;
    editor.editor.off('gutterClick', this.onGutterClick);
  }

  protected getEditorId(): string {
    return this._editor.uuid;
  }

  protected onGutterClick = (editor: Editor, lineNumber: number) => {
    const info = editor.lineInfo(lineNumber);

    if (!info || this._id !== this._debuggerService.session.client.path) {
      return;
    }

    editor.focus();
    EditorHandler.clearGutter(this._editor);

    const isRemoveGutter = !!info.gutterMarkers;
    let breakpoints: IDebugger.IBreakpoint[] = this.getBreakpoints();
    if (isRemoveGutter) {
      breakpoints = breakpoints.filter(ele => ele.line !== info.line + 1);
    } else {
      breakpoints.push(
        Private.createBreakpoint(
          this._debuggerService.session.client.name,
          this.getEditorId(),
          info.line + 1
        )
      );
    }

    void this._debuggerService.updateBreakpoints(
      this._editor.model.value.text,
      breakpoints
    );
  };

  private addBreakpointsToEditor() {
    const editor = this._editor as CodeMirrorEditor;
    const breakpoints = this.getBreakpoints();
    if (
      breakpoints.length === 0 &&
      this._id === this._debuggerService.session.client.path
    ) {
      EditorHandler.clearGutter(editor);
    } else {
      breakpoints.forEach(breakpoint => {
        editor.editor.setGutterMarker(
          breakpoint.line - 1,
          'breakpoints',
          Private.createMarkerNode()
        );
      });
    }
  }

  private getBreakpointsFromEditor(): ILineInfo[] {
    const editor = this._editor as CodeMirrorEditor;
    let lines = [];
    for (let i = 0; i < editor.doc.lineCount(); i++) {
      const info = editor.editor.lineInfo(i);<commit_msg>Breakpoints can be removed across cells and editors<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

import { CodeEditor } from '@jupyterlab/codeeditor';

import { CodeMirrorEditor } from '@jupyterlab/codemirror';

import { ActivityMonitor } from '@jupyterlab/coreutils';

import { IObservableString } from '@jupyterlab/observables';

import { IDisposable } from '@phosphor/disposable';

import { Signal } from '@phosphor/signaling';

import { Editor } from 'codemirror';

import { Breakpoints } from '../breakpoints';

import { Callstack } from '../callstack';

import { Debugger } from '../debugger';

import { IDebugger } from '../tokens';

const LINE_HIGHLIGHT_CLASS = 'jp-breakpoint-line-highlight';

const EDITOR_CHANGED_TIMEOUT = 1000;

export class EditorHandler implements IDisposable {
  constructor(options: EditorHandler.IOptions) {
    this._id = options.debuggerService.session.client.path;
    this._debuggerService = options.debuggerService;
    this.onModelChanged();
    this._debuggerService.modelChanged.connect(() => this.onModelChanged());
    this._editor = options.editor;

    this._editorMonitor = new ActivityMonitor({
      signal: this._editor.model.value.changed,
      timeout: EDITOR_CHANGED_TIMEOUT
    });

    this._editorMonitor.activityStopped.connect(() => {
      this.sendEditorBreakpoints();
    }, this);

    this.setup();
  }

  isDisposed: boolean;

  private onModelChanged() {
    this._debuggerModel = this._debuggerService.model as Debugger.Model;
    if (!this._debuggerModel) {
      return;
    }
    this.breakpointsModel = this._debuggerModel.breakpointsModel;

    this._debuggerModel.callstackModel.currentFrameChanged.connect(() => {
      EditorHandler.clearHighlight(this._editor);
    });

    this.breakpointsModel.changed.connect(async () => {
      if (!this._editor || this._editor.isDisposed) {
        return;
      }
      this.addBreakpointsToEditor();
    });

    this.breakpointsModel.restored.connect(async () => {
      if (!this._editor || this._editor.isDisposed) {
        return;
      }
      this.addBreakpointsToEditor();
    });
  }

  dispose(): void {
    if (this.isDisposed) {
      return;
    }
    this._editorMonitor.dispose();
    this.removeGutterClick();
    EditorHandler.clearHighlight(this._editor);
    // EditorHandler.clearGutter(this._editor);
    Signal.clearData(this);
    this.isDisposed = true;
  }

  protected sendEditorBreakpoints() {
    if (this._editor.isDisposed) {
      return;
    }

    const breakpoints = this.getBreakpointsFromEditor().map(lineInfo => {
      return Private.createBreakpoint(
        this._debuggerService.session.client.name,
        this.getEditorId(),
        lineInfo.line + 1
      );
    });

    void this._debuggerService.updateBreakpoints(
      this._editor.model.value.text,
      breakpoints
    );
  }

  protected setup() {
    if (!this._editor || this._editor.isDisposed) {
      return;
    }

    this.addBreakpointsToEditor();

    const editor = this._editor as CodeMirrorEditor;
    editor.setOption('lineNumbers', true);
    editor.editor.setOption('gutters', [
      'CodeMirror-linenumbers',
      'breakpoints'
    ]);

    editor.editor.on('gutterClick', this.onGutterClick);
  }

  protected removeGutterClick() {
    if (!this._editor || this._editor.isDisposed) {
      return;
    }
    const editor = this._editor as CodeMirrorEditor;
    editor.editor.off('gutterClick', this.onGutterClick);
  }

  protected getEditorId(): string {
    return this._editor.uuid;
  }

  protected onGutterClick = (editor: Editor, lineNumber: number) => {
    const info = editor.lineInfo(lineNumber);

    if (!info || this._id !== this._debuggerService.session.client.path) {
      return;
    }

    editor.focus();
    EditorHandler.clearGutter(this._editor);

    const isRemoveGutter = !!info.gutterMarkers;
    let breakpoints: IDebugger.IBreakpoint[] = this.getBreakpoints();
    if (isRemoveGutter) {
      breakpoints = breakpoints.filter(ele => ele.line !== info.line + 1);
    } else {
      breakpoints.push(
        Private.createBreakpoint(
          this._debuggerService.session.client.name,
          this.getEditorId(),
          info.line + 1
        )
      );
    }

    void this._debuggerService.updateBreakpoints(
      this._editor.model.value.text,
      breakpoints
    );
  };

  private addBreakpointsToEditor() {
    const editor = this._editor as CodeMirrorEditor;
    const breakpoints = this.getBreakpoints();
    if (this._id !== this._debuggerService.session.client.path) {
      return;
    }
    EditorHandler.clearGutter(editor);
    breakpoints.forEach(breakpoint => {
      editor.editor.setGutterMarker(
        breakpoint.line - 1,
        'breakpoints',
        Private.createMarkerNode()
      );
    });
  }

  private getBreakpointsFromEditor(): ILineInfo[] {
    const editor = this._editor as CodeMirrorEditor;
    let lines = [];
    for (let i = 0; i < editor.doc.lineCount(); i++) {
      const info = editor.editor.lineInfo(i);
{'fraction_non_alphanumeric': 0.12400900460017618, 'fraction_numerical': 0.0018596456885582853, 'mean_word_length': 3.069295101553166, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3063275', 'n_tokens_mistral': 3127, 'n_tokens_neox': 3022, 'n_words': 544}