scniro / react-codemirror2

Codemirror integrated components for React
MIT License
1.66k stars 193 forks source link

Unexpected behaviour when using replace and moving cursor #102

Closed mjessome closed 6 years ago

mjessome commented 6 years ago

Hello! I'm very new to react and family, so please bear with me if this is a simple mistake on my part.

I'm trying to implement simple Markdown formatting with the Controlled component and I'm having issues with the cursor ending up in an unexpected place when performing a replace + setCursor operation.

I have an example application below which has a "bold" button that can be pressed. The expectation is that when pressed it will enter "***" and put the cursor in the middle of them. When the cursor is at the end of a line, the cursor ends up at the end of the line after insertion. If the cursor is in the middle of a line, it ends up 2 characters over from the end* of the insertion. If I change my offset to -2, it moves backwards from the start position.

This exact approach seems to work fine with react-codemirror.

App.js

import './App.css';

import React, { Component } from 'react';
import 'codemirror/lib/codemirror.css';
import { Controlled as CodeMirror } from 'react-codemirror2';

class App extends Component {
  constructor(props) {
    super(props);
    this.cm = React.createRef();
    this.state = { value: "starting\n" }
  }
  bold = () => {
    if (!this.cm.current) return;
    const cm = this.cm.current.editor;
    cm.focus();
    const doc = cm.getDoc();
    const c = doc.getCursor();
    doc.replaceRange('****', { line: c.line, ch: c.ch });
    doc.setCursor({ line: c.line, ch: c.ch + 2 });
  }
  render() {
    return (
      <div className="App">
        <CodeMirror
          value={this.state.value}
          onBeforeChange={(editor, data, value, next) => {
            this.setState({value});
          }}
          onChange={(editor, data, value) => {
          }}
          ref={this.cm}
        />
        <button onClick={this.bold}>
          bold
        </button>
      </div>
    );
  }
}

export default App;

Thanks

scniro commented 6 years ago

@mjessome hm, seems as if you're wrestling the instance a bit with the calls to doc.replaceRange and doc.setCursor. Why not manage range and cursor through the props?. This would be the "react way" to do this.

Perhaps modifying the value, finding the cursor mark, then setting all three cursor, selection and value within the callback of your bold button click? I'd be happy to help you further but I suspect that to be a good starting point. By the way I may end up closing this out as it seems to be a troubleshooting issue and not a defect with the lib itself. In which case asking a well-composed question on stackoverflow.com will likely yield fast and quality results