-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Renderer API #2005
Comments
IMHO we should add another abstraction between the renderer and the buffer in this step. We are still passing the raw terminal buffer to the renderer, which has the following disadvantages:
I think we need some kind of a view model in the middle that communicates with the buffer and provides a more abstract representation of it to the renderer. This view model would also be a good place for plugins to provide cell decorations (e.g. search highlight, link underline). Having a view model would also de-duplicate alot of the logic currently done by every renderer:
The model might produce an array of per-cell paint instructions to the renderer, only including the cells that need a redraw: interface ICellPaint {
/** The 0-based x-index of the cell inside the viewport */
x: number;
/** The 0-based y-index of the cell inside the viewport */
y: number;
/** The character to draw in the cell */
char: string | void;
/**
* The width of the cell, can be either 1 or 2 (for doube-width chars).
* Note: We won't generate ICellPaint for 0-width cells
*/
width: number;
/** Cell styles like Italic, Bold or Underline */
styles: ICellPaintStyle[];
/** The foreground color of the text */
fg: IColor;
/** The background color of the cell */
bg: IColor;
/** A reference to the decorations targeting this cell */
decorations: IDecoration[];
} The interface on IRenderer might then look like this: interface IRenderer extends IDisposable {
// ...
renderCells(paints: ICellPaint[]): void
// ...
} Now, scrolling the viewport up or down would lead to a full redraw with every line scrolled, which is not ideal. We can be smarter by introducing different types of instructions the renderer should execute. I'm currently thinking of two types of instructions: One instruction that causes the renderer to pull or push the viewport up or down by n rows, cutting off the rows that fall out of the viewport, and another one that provides ICellPaint[] instructions. interface IRenderer extends IDisposable {
// ...
// forget about renderCells, render does replace it
// ...
render(instructions: IPaintInstruction[]): void
// ...
} As an example, if we scroll the viewport up one row, the instructions generated might look like this: // the IPaintInstruction[] passed to the renderer
[
// positive offset pushes rows down, negative pull them up
{ type: RenderInstructionType.Shift, offset: 1 },
// only contains cell instructions for row 0, as the others don't need a redraw
{ type: RenderInstructionType.PaintCell, paints: [ICellPaint, ICellPaint, ...] }
] From an implementation point of view, it might look like this (DOM renderer): render(instructions: IPaintInstruction[]) {
// iterate instructions
for (let instruction of instructions) {
// render instruction
switch (instruction.type) {
case RendererInstructionType.Shift: {
// based on the offset, either remove dom rows at the top and add empty ones
// at the bottom, or the other way around
}
case RendererInstructionType.PaintCell: {
for (let paint of instruction.paints) {
// create or update cell spans, set character, apply styles and so on...
const cellSpan = this.cellDomNodes[paint.y][paint.x];
cellSpan.textContent = paint.char;
}
}
}
}
} I think it would also make sense to have an instruction to resize the viewport, and possibly decorations could also get their own instruction type later on. Is this too complex? |
@mofux 👍 yeah something like that sounds good and should resolve my TODO about the buffer, I think we could reuse the cells and only create new objects after a resize. I think we should stick to rendering rows and not get into paint instructions though (yet?), that really complicates things. Will revise tonight 🙂 |
Part of xtermjs#2005 Part of xtermjs#1507
Decided against exposing this so we can move the webgl renderer forward #1790 (comment), decorations are still on the table to allow addons to customize what's shown. |
An API for renderers is necessary if we want to include the WebGL renderer as an optional component. A lot of this issue is about refactoring the renderer code to share a lot of the core functionality of all the renderers to make the actual
IRenderer
API interface slimmer for addons to eventually implement. Here are the sub-goals:ColorManager
to live underTerminal
, passIColorSet
to the rendererCharMeasure
can live only in the DOM renderer code (canvas renderers can useonOptionsChange
)Break renderer dependence on Terminal by passing in relevant objects to handlersRenderer addons will have access to the xterm.js APIAPI proposal
Note that the inline proposal will change as progress is made.
The text was updated successfully, but these errors were encountered: