xtermjs / xterm.js

A terminal for the web
https://xtermjs.org/
MIT License
17.72k stars 1.63k forks source link

registerMarker/registerDecoration in overviewRuler does not work if invoked immediately #5214

Open jtbandes opened 4 hours ago

jtbandes commented 4 hours ago

When writing a large batch of lines after clearing the terminal, and using registerMarker+registerDecoration to render colors in the overview ruler, the decorations do not appear if all the lines are written at once. If a small delay is introduced with setTimeout, the decorations work properly.

Additionally, a small decoration sometimes remains at the very top of the overview ruler even after the terminal is cleared.

https://github.com/user-attachments/assets/b52d0096-d6b7-455e-a082-d7ce251e3b69

Details

Steps to reproduce

https://codesandbox.io/p/sandbox/xtermjs-test-forked-yxrd7j

import "./styles.css";
import "@xterm/xterm/css/xterm.css";
import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";

const container = document.getElementById("app");
const button1 = document.getElementById("reload-button-1");
const button2 = document.getElementById("reload-button-2");

const terminal = new Terminal({
  cursorStyle: "bar",
  allowProposedApi: true,
  overviewRulerWidth: 20,
});

const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);

terminal.open(container);

const lines = new Array(100).fill().map((_, i) => {
  return `line ${i}: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua`;
});

async function render(delay) {
  terminal.clear();
  terminal.reset();

  let first = true;
  let i = 0;
  for (const line of lines) {
    if (Math.random() < 0.2) {
      if (delay) {
        await new Promise((resolve) => setTimeout(resolve, 1));
      }
      if (!first) {
        terminal.write("\r\n");
      }
      if (i % 5 === 0) {
        const marker = terminal.registerMarker();
        terminal.registerDecoration({
          marker,
          overviewRulerOptions: {
            color: "#ff0000",
          },
        });
      }
      terminal.write(line);
      first = false;
    }
    i++;
  }
}

button1.addEventListener("click", () => {
  render(false);
});
button2.addEventListener("click", () => {
  render(true);
});

const resizeObserver = new ResizeObserver((_entries) => {
  fitAddon.fit();
});
resizeObserver.observe(container);
jtbandes commented 4 hours ago

It seems like the marker always has line: 0. Maybe this is because the newly written input has not yet been parsed?

Waiting for the new input to be parsed before adding the marker might introduce a lot of delay (I assume I would also have to delay writing any additional lines until the parsing finishes). Is there any way to do this without waiting, or to force processing of the write buffer?