-
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
Stabilize buffer API #2075
Comments
@Tyriar Made some progress in that field in #2369 together with @JavaCS3, see this comment #2369 (comment) and my changes here: master...jerch:serialize_with_private#diff-3cf93a85b230e197db3cdf45b888018f I am sure you not gonna like the changes much, basically it seems we should still avoid getters. |
@jerch this blocks the v4 release as we should stabilize such that we don't run into issues with the search addon having broken API in a later v4 release. How about instead converting all the getters into a function we preemptively fetch all the line's values and expose them as regular properties. It's readonly on the API and doesn't matter much if people try to workaround it anyway. So instead of this: return new BufferCellApiView(this._line, x);
...
class BufferCellApiView implements IBufferCellApi {
constructor(private _line: IBufferLine, private _x: number) {}
public get char(): string { return this._line.getString(this._x); }
public get width(): number { return this._line.getWidth(this._x); }
} We do this instead: return {
char: this._line.getString(x),
width: this._line.getWidth(x)
} Or this: return new BufferCellApiView(this._line, x);
...
class BufferCellApiView implements IBufferCellApi {
public char: string;
public width: number;
constructor(line: IBufferLine, x: number) {
this.char = line.getString(x);
this.width = line.getWidth(x);
}
} Reasons I don't think we should do all functions:
/cc @JavaCS3 |
You want to accept an API slowdown of 4 - 10 times for the sake of writing A major version jump is the only right place to address things like that, if we miss it now its not for a better xterm.js because ppl will jump on that API and we prolly cannot undo it later on easily. You already argue in that direction
Do you think this will get any better if done later? |
AFAICT this would be the first inconsistency for properties being exposed as a function (except for maybe set/getOption but they have been around forever), it would be a shame if we stopped following the standard now and made it inconsistent as I've been told from several people about how xterm.js is easy to use. I looked over #2369 again and thought about it, how about an optional argument that needs to be passed to calculate the flags/colors? interface IBufferLine {
/**
* Gets a cell from the line, or undefined if the line index does not exist.
*
* Note that the result of this function should be used immediately after
* calling as when the terminal updates it could lead to unexpected
* behavior.
*
* @param x The character index to get.
* @param includeAttributes Optional boolean that indicates whether to
* calculate and include attribute data (font style, colors) in the returned
* object.
* @param cell Optional cell object to recycle as the result of this call.
* This can boost performance by reducing browser garbage collection time when
* iterating over the entire buffer.
*/
getCell(x: number, includeAttributes?: boolean, cell?: IBufferCell): IBufferCell | undefined;
}
interface IBufferCell {
/**
* The character within the cell.
*/
readonly char: string;
/**
* The width of the character. Some examples:
*
* - This is `1` for most cells.
* - This is `2` for wide character like CJK glyphs.
* - This is `0` for cells immediately following cells with a width of `2`.
*/
readonly width: number;
/**
* Text attribute flags like bold, underline etc.
*
* Note that this should only be used when `includeAttributes` is passed to
* `getCell`. Never store this object as it may get recycled by the library.
*/
readonly flags: IBufferCellFlags;
/**
* Foreground color.
*
* Note that this should only be used when `includeAttributes` is passed to
* `getCell`. Never store this object as it may get recycled by the library.
*/
readonly fg: IBufferCellColor;
/**
* Background color.
*
* Note that this should only be used when `includeAttributes` is passed to
* `getCell`. Never store this object as it may get recycled by the library.
*/
readonly bg: IBufferCellColor;
} So implementation would do this:
This would allow us to keep the naming scheme as well as probably boost performance for cases like #2369 where we need all cell properties since we're dealing with simple object references/primitives rather than in-between functions. |
@Tyriar This one looks better since class BufferCellApiView implements IBufferCellApi {
public char: string;
public width: number;
constructor(line: IBufferLine, x: number) {
this.char = line.getString(x);
this.width = line.getWidth(x);
}
} |
@Tyriar How would the implementation look for the (yet to come) attributes with this? I played with a getter based variant in my playground branch: https://github.com/jerch/xterm.js/blob/09de17dbdb682ed3dbfac7dc3e504f1c28d89055/src/public/Terminal.ts#L238-L281 This variant tries to omit the recreation of subobjects for flags, fg and bg by closuring the Also what @JavaCS3 points to might be a source of inconsistency - we have to decide whether a cell in the API should have stable content once requested (created/realized in memory) or always derive from Yepp, sticking with the optional Edit: Maybe we should not try to to get an attribute API with v4 yet. To me this still seems to be to unreliable to be spec'ed out. How about just leaving it as it is for now (with experimental tag to indicate possible changes in the future)? |
No, the idea was that it would work something like this: const errorProp = () => throw new Error('You must call getCell with includeAttributes to use this');
// Pretty sure this doesn't compile but it gets the idea across
const invalidColor = { get colorMode(): errorProp, get color(): errorProp, get rgb(): errorProp};
const invalidFlags = ...;
...
constructor(x: number, includeAttributes?: boolean, cell?: IBufferCell) {
cell = cell || ...;
if (includeAttributes) {
// Calculate and set attributes because the user wants them
} else {
cell.fg = invalidColor;
cell.bg = invalidColor;
cell.flags = invalidFlags;
}
} Any call to
Sounds better than a hasty decision 👍 |
It looks like still have trouble finalizing API design. |
@jerch thoughts on |
I don't like APIs using flags to change it's behavior except something like |
Yeah I am not a big fan of this idea either, its like hiding 2 different APIs behind this flag (one with and one without attributes). And it does not really help with the question how to represent the attributes in the end. Maybe we should stick with something like my interim proposal with sub-getters (this one), thus things dont add runtime if not requested (no additional costs if attributes are not needed), still the values can be lazy eval'ed (they are kinda at stand by). Only thing that bugs me with this version is the deopt, that happens with 2+ run, but maybe we should ignore that for now and leave it to the JS engine devs to fix it in their JIT opt routines. Beside that deopt issue this version is still reasonable fast. |
@jerch What do u mean "deopt"? Sorry, I don't know this word. |
@JavaCS3 I mean deoptimization with that. |
Had a chat with @mofux who needs this API to be high performance. I think everyone is against me on the facade approach that adds extra protection to the API and makes all the calls consistent due to performance implications. I think this is similar to what was originally exposed in @JavaCS3's PR but what about this: interface IBufferLine {
getCell(x: number, cell?: IBufferCell): IBufferCell | undefined;
}
interface IBufferCell {
getWidth(): number;
getChars(): string;
getCode(): number;
isInverse(): number;
isBold(): number;
isUnderline(): number;
isBlink(): number;
isInvisible(): number;
isItalic(): number;
isDim(): number;
getFgColorMode(): number;
getBgColorMode(): number;
isFgRGB(): boolean;
isBgRGB(): boolean;
isFgPalette(): boolean;
isBgPalette(): boolean;
isFgDefault(): boolean;
isBgDefault(): boolean;
getFgColor(): number;
getBgColor(): number;
} |
@Tyriar Yeah this is most likely the fastest (not tested, but its kinda is a slim 1:1 abstraction of the internal structures, thus almost no API translation needed). @ all |
@jerch I agree that the huge amount of color functions could probably be shrunk down to a more simple to use color interface. In fact, the current interfaces are inappropriate to simply get the final color a cell would have. There is quite a lot of (duplicated) logic going on in our renderers that extract the correct color by considering the xterm.js/src/browser/renderer/dom/DomRendererRowFactory.ts Lines 100 to 126 in 5cb0bce
IMO the I'd also like the |
It's actually just a subset of interfaces from AttributeData and CellData (most of the "getters").
While we could potentially expose it like this in the API, just to clarify we can't do it internally as the buffer shouldn't know about the theme. Trying to generalize this internally would likely be overly complicated. Also for buffer serialization we definitely still need to expose the palette type. |
I think that would be good to have. Otherwise we would probably have to expose certain enums and functions that would be required to figure out the final color
That's a very good point - I haven't considered that |
I second that, imho theme related stuff should not be part of a "buffer API". If we want that at public API level some render related sub API might be better for this kind of data. |
I added interface IBufferLine {
getCell(x: number, cell?: IBufferCell): IBufferCell | undefined;
} |
@jerch You're right, it's out of scope for a |
Is this the final API? |
@JavaCS3 I think we've all decided on exposing the raw @mofux @jerch any additional feedback on the API? Should we have helpers that convert the colors into a more useful object like |
Im good with this, imho its the fastest/thinnest API we can get atm. |
Introduced in #2074, we should remove experimental after we know confirm no changes are needed for the search addon to function.
The text was updated successfully, but these errors were encountered: