Редактор одиночной линии Ace

Я пытаюсь настроить редактор Ace только с одной строкой текста.

Идея состоит в том, чтобы имитировать поведение поля <input type="text">, но с синтаксической раскраской: введите описание изображения здесь

В настоящее время, если пользователь нажимает Enter, находясь в редакторе, он создает новую строку: введите описание изображения здесь

Итак, мой вопрос:

Как настроить Ace, чтобы разрешить только одну строку, например, стандартное поле ввода текста?

Ниже я попытался до сих пор, и причины, по которым это не удалось.

  • Вызов editor.undo() на change, если e.lines.length > 1

    Проблема заключается в том, что change запускается до того, как фактическое изменение применяется в дельтах, поэтому undo() здесь не работает (или относится к предыдущей дельта)

  • Отмена keypress, если Event.which = 13

    Это работает, но очень грязно, и он не обрабатывает случай, когда текст с несколькими строками вставлен, поэтому нам нужно было бы обрабатывать событие paste, что сделало бы это решение еще более грязным. Я также довольно уверен, что будут приняты еще более кратные случаи.

  • Попытка "пустить" e в on("change", function(e) { ... })

    Например, говоря, что e = {} в функции обратного вызова при условии, что e является просто ссылкой на фактический объект. Нет никакого эффекта.

  • Попытка найти встроенный параметр в редакторе Ace для этого

    Не удалось найти такой параметр...

Ответ 1

По какой-то причине ни один e.preventDefault и e.stopPropagation не работает в обработчике событий изменений. Но вы можете найти find-replace.

Смотрите скрипту: http://jsfiddle.net/vittore/3rLfdtxb/

 var editor = ace.edit("editor");
 editor.setTheme("ace/theme/monokai");
 editor.getSession().setMode("ace/mode/javascript");
 editor.setFontSize(30)
 editor.getSession().on('change', function(e) {
    console.log(e)
    if (e.data.text.charCodeAt(0) === 10 && e.data.action == "insertText") {
      console.log('cancel event')
      //e.preventDefault() // doesnt work
      //e.stopPropagation()  // doesnt work
      editor.find(String.fromCharCode(10))
      editor.replaceAll(''); // this work
    }
 })

Вы даже можете удалить инструкцию if из обработчика и заменить разрыв строки при любом изменении, независимо.

Когда вы находите-заменяете в изменении, вы получаете строку от курсора до конца выбранной строки. Чтобы отменить выбор после этого, используйте:

editor.selection.clearSelection()

Ответ 2

вы можете использовать следующий код, чтобы сделать поведение редактора похожим на тип ввода = "текст" (в основном взятый из https://github.com/ajaxorg/ace/blob/v1.2.0/demo/kitchen-sink/layout.js#L103)

var el = document.getElementById("textbox")
var editor = ace.edit(el);
editor.setOptions({
    maxLines: 1, // make it 1 line
    autoScrollEditorIntoView: true,
    highlightActiveLine: false,
    printMargin: false,
    showGutter: false,
    mode: "ace/mode/javascript",
    theme: "ace/theme/tomorrow_night_eighties"
});
// remove newlines in pasted text
editor.on("paste", function(e) {
    e.text = e.text.replace(/[\r\n]+/g, " ");
});
// make mouse position clipping nicer
editor.renderer.screenToTextCoordinates = function(x, y) {
    var pos = this.pixelToScreenCoordinates(x, y);
    return this.session.screenToDocumentPosition(
        Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
        Math.max(pos.column, 0)
    );
};
// disable Enter Shift-Enter keys
editor.commands.bindKey("Enter|Shift-Enter", "null")
#textbox {
    font-size: 30px;
    border:solid 2px gray;
}
body{
   background: #161619;
   padding: 40px 20px
}
<script src="https://ajaxorg.github.io/ace-builds/src/ace.js"></script>


<div id=textbox>var a = 1</div>

Ответ 3

Оба существующих ответа были очень полезны для меня, но я все еще сталкивался с некоторыми проблемами, с которыми мне пришлось столкнуться (некоторые, по-видимому, из-за изменений API). Это комбинация обоих ответов, которая работает для меня.

Обратите внимание,, когда вы запускаете эти фрагменты или, например, jsFiddle, проверка CORS запрещает загрузку работника, поэтому тот факт, что пример "работает", не означает, что он будет работать при интеграции в ваш проект.

Также обратите внимание... это код реакции с использованием хуков, но базовый код должен применяться.

Он также использует код изменения размера из Установить ширину экземпляра редактора ace в соответствии с длиной символов в нем

const editDiv = useRef<HTMLDivElement>(null);

useEffect(() => {
    if (editing) {
      if (!editor && editDiv.current) {
        editDiv.current.textContent = value;
        const ed = ace.edit(editDiv.current);
        ed.setOptions({
          maxLines: 1,
          autoScrollEditorIntoView: true,
          highlightActiveLine: false,
          printMargin: false,
          showGutter: false,
          enableLiveAutocompletion: true,
          enableBasicAutocompletion: true,
          enableSnippets: false,
          mode: "ace/mode/javascript",
          theme: "ace/theme/tomorrow_night_eighties"
        });
        ed.commands.bindKey(
          "Up|Ctrl-P|Down|Ctrl-N|PageUp|PageDown",
          "null"
        );
        ed.commands.addCommand({
          name: "SaveOnEnter",
          bindKey: {
            win: "Enter",
            mac: "Enter",
            sender: "editor|cli"
          },
          exec: () => {
            setValue(ed.getValue());
            // Handle new value;
          }
        });
        ed.on("paste", e => {
          e.text = e.text.replace(/[\r\n]+/g, " ");
        });
        setEditor(ed);

        ed.getSession().on("change", e => {
          if (e.action == "insert" && ed.session.getValue().includes("\n")) {
            setTimeout(() => {
              // doing a replaceAll during a change event causes issues in the
              // worker, so we'll queue up the change
              ed.find(String.fromCharCode(10));
              ed.replaceAll("");
              ed.selection.clearSelection();
            }, 0);
          }
        });
        ed.renderer.on("beforeRender", (e, renderer) => {
          const rSession = renderer.session;
          const text = rSession.getLine(0);
          const charCount = rSession.$getStringScreenWidth(text)[0];
          const width =
            Math.max(charCount, 2) * renderer.characterWidth + // text size
            2 * renderer.$padding + // padding
            2 + // little extra for the cursor
            0; // add border width if needed

          renderer.container.style.width = width + "px";
          renderer.onResize(false, 0, width, renderer.$size.height);
        });
        ed.setWrapBehavioursEnabled(false);
        ed.session.setUseWrapMode(false);
        ed.focus();
      }
    } else {
      if (editor) {
        editor.renderer.on("beforeRender", () => {});
        editor.destroy();
        setEditor(undefined);
      }
    }
  }, [editing]);

  return <div ref={editDiv} style={{ width: "1em", height: "1.2em" }} />;