message: add transform step to composer to auto-replace emoticons with emoji
diff: @@ -25,6 +25,11 @@ import {autoCompleteCreator} from '../../../editor/parts';
  import {renderModel} from '../../../editor/render';
  import {Room} from 'matrix-js-sdk';
  import TypingStore from "../../../stores/TypingStore";
  +import EMOJIBASE from 'emojibase-data/en/compact.json';
  +import SettingsStore from "../../../settings/SettingsStore";
  +import EMOTICON_REGEX from 'emojibase-regex/emoticon';
  +
  +const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$');
  const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
  @@ -70,6 +75,28 @@ export default class BasicMessageEditor extends React.Component {
  this._modifiedFlag = false;
  }
  + _replaceEmoticon = (caret, inputType, diff) => {
  + const {model} = this.props;
  + const range = model.startRange(caret);
  + // expand range max 8 characters backwards from caret
  + let n = 8;
  + range.expandBackwardsWhile((index, offset) => {
  + const part = model.parts[index];
  + n -= 1;
  + return n >= 0 && (part.type === "plain" || part.type === "pill-candidate");
  + });
  + const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(range.text);
  + if (emoticonMatch) {
  + const query = emoticonMatch[1].toLowerCase().replace("-", "");
  + const data = EMOJIBASE.find(e => e.emoticon ? e.emoticon.toLowerCase() === query : false);
  + if (data) {
  + // + 1 because index is reported without preceding space
  + range.moveStart(emoticonMatch.index + 1);
  + return range.replace([this.props.model.partCreator.plain(data.unicode + " ")]);
  + }
  + }
  + }
  +
  _updateEditorState = (caret, inputType, diff) => {
  renderModel(this._editorRef, this.props.model);
  if (caret) {
  @@ -262,6 +289,9 @@ export default class BasicMessageEditor extends React.Component {
  componentDidMount() {
  const model = this.props.model;
  model.setUpdateCallback(this._updateEditorState);
  + if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
  + model.setTransformCallback(this._replaceEmoticon);
  + }
  const partCreator = model.partCreator;
  // TODO: does this allow us to get rid of EditorStateTransfer?
  // not really, but we could not serialize the parts, and just change the autoCompleter
  