【问题标题】:Single line Ace editor单行 Ace 编辑器
【发布时间】:2015-11-25 17:22:32
【问题描述】:

我正在尝试设置一个只有一行文本的 Ace 编辑器。

这个想法是模仿<input type="text"> 框的行为,但使用语法着色:

目前如果用户在编辑器中按下Enter,它会创建一个新行:

所以我的问题是:

如何将 Ace 设置为仅允许一行,例如标准文本输入框?

以下是我迄今为止尝试过的方法,以及它没有成功的原因。

  • 如果e.lines.length > 1,则在change 上调用editor.undo()

    问题是,change 被触发实际更改应用到增量中之前,所以 undo() 在这里不起作用(或者它与之前的增量有关)

  • 如果Event.which = 13,则取消keypress

    它有点工作,但很脏,它不能处理粘贴多行文本的情况,所以我们还需要处理paste 事件——这会使这个解决方案更加脏。我也非常有信心会考虑更多的边缘情况。

  • 试图在on("change", function(e) { ... })中“清空”e

    例如,在回调函数中说e = {},前提是e 只是对实际对象的引用。一点效果都没有。

  • 试图在 Ace 编辑器中找到一个内置参数来做到这一点

    目前还没有找到这样的参数...

【问题讨论】:

  • e 是一个参考,因此更改它不会影响原始事件。您是否尝试过修改 e 上包含新文本的任何属性?
  • 听起来你应该列出元素上的 Enter 事件并取消它,它并不脏,因为你想停止使用 Enter。在粘贴时,只需从字符串中删除行分隔符,最终用户就会很快掌握。我没有看到比打字/粘贴更多的边缘案例。
  • 加载元素时去掉换行符;按键处理程序似乎是正确的。巧合的是,我正在使用不同的编辑器组件做类似的事情。
  • @DaveNewton 这就是我最终在小提琴中所做的。由于某种原因 stopPropagation 和 preventDefault 不起作用
  • 谢谢大家。看来,如果change 有效地被触发之前 增量插入undo 链中,它仍然被触发之后 更改已进入编辑器 - 所以@vittore 的解决方案就像一个魅力

标签: javascript ace-editor


【解决方案1】:

您可以使用以下代码使编辑器的行为类似于 input type="text"(主要取自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>

【讨论】:

    【解决方案2】:

    由于某种原因,e.preventDefaulte.stopPropagation 在更改事件处理程序中都不起作用。但是你可以做查找替换。

    见小提琴: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()
    

    【讨论】:

    • 在更改事件中进行更改会导致工作人员出现问题。自此答案以来,更改事件结构似乎发生了变化。我将发布适合我的代码作为另一个答案。
    【解决方案3】:

    现有的两个答案都非常有助于我解决这个问题,但我仍然遇到了一些我必须解决的问题(一些显然是由于 api 的变化)。这是对我有用的两个答案的组合。

    注意,例如,当您运行这些 sn-ps 或 jsFiddle 时,CORS 检查将阻止 worker 加载,仅仅因为示例“有效”并不意味着它会全部集成到您的项目中即可工作。

    另请注意...这是使用钩子的反应代码,但应该应用基本代码。

    它还使用来自Set width of ace editor instance according to the length of characters in it的调整大小代码

    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" }} />;
    
    

    【讨论】:

      【解决方案4】:

      尝试在setOptions 属性中将maxLines 设置为1。 我使用react-ace 版本9.2.0。 我将其用作可选/可配置行为,并在包装​​器组件上有一个公开的属性: maxLines={this.props.singleLine ? 1 : undefined}

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多