react-codemirror icon indicating copy to clipboard operation
react-codemirror copied to clipboard

I want to get the cursor position.

Open ilysusu opened this issue 2 years ago • 9 comments

我需要获取光标在编辑器中的位置,做一个文本插入的功能,需要怎么做

ilysusu avatar Nov 09 '23 09:11 ilysusu

I need to get the position of the cursor in the editor to implement a text insertion function. What should I do?

ilysusu avatar Nov 09 '23 09:11 ilysusu

在 CodeMirror v6 中,要向光标位置插入字符串,你可以使用 EditorView.dispatch 方法来发送一个 StateEffect,以修改编辑器的状态。具体步骤如下:

  1. 创建一个 StateEffect,用于表示要插入的字符串。例如,假设你要插入的字符串是 insertText

    import { StateEffect } from "@codemirror/state";
    
    const insertText = StateEffect.appendText("insertText");
    
  2. 使用 ref 获取 codemirror 的 view 对象

  3. 使用 EditorView.dispatch,将该 StateEffect 分发给编辑器视图:

    view.dispatch({
      effects: insertText,
    });
    

@ilysusu

jaywcjlove avatar Nov 09 '23 09:11 jaywcjlove

import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react'; import CodeMirror from "@uiw/react-codemirror"; import { StateEffect } from "@codemirror/state"; import { javascript } from "@codemirror/lang-javascript"; import { EditorView } from "@codemirror/view"

const AutoCodeMirror = forwardRef(({exprVal, setExprVal}, ref) => { const currentNode = useContext(NodesContext) const [options, setOptions] = useState([])

const codeMirrorRef = useRef(null);

useImperativeHandle(ref, () => { codeMirrorRef.current })

// 将 codeMirrorRef 暴露给父组件 useImperativeHandle(ref, () => ({ getCodeMirrorInstance: () => codeMirrorRef.current, })); useEffect(() => { function initOptions() { const currNodeId = parseInt(currentNode.data.nodeId.substring(1), 10); const newOptions = []; for (let i = currNodeId; i >= 1; i--) { const nodeId = N${i.toString().padStart(4, '0')}; newOptions.push({ label: nodeId, type: "variable" }); } console.log(newOptions, 'newOptions') setOptions(newOptions)

}
initOptions()

}, [currentNode]);

const onChange = useCallback((val, viewUpdate) => { // console.log("val:", val); setExprVal(val); // 当前光标位置插入数据 const insertText = StateEffect.appendText("insertText"); const view = codeMirrorRef.current.view; console.log(view,' view') view.dispatch({ effects: insertText, }); }, [setExprVal]);

const myCompletions = (context) => { let word = context.matchBefore(/\w*/) if (word.from === word.to && !context.explicit) return null return { from: word.from, // 建议内容 options: options }; };

return ( <> <CodeMirror ref={codeMirrorRef} style={{border: "1px solid #eee"}} value={exprVal} height="250px" extensions={[ javascript({ jsx: true }), javascript().language.data.of({autocomplete: myCompletions}), EditorView.lineWrapping ]} onChange={onChange} />

</>

); })

export default AutoCodeMirror;

这是我的代码,出现报错 update listener: TypeError: StateEffect.appendText is not a function

ilysusu avatar Nov 10 '23 02:11 ilysusu

如果有错误,清使用下面工具工具之一提供重现的示例

另外我可能给了个错误的示例,我这里有个 编辑器的实现有类似的插入代码

https://github.com/uiwjs/react-markdown-editor/blob/c237b69acdad69c2d1804537ecb797bcdcdc14c9/core/src/commands/bold.tsx#L18-L26

    view.dispatch(
      view.state.changeByRange((range) => ({
        changes: [
          { from: range.from, insert: '**' },
          { from: range.to, insert: '**' },
        ],
        range: EditorSelection.range(range.from + 2, range.to + 2),
      })),
    );

@ilysusu 你可以参考我封装的 react-markdown-editor 组件代码

jaywcjlove avatar Nov 10 '23 03:11 jaywcjlove

https://codesandbox.io/s/react-codemirror-example-codemirror-6-https-github-com-uiwjs-react-codemirror-issues-314-forked-8glmzg?file=/src/App.js 重现的示例 @jaywcjlove

ilysusu avatar Nov 10 '23 07:11 ilysusu

@ilysusu 示例: https://codesandbox.io/embed/https-github-com-uiwjs-react-codemirror-issues-603-wr6ph9?fontsize=14&hidenavigation=1&theme=dark

import React, { useRef, useState } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { StateEffect } from "@codemirror/state";
import { javascript } from "@codemirror/lang-javascript";
import { EditorSelection } from "@codemirror/state";

export default function App() {
  const codeMirrorRef = useRef(null);
  const [val, setVal] = useState("console.log('hello world!');");
  const onChange = React.useCallback((value, viewUpdate) => {
    console.log("value:", value);
  }, []);

  // 从光标位置插入文本
  const handleInsertVal = () => {
    const view = codeMirrorRef.current.view;

    view.dispatch(
      view.state.changeByRange((range) => ({
        changes: [
          { from: range.from, insert: "**" },
          { from: range.to, insert: "**" }
        ],
        range: EditorSelection.range(range.from + 2, range.to + 2)
      }))
    );
  };

  const onRefChange = () => {
    codeMirrorRef.current.view.dispatch({
      changes: { from: 0, to: 12, insert: "InsertText" }
    });
  };

  return (
    <div>
      <button onClick={handleInsertVal}> insertValue </button>
      <button onClick={onRefChange}>Ref Change Value</button>
      <CodeMirror
        value={val}
        ref={codeMirrorRef}
        height="200px"
        theme="dark"
        extensions={[javascript({ jsx: true })]}
        onChange={onChange}
      />
    </div>
  );
}

jaywcjlove avatar Nov 10 '23 11:11 jaywcjlove

In answer to the issue title (for others looking for an answer to "How to get cursor position"

const cursorPosition = editorRef.current?.view?.state?.selection.main.head

NOTE: Use editorRef.current?.view?.state NOT just editorRef.current?.state as the latter seems to always return 0

fsuk avatar Nov 15 '23 17:11 fsuk