Skip to content

View jumps when near end of document, with line wrapping enabled & custom decorations #1552

@tuxmark5

Description

@tuxmark5

CodeMirror view jumps when programatically inserting text when the following conditions are met:

  1. Document is long enough for vertical scrollbar to appear
  2. lineWrapping is enabled
  3. Document is scrolled to the very bottom / last line of the document is visible
  4. Text is programatically inserted using replaceSelection or a similar method
  5. The inserted text contains fragments that get replaced with replace decorations

Code to reproduce this:

import { basicSetup, EditorView } from "codemirror"
import { drawSelection, highlightActiveLine, keymap, lineNumbers, MatchDecorator, WidgetType } from "@codemirror/view"
import { Decoration, ViewPlugin, ViewUpdate } from "@codemirror/view"
import { standardKeymap } from "@codemirror/commands"

const placeholderMatcher = new MatchDecorator({
    regexp: /\[\[(\w+)\]\]/g,
    
    decoration: match => Decoration.replace({
        widget: new PlaceholderWidget(match[1]),
        //inclusive: true,
    })
})

const placeholders = ViewPlugin.fromClass(class {
    constructor(view) {
        this.placeholders = placeholderMatcher.createDeco(view)
    }
    
    update(update) {
        this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders)
    }
}, {
    decorations: instance => instance.placeholders,
    
    provide: plugin => EditorView.atomicRanges.of(view => {
        return view.plugin(plugin)?.placeholders || Decoration.none
    })
})

class PlaceholderWidget extends WidgetType {
    constructor(repr) {
        super()
        this.repr = repr
    }

    eq(other) {
        return this.repr === other.repr
    }

    toDOM() {
        const dom = document.createElement("span")
        this.updateDOMInner(dom)
        return dom
    }

    ignoreEvent() { return false }

    updateDOM(dom, view) {
        this.updateDOMInner(dom)
        return true
    }

    updateDOMInner(dom) {
        dom.textContent = this.repr
    }
}

const KB_TEST = {
    key: "Ctrl-.",

    run: (view) => {
        const text = '[[boopbooopbooop]]'
        const spec = view.state.replaceSelection(text)
        const tr = view.state.update(spec)
        view.update([tr])
        return true
    },
};

const start = "foo\n".repeat(200)
const middle = "z = [[boopbooopbooop]] + [[boopbooopbooop]] + \n"
const end = "\n".repeat(10)
const doc = [start, middle, end].join('')

new EditorView({
    doc,
    parent: document.body,
    extensions: [
        drawSelection(),
        highlightActiveLine(),
        lineNumbers(),
        keymap.of([KB_TEST, ...standardKeymap]),
        placeholders, 
        EditorView.lineWrapping,
    ],
})

Steps to reproduce the problem using the code provided:

  1. Run the code in CodeMirror sandbox
  2. Scroll to the very bottom so the last line of the document (line 212) is visible.
  3. Go to the end of line 201
  4. Press Ctrl+. to activate KB_TEST keybinding, which will insert '[[boopbooopbooop]]' at the cursor position
  5. The document scrolls up on its own and the last line (212) is no longer visible.

The issue happens both on Firefox and Chromium-based browsers.

Reproduction url

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions