-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Expose important pieces of vs folder on API #5283
Comments
Good idea for common things like |
Looking through the list of addons, these would benefit from exposing event/emitter stuff on the API:
|
@Tyriar Did some further digging around Event/Emitter and Disposable:
Given the list of addons using
|
@jerch if you don't extend the typical thing is to use DisposableStore as a property instead. That's less convenient sometimes but if it'll save a bunch we can use that approach exclusively in addons instead of extends? Example: Did you want to take a stab at a PR for this or want me to, since I introduced the problem? 😉 |
Oh ic, yeah that might be the easier pattern then. Although I found a pattern with runtime /////////////////
// before
// in AddonXY.ts
import { Disposable } from 'vs/base/common/lifecycle';
export class AddonXY extends Disposable { ... }
/////////////////
// with runtime extends
// in core terminal (quickly hacked into core)
private helperCls = class extends Disposable {}
public disposableCtor(): {new(): Disposable} {
return this.helperCls;
}
// in AddonXY.ts
import type { Disposable } from 'vs/base/common/lifecycle';
export function AddonXY(terminal: Terminal, ...) {
const DisposableBase = (terminal as any)._core.disposableCtor() as {new(): Disposable};
const AddonXY = class extends DisposableBase { ... };
return new AddonXY(...);
} It works basically the same as before, but now needs a terminal arg at the fake ctor already to derive the Disposable impl from there. Its not nicely abstracted yet, the
Up to you. I can try to formalize that idea, though I am not 100% sure yet, if this works properly in all circumstances. At the moment this is a full drop-in replacement with only the Edit: To get |
Runtime extends looks pretty confusing to me. The composition approach isn't quite as convenient but it's super clear how it works. We could also have a special // shared/
export class AddonDisposable {
constructor(storeCtor: DisposableStoreCtor) {
// init protected readonly _store
}
dispose() {
this._store.dispose();
}
} // addon
export class WebglAddon extends AddonDisposable {
constructor() {
super(xterm.DisposableStore);
}
} That way only the class in shared/ will be pulled into the addon bundle |
Yepp, +1 to composition. It also needs less awkward APi additions. |
@Tyriar Would we need a separate xterm-shared for external addon creators, or will installing the xterm package with extended API be enough in the end to build an external addon? To me it seems quite involved if the full xterm source would be needed just to build a tiny addon xy. |
@jerch I don't think so, it'll be a very small wrapper and they'll only be gaining things by having new stuff exposed on the API. |
It is kinda obvious, that things here might lead to a bigger API change. So its prolly better to discuss a few things to not get on the wrong track. current situation xterm base package:
addons (plugins):
non-monolithic / non-single-API bundling for the rescue? Whats the actual issue? ideas for a solution Current addon invocation and relation/lifecycle relative to xterm:
Beside that there are no further restrictions. The last point is an important aspect of the addon instance's lifecycling: const xy = new AddonXY(customArgs);
/* Now instance xy is alive but not yet attached to a terminal instance.
For most addons this means, it cannot do anything yet
(most addon have no explicit or only tiny stub ctors).
*/
term.loadAddon(xy);
/* Thats the crucial call for the addon registering it to a terminal instance.
`loadAddon` does some bookkeeping of the instance and
takes the ownership for final disposal (in case the whole terminal
got disposed, so ppl dont have to cleanup addon instances themselves)
Finally `loadAddon` will call into `activate`...
*/
--> xy.activate(term);
/* The second or true initializer of the addon is finally called with
the terminal instance as argument. Most addons will postpone their real init code
to that method, as they have no means for any action without a terminal instance.
*/
...
// later on addon disposal happens either by:
xy.dispose() // or
term.dispose() So we basically have a 2-stage construction pattern for addons with the ctor doing its memory object creation thing and But why a 2-stage construction? So maybe I've overlooked something crucial in that 2-stage construction setup, if so plz tell me. The only thing I can imagine is a strong need of independence between stage 1 and stage 2 (between ctor and Proposal: class AddonXY implements ... {
constructor(term: Terminal, ...someOtherNeededArgs: any[]) {
// pre-registering setup goes here (prev code in ctor)
onChange = new term.shared.Emitter<Wotever>();
...
term.loadAddon(this);
// post-registering setup goes here (prev code in activate)
...
}
} This might need some different handling at taking the disposal ownership in the addon manager, but should be doable. The real benefit of such a simplified addon construction is the fact, that we can expose the needed goodies on the terminal instance itself, so the addon ctor can use them normally without getting the Finally I came back to the issue at hand, ewww. It is much much easier to solve with a single step addon construction than it is now with the ctor/activate separation. If we stick to the composition pattern and reduce the addon construction to a single step we should be able to avoid most of the doubled/tripled code issues from addons. @Tyriar Sorry for the wall of text, I admit that I got lost myself a bit in the middle weighing the pros/cons. I still think the big arc was needed to transport the whole picture. |
Coming from #5251 (comment)
Currently using Event, Disposable etc. from the
vs/*
path in addons adds ~50kB on addons. We should investigate, if and how we can save some binary size on addons by exposing crucial parts on the API, so the impls dont have to be copied over for every single addon.The text was updated successfully, but these errors were encountered: