<commit_before>import clone from 'clone';
import equal from 'deep-equal';
import extend from 'extend';
import * as platform from './lib/platform';
import Delta from './lib/delta';
import Emitter from './emitter';
import logger from './lib/logger';
import { Range } from './selection';


let debug = logger('quill:keyboard');


class Keyboard {
  constructor(quill) {
    this.quill = quill;
    this.bindings = {};
    this.addBinding({ key: 'B', metaKey: true }, this.onFormat.bind(this, 'bold'));
    this.addBinding({ key: 'I', metaKey: true }, this.onFormat.bind(this, 'italic'));
    this.addBinding({ key: 'U', metaKey: true }, this.onFormat.bind(this, 'underline'));
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, this.onEnter.bind(this));
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDelete.bind(this, true));
    this.addBinding({ key: Keyboard.keys.DELETE }, this.onDelete.bind(this, false));
    // TODO implement
    // this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDeleteWord.bind(this, true));
    // this.addBinding({ key: Keyboard.keys.DELETE }, this.onDeleteWord.bind(this, false));
    this.addBinding({ key: Keyboard.keys.TAB, shiftKey: null }, this.onTab.bind(this));
    this.quill.root.addEventListener('keydown', (evt) => {
      let which = evt.which || evt.keyCode;
      let handlers = (this.bindings[which] || []).reduce(function(handlers, binding) {
        let [key, handler] = binding;
        if (match(evt, key)) handlers.push(handler);
        return handlers;
      }, []);
      if (handlers.length > 0) {
        let range = this.quill.getSelection();
        handlers.forEach((handler) => {
          handler(range, evt);
        });
        evt.preventDefault();
      }
    });
  }

  addBinding(binding, handler) {
    binding = normalize(binding);
    if (binding == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push([binding, handler]);
  }

  onDelete(backspace, range) {
    if (!range.isCollapsed()) {
      this.quill.deleteAt(range.start, range.end, Quill.sources.USER);
    } else if (!backspace) {
      this.quill.deleteAt(range.start, range.start + 1, Quill.sources.USER);
    } else {
      let [line, offset] = this.quill.scroll.findLine(range.start);
      let formats = this.quill.getFormat(range.start, range.end);
      if (line != null && offset === 0 && formats['list'] != null) {
        if (formats['indent'] != null) {
          this.quill.formatLine(range, 'indent', parseInt(formats['indent']) - 1, Emitter.sources.USER);
        } else {
          this.quill.formatLine(range, 'list', false, Emitter.sources.USER);
        }
      } else {
        this.quill.deleteText(range.start - 1, range.start, Quill.sources.USER);
        range = new Range(Math.max(0, range.start - 1));
      }
    }
    this.quill.setSelection(range.start, Quill.sources.SILENT);
  }

  onEnter(range) {
    let formats = this.quill.getFormat(range);
    let lineFormats = Object.keys(formats, function(lineFormats, format) {
      if (Parchment.query(format, Parchment.Scope.BLOCK)) {
        lineFormats[name] = formats[name];
      }
      return lineFormats;
    }, {});
    let delta = new Delta()
      .retain(range.start)
      .insert('\n', lineFormats)
      .delete(range.start - range.end);
    this.quill.updateContents(delta, Quill.sources.USER);
    this.quill.setSelection(range.start + 1, Quill.sources.SILENT);
    Object.keys(formats).forEach((name) => {
      if (lineFormats[name] == null) {
        this.quill.formatCursor(name, formats[name]);
      }
    });
  }<commit_msg>fix backspace behavior<commit_after>import clone from 'clone';
import equal from 'deep-equal';
import extend from 'extend';
import * as platform from './lib/platform';
import Delta from './lib/delta';
import Emitter from './emitter';
import logger from './lib/logger';
import { Range } from './selection';


let debug = logger('quill:keyboard');


class Keyboard {
  constructor(quill) {
    this.quill = quill;
    this.bindings = {};
    this.addBinding({ key: 'B', metaKey: true }, this.onFormat.bind(this, 'bold'));
    this.addBinding({ key: 'I', metaKey: true }, this.onFormat.bind(this, 'italic'));
    this.addBinding({ key: 'U', metaKey: true }, this.onFormat.bind(this, 'underline'));
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, this.onEnter.bind(this));
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDelete.bind(this, true));
    this.addBinding({ key: Keyboard.keys.DELETE }, this.onDelete.bind(this, false));
    // TODO implement
    // this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDeleteWord.bind(this, true));
    // this.addBinding({ key: Keyboard.keys.DELETE }, this.onDeleteWord.bind(this, false));
    this.addBinding({ key: Keyboard.keys.TAB, shiftKey: null }, this.onTab.bind(this));
    this.quill.root.addEventListener('keydown', (evt) => {
      let which = evt.which || evt.keyCode;
      let handlers = (this.bindings[which] || []).reduce(function(handlers, binding) {
        let [key, handler] = binding;
        if (match(evt, key)) handlers.push(handler);
        return handlers;
      }, []);
      if (handlers.length > 0) {
        let range = this.quill.getSelection();
        handlers.forEach((handler) => {
          handler(range, evt);
        });
        evt.preventDefault();
      }
    });
  }

  addBinding(binding, handler) {
    binding = normalize(binding);
    if (binding == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push([binding, handler]);
  }

  onDelete(backspace, range) {
    if (!range.isCollapsed()) {
      this.quill.deleteAt(range.start, range.end, Quill.sources.USER);
    } else if (!backspace) {
      this.quill.deleteAt(range.start, range.start + 1, Quill.sources.USER);
    } else {
      let [line, offset] = this.quill.scroll.findLine(range.start);
      let formats = this.quill.getFormat(range.start, range.end);
      if (line != null && offset === 0 && (formats['indent'] || formats['list'])) {
        if (formats['indent'] != null) {
          line.format('indent', parseInt(formats['indent']) - 1, Emitter.sources.USER);
        } else {
          line.format('list', false);
        }
      } else {
        this.quill.deleteText(range.start - 1, range.start, Quill.sources.USER);
        range = new Range(Math.max(0, range.start - 1));
      }
    }
    this.quill.setSelection(range.start, Quill.sources.SILENT);
  }

  onEnter(range) {
    let formats = this.quill.getFormat(range);
    let lineFormats = Object.keys(formats, function(lineFormats, format) {
      if (Parchment.query(format, Parchment.Scope.BLOCK)) {
        lineFormats[name] = formats[name];
      }
      return lineFormats;
    }, {});
    let delta = new Delta()
      .retain(range.start)
      .insert('\n', lineFormats)
      .delete(range.start - range.end);
    this.quill.updateContents(delta, Quill.sources.USER);
    this.quill.setSelection(range.start + 1, Quill.sources.SILENT);
    Object.keys(formats).forEach((name) => {
      if (lineFormats[name] == null) {
        this.quill.formatCursor(name, formats[name]);
      }
    });
  }
{'fraction_non_alphanumeric': 0.1670255720053836, 'fraction_numerical': 0.0021534320323014803, 'mean_word_length': 3.105524861878453, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, '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': '3255420', 'n_tokens_mistral': 2398, 'n_tokens_neox': 2375, 'n_words': 475}