uiwjs / react-codemirror

CodeMirror 6 component for React. @codemirror https://uiwjs.github.io/react-codemirror/
https://uiwjs.github.io/react-codemirror/
MIT License
1.65k stars 132 forks source link

Line number is not updated #221

Closed JMLX42 closed 2 years ago

JMLX42 commented 2 years ago

Hello,

I'm trying to use the lineNumbers extension to add an offset to each line number. It works fine when the component is created/rendered for the first time.

But then if the lineNumberOffset prop is changed, the CodeMirror editor is not updated. Any idea how to fix this?

import { CatalaCell, setCodeValue } from "../file/fileSlice";
import { connect, ConnectedProps } from "react-redux";
import { RootState } from "../../app/store";
import CodeMirror from '@uiw/react-codemirror';
import { lineNumbers } from "@codemirror/gutter";
import { EditorView } from "@codemirror/view";
import { useAppDispatch } from "../../app/hooks";

const mapState = (state: RootState) => ({
  fileContent: state.file.content,
});

const mapDispatch = {
};

const connector = connect(mapState, mapDispatch);

type PropsFromRedux = ConnectedProps<typeof connector>;

type Props = PropsFromRedux & {
  cellIndex: number;
  lineNumberOffset: number;
}

const CodeEditor = (props: Props) => {
  const dispatch = useAppDispatch();
  const cell: CatalaCell = props.fileContent![props.cellIndex];

  let theme = EditorView.theme({
    "&.cm-editor": {
      padding: "10px 0 10px 0",
      fontSize: ".9em",
      fontFamily: "Roboto Mono, sans-serif",
    },
    ".cm-lineNumbers .cm-gutterElement": {
      minWidth: "45px",
      paddingRight: "0px",
      fontSize: "15px",
      lineHeight: "24px",
      marginRight: "18px",
      fontFamily: "Roboto Mono, sans-serif",
    },
    /* Disable CodeMirror's active line highlighting. */
    "& .cm-activeLineGutter, & .cm-activeLine": {
      backgroundColor: "transparent !important",
    },
    /* Disable CodeMirror's focused editor outline. */
    "&.cm-editor.cm-focused": {
      outline: "none",
    },
  }, {dark: true});

  const extensions = [
    theme,
    lineNumbers({
      formatNumber: (n, s) => {
        return (n + props.lineNumberOffset).toString();
      }
    }),
  ];

  return (
    <div style={{ marginTop: 10, marginBottom: 10 }}>
      <CodeMirror
        basicSetup={false}
        value={cell?.code?.code}
        onChange={(value, viewUpdate) => {
          dispatch(setCodeValue([props.cellIndex, value]));
        }}
        extensions={extensions}
        theme="dark"
      />
    </div>
  );
}

export default connector(CodeEditor);
JMLX42 commented 2 years ago

I just asked on the CodeMirror forum: https://discuss.codemirror.net/t/codemirror-6-offset-line-numbers/2675/4

jaywcjlove commented 2 years ago

@JMLX42 Can you provide examples through codesandbox?

https://codesandbox.io/embed/react-codemirror-example-codemirror-6-slvju?fontsize=14&hidenavigation=1&theme=dark

JMLX42 commented 2 years ago

@jaywcjlove here you go:

https://codesandbox.io/s/react-codemirror-example-codemirror-6-forked-k0uyu?file=/src/App.js

Update: just fixed the codesandbox link

JMLX42 commented 2 years ago

@jaywcjlove I have tried reconfiguring the extension but it doesn't help:

function CodeEditor(props) {
  const gutter = new Compartment();
  const lines = lineNumbers({
    formatNumber: (n, s) => {
      return (n + props.lineNumberOffset).toString();
    }
  });
  const extensions = [gutter.of(lines)];
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current.view) {
      console.log("reconfigure");
      ref.current.view.dispatch({
        effects: gutter.reconfigure(lines)
      });
    }
  });

  return (
    <CodeMirror
      ref={ref}
      value="some_code();"
      height="200px"
      extensions={extensions}
    />
  );
}

Sadly, the editor is not refreshed (and the line number are not updated) until it is focused the 1st time. Then if new lines are added, focusing the editor doesn’t update the lines anymore. But adding content to the editor updates the line numbers.

Is there a way to force an editor update after reconfiguring an extension without focusing it/typing anything in it?

JMLX42 commented 2 years ago

@jaywcjlove it might actually be a CodeMirror bug: https://discuss.codemirror.net/t/codemirror-6-offset-line-numbers/2675/10

JMLX42 commented 2 years ago

It was a bug and it is now fixed in @codemirror/gutter version 0.19.5. The following code now works as expected:

import { CatalaCell, setCodeValue } from "../file/fileSlice";
import { connect, ConnectedProps } from "react-redux";
import { RootState } from "../../app/store";
import { lineNumbers } from "@codemirror/gutter";
import { useAppDispatch } from "../../app/hooks";
import CodeMirror from "@uiw/react-codemirror";
import { EditorState } from "@codemirror/state";

const mapState = (state: RootState) => ({
  fileContent: state.file.content,
});

const mapDispatch = {
};

const connector = connect(mapState, mapDispatch);

type PropsFromRedux = ConnectedProps<typeof connector>;

type Props = PropsFromRedux & {
  cellIndex: number;
  lineNumberOffset: number;
}

const CodeEditor = (props: Props) => {
  const dispatch = useAppDispatch();
  const cell: CatalaCell = props.fileContent![props.cellIndex];

  const extensions = [
    lineNumbers({ formatNumber: (n: number, s: EditorState) =>
      (n + props.lineNumberOffset).toString()
    }),
  ];

  return (
    <div style={{ marginTop: 10, marginBottom: 10 }}>
      <CodeMirror
        value={cell?.code?.code}
        onChange={(value, viewUpdate) => {
          dispatch(setCodeValue([props.cellIndex, value]));
        }}
        extensions={extensions}
        theme="dark"
      />
    </div>
  );
}

export default connector(CodeEditor);

And here is the updated CodeSandbox:

https://codesandbox.io/s/react-codemirror-example-codemirror-6-forked-2s82t?file=/src/App.js

jaywcjlove commented 2 years ago

https://codesandbox.io/s/react-codemirror-example-codemirror-6-forked-2s82t?file=/src/App.js

import CodeMirror from "@uiw/react-codemirror";
import { lineNumbers } from "@codemirror/gutter";
import React from "react";

export function TextEditor(props) {
  const [value, setValue] = React.useState(props.defaultValue);

  const onChange = (e) => {
    setValue(e.target.value);
    props.onChange(e);
  };

  return (
    <textarea
      style={{ width: 400, height: 200 }}
      onChange={onChange}
      value={value}
    ></textarea>
  );
}

function CodeEditor(props) {
  const extensions = [
    lineNumbers({
      formatNumber: (n, s) => {
        return (n + props.lineNumberOffset).toString();
      }
    })
  ];

  return (
    <CodeMirror value="some_code();" height="200px" extensions={extensions} />
  );
}

export default function App() {
  const defaultText = "some text\nadd lines to test";
  const countLines = (s) => s.match(/\n/gm)?.length ?? 0;
  const [lineNumberOffset, setLineNumberOffset] = React.useState(
    countLines(defaultText) + 1
  );

  const onChange = (e) => {
    setLineNumberOffset(countLines(e.target.value) + 1);
  };

  return (
    <div>
      <TextEditor onChange={onChange} defaultValue={defaultText} />
      <div>Line number offset = {lineNumberOffset}</div>
      <CodeEditor lineNumberOffset={lineNumberOffset} />
    </div>
  );
}
jaywcjlove commented 2 years ago

https://github.com/uiwjs/react-codemirror/issues/316#issuecomment-1159631781

xiefengnian commented 10 months ago

https://codesandbox.io/s/react-codemirror-example-codemirror-6-forked-2s82t?file=/src/App.js

import CodeMirror from "@uiw/react-codemirror";
import { lineNumbers } from "@codemirror/gutter";
import React from "react";

export function TextEditor(props) {
  const [value, setValue] = React.useState(props.defaultValue);

  const onChange = (e) => {
    setValue(e.target.value);
    props.onChange(e);
  };

  return (
    <textarea
      style={{ width: 400, height: 200 }}
      onChange={onChange}
      value={value}
    ></textarea>
  );
}

function CodeEditor(props) {
  const extensions = [
    lineNumbers({
      formatNumber: (n, s) => {
        return (n + props.lineNumberOffset).toString();
      }
    })
  ];

  return (
    <CodeMirror value="some_code();" height="200px" extensions={extensions} />
  );
}

export default function App() {
  const defaultText = "some text\nadd lines to test";
  const countLines = (s) => s.match(/\n/gm)?.length ?? 0;
  const [lineNumberOffset, setLineNumberOffset] = React.useState(
    countLines(defaultText) + 1
  );

  const onChange = (e) => {
    setLineNumberOffset(countLines(e.target.value) + 1);
  };

  return (
    <div>
      <TextEditor onChange={onChange} defaultValue={defaultText} />
      <div>Line number offset = {lineNumberOffset}</div>
      <CodeEditor lineNumberOffset={lineNumberOffset} />
    </div>
  );
}

its not wroking in latest @uiw/react-codemirror (4.21.20).

image

how can i set the linenum offet of react-codemiirror?

xiefengnian commented 10 months ago

https://codesandbox.io/s/react-codemirror-example-codemirror-6-forked-2s82t?file=/src/App.js

import CodeMirror from "@uiw/react-codemirror";
import { lineNumbers } from "@codemirror/gutter";
import React from "react";

export function TextEditor(props) {
  const [value, setValue] = React.useState(props.defaultValue);

  const onChange = (e) => {
    setValue(e.target.value);
    props.onChange(e);
  };

  return (
    <textarea
      style={{ width: 400, height: 200 }}
      onChange={onChange}
      value={value}
    ></textarea>
  );
}

function CodeEditor(props) {
  const extensions = [
    lineNumbers({
      formatNumber: (n, s) => {
        return (n + props.lineNumberOffset).toString();
      }
    })
  ];

  return (
    <CodeMirror value="some_code();" height="200px" extensions={extensions} />
  );
}

export default function App() {
  const defaultText = "some text\nadd lines to test";
  const countLines = (s) => s.match(/\n/gm)?.length ?? 0;
  const [lineNumberOffset, setLineNumberOffset] = React.useState(
    countLines(defaultText) + 1
  );

  const onChange = (e) => {
    setLineNumberOffset(countLines(e.target.value) + 1);
  };

  return (
    <div>
      <TextEditor onChange={onChange} defaultValue={defaultText} />
      <div>Line number offset = {lineNumberOffset}</div>
      <CodeEditor lineNumberOffset={lineNumberOffset} />
    </div>
  );
}

its not wroking in latest @uiw/react-codemirror (4.21.20).

image

how can i set the linenum offet of react-codemiirror?

For those who need it:

import { lineNumbers } from "@codemirror/gutter"; // remove this
import { lineNumbers } from "@codemirror/view"; // use this

make it work.