RivasCVA / react-native-code-editor

A code editor with syntax highlighting built for React Native applications.
MIT License
53 stars 18 forks source link

Cursor jumps to the start when typing code #13

Open HarunKaranja opened 2 years ago

HarunKaranja commented 2 years ago

https://user-images.githubusercontent.com/98604306/176841761-af583e08-9508-4780-b3c2-0fb53c736305.mp4

How do I solve this

The code

<CodeEditor
  style={Styles.codeEditorStyles}
  language="html"
  syntaxStyle={CodeEditorSyntaxStyles.atomOneDark}
  showLineNumbers
  initialValue={ htmlCode }
/>

Styles

codeEditorStyles: {
  fontFamily: 'Muli',
  fontSize: 20,
  inputLineHeight: 28,
  highlighterLineHeight: 28,
}
RivasCVA commented 2 years ago

Hello! Thanks for pointing that out. This doesn't seem to be an issue with iOS, and I do not have the necessary tools to test it out on Android.

Help would be appreciated to get this resolved. All of the cursor movements are controlled in the "CodeEditor.tsx" file. There is a method moveCursor(...) that is responsible for moving the cursor around (see here). I would suggest tracing all of the places that use the method and seeing where the issue is occurring.

DeveloperInside commented 2 years ago

Hi, I just solved this problem with removing some lines of CodeEditor.tsx file which is in the TextInput component. I commented below which lines removed. It's not a real solve but works for me.

Accually, you need to edit moveCursor function. If I can find some time, I'll try to solve at core.

<View style={{ width, height, marginTop, marginBottom }}>
            <SyntaxHighlighter
                language={language}
                addedStyle={addedStyle}
                syntaxStyle={syntaxStyle}
                scrollEnabled={false}
                showLineNumbers={showLineNumbers}
                ref={highlighterRef}
            >
                {value}
            </SyntaxHighlighter>
            <TextInput
                style={[
                    styles.input,
                    {
                        lineHeight: inputLineHeight,
                        color: inputColor,
                        fontFamily: fontFamily,
                        fontSize: fontSize,
                        padding,
                        paddingTop: padding,
                        paddingLeft: lineNumbersPadding,
                    },
                ]}
                value={value}
                onChangeText={handleChangeText}
                onScroll={handleScroll}
                onKeyPress={handleKeyPress}
                onSelectionChange={handleSelectionChange}
                autoCapitalize="none"
                autoComplete="off"
                autoCorrect={false}
                autoFocus={autoFocus}
                // keyboardType="ascii-capable"
                editable={!readOnly}
                // ref={inputRef}
                multiline
            />
        </View>

removed lines

// keyboardType="ascii-capable"
// ref={inputRef}
RivasCVA commented 2 years ago

Interesting... I'm certain it's fixed because moveCursor(...) requires the inputRef in order to move the cursor. It would be nice to have a solution that doesn't require removing ref={inputRef}.

Ritikkk-09 commented 1 year ago

@RivasCVA Please update this library and fix this bug

Kim1254 commented 1 year ago

It seems to be a bug of setNativeProps. Using the method makes the selection value a fixed value.

Writing the selection changes in props solved the problem while removing the inputRef losses the moveCursor feature of the editor.

Check the changes below.

const CodeEditor = (props: PropsWithForwardRef): JSX.Element => {
    // ...

    const [value, setValue] = useState<string>(initialValue);
    const highlighterRef = useRef<ScrollView>(null);
    const inputRef = useRef<TextInput>(null);
    const [inputSelection, setSelection] = useState({start: 0, end: 0});

    // ...

    const moveCursor = (current: number, amount: number) => {
        const newPosition = current + amount;

        setSelection({
            start: newPosition,
            end: newPosition
        });

        return newPosition;
    };

    const addIndentation = (val: string) => {
        let cursorPosition = inputSelection.start - 1;
        // ...
    };

    const addClosingBrace = (val: string, key: string) => {
        let cursorPosition = inputSelection.start;
        return Strings.insertStringAt(val, cursorPosition, Braces.getCloseBrace(key));
    };

    // ...

    const handleSelectionChange = (e: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
        setSelection(e.nativeEvent.selection);
    };

    return (
        <View style={{ width, height, marginTop, marginBottom }}>
            <SyntaxHighlighter
                // ...
            >
                {value}
            </SyntaxHighlighter>
            <TextInput
                // ...
                selection={inputSelection}
            />
        </View>
    );
};
RivasCVA commented 1 year ago

@KyeongMin5307 Can you make the changes a PR?

Kim1254 commented 1 year ago

@KyeongMin5307 Can you make the changes a PR?

Using selection props seems to make critical problems. I met race conditions in cursor selection. I could find that it is a bug of TextInput in react-native: https://github.com/facebook/react-native/issues/29063#issuecomment-658615266

Therefore, I rewrote the code using setNativeProps for every handleChangeText call. Referenced: https://github.com/facebook/react-native/issues/29063#issuecomment-792955726

const handleChangeText = (text: string) => {
    const diff = text.length - value.length;
    setValue(Strings.convertTabsToSpaces(text));

    inputRef.current?.setNativeProps({
        selection: {
            start: inputSelection.current.start + diff,
            end: inputSelection.current.start + diff,
        },
    });
};

Check the details in the PR.

Here's a demo video.

https://github.com/RivasCVA/react-native-code-editor/assets/93812148/4a37e01f-98bd-49c1-86bf-06105fb7f68a