Skip to content
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 key helpers on the API for addons #5292

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions addons/addon-image/src/ImageAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import type { ITerminalAddon, IDisposable } from '@xterm/xterm';
import type { ITerminalAddon, IDisposable, ISharedExports } from '@xterm/xterm';
import type { ImageAddon as IImageApi } from '@xterm/addon-image';
import { IIPHandler } from './IIPHandler';
import { ImageRenderer } from './ImageRenderer';
Expand Down Expand Up @@ -57,7 +57,7 @@ export class ImageAddon implements ITerminalAddon , IImageApi {
private _terminal: ITerminalExt | undefined;
private _handlers: Map<String, IResetHandler> = new Map();

constructor(opts?: Partial<IImageAddonOptions>) {
constructor(private _sharedExports: ISharedExports, opts?: Partial<IImageAddonOptions>) {
this._opts = Object.assign({}, DEFAULT_OPTIONS, opts);
this._defaultOpts = Object.assign({}, DEFAULT_OPTIONS, opts);
}
Expand All @@ -80,7 +80,7 @@ export class ImageAddon implements ITerminalAddon , IImageApi {
this._terminal = terminal;

// internal data structures
this._renderer = new ImageRenderer(terminal);
this._renderer = new ImageRenderer(this._sharedExports, terminal);
this._storage = new ImageStorage(terminal, this._renderer, this._opts);

// enable size reports
Expand Down
15 changes: 8 additions & 7 deletions addons/addon-image/src/ImageRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*/

import { toRGBA8888 } from 'sixel/lib/Colors';
import { IDisposable } from '@xterm/xterm';
import { IDisposable, IMutableDisposable, ISharedExports } from '@xterm/xterm';
import { ICellSize, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { AddonDisposable } from 'common/shared/AddonDisposable';

const PLACEHOLDER_LENGTH = 4096;
const PLACEHOLDER_HEIGHT = 24;
Expand All @@ -17,12 +17,12 @@ const PLACEHOLDER_HEIGHT = 24;
* - add canvas layer to DOM (browser only for now)
* - draw image tiles onRender
*/
export class ImageRenderer extends Disposable implements IDisposable {
export class ImageRenderer extends AddonDisposable implements IDisposable {
public canvas: HTMLCanvasElement | undefined;
private _ctx: CanvasRenderingContext2D | null | undefined;
private _placeholder: HTMLCanvasElement | undefined;
private _placeholderBitmap: ImageBitmap | undefined;
private _optionsRefresh = this._register(new MutableDisposable());
private _optionsRefresh: IMutableDisposable<IDisposable>;
private _oldOpen: ((parent: HTMLElement) => void) | undefined;
private _renderService: IRenderService | undefined;
private _oldSetRenderer: ((renderer: any) => void) | undefined;
Expand Down Expand Up @@ -67,8 +67,8 @@ export class ImageRenderer extends Disposable implements IDisposable {
}


constructor(private _terminal: ITerminalExt) {
super();
constructor(sharedExports: ISharedExports, private _terminal: ITerminalExt) {
super(sharedExports);
this._oldOpen = this._terminal._core.open;
this._terminal._core.open = (parent: HTMLElement): void => {
this._oldOpen?.call(this._terminal._core, parent);
Expand All @@ -78,13 +78,14 @@ export class ImageRenderer extends Disposable implements IDisposable {
this._open();
}
// hack to spot fontSize changes
this._optionsRefresh = new sharedExports.MutableDisposable();
this._optionsRefresh.value = this._terminal._core.optionsService.onOptionChange(option => {
if (option === 'fontSize') {
this.rescaleCanvas();
this._renderService?.refreshRows(0, this._terminal.rows);
}
});
this._register(toDisposable(() => {
this._register(sharedExports.toDisposable(() => {
this.removeLayerFromDom();
if (this._terminal._core && this._oldOpen) {
this._terminal._core.open = this._oldOpen;
Expand Down
7 changes: 4 additions & 3 deletions addons/addon-image/test/ImageAddon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { readFileSync } from 'fs';
import { FINALIZER, introducer, sixelEncode } from 'sixel';
import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils';
import { deepStrictEqual, ok, strictEqual } from 'assert';
import { ISharedExports } from '@xterm/xterm';

/**
* Plugin ctor options.
Expand All @@ -27,7 +28,7 @@ export interface IImageAddonOptions {

// eslint-disable-next-line
declare const ImageAddon: {
new(options?: Partial<IImageAddonOptions>): any;
new(sharedExports: ISharedExports, options?: Partial<IImageAddonOptions>): any;
};

interface ITestData {
Expand Down Expand Up @@ -91,7 +92,7 @@ test.describe('ImageAddon', () => {
await ctx.page.evaluate(`
window.term.reset()
window.imageAddon?.dispose();
window.imageAddon = new ImageAddon({ sixelPaletteLimit: 512 });
window.imageAddon = new ImageAddon(sharedExports, { sixelPaletteLimit: 512 });
window.term.loadAddon(window.imageAddon);
`);
});
Expand Down Expand Up @@ -152,7 +153,7 @@ test.describe('ImageAddon', () => {
iipSizeLimit: 1000
};
await ctx.page.evaluate(opts => {
(window as any).imageAddonCustom = new ImageAddon(opts.opts);
(window as any).imageAddonCustom = new ImageAddon((window as any).sharedExports, opts.opts);
(window as any).term.loadAddon((window as any).imageAddonCustom);
}, { opts: customSettings });
deepStrictEqual(await ctx.page.evaluate(`window.imageAddonCustom._opts`), customSettings);
Expand Down
4 changes: 2 additions & 2 deletions addons/addon-image/typings/addon-image.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import { Terminal, ITerminalAddon } from '@xterm/xterm';
import { Terminal, ITerminalAddon, ISharedExports } from '@xterm/xterm';

declare module '@xterm/addon-image' {
export interface IImageAddonOptions {
Expand Down Expand Up @@ -79,7 +79,7 @@ declare module '@xterm/addon-image' {
}

export class ImageAddon implements ITerminalAddon {
constructor(options?: IImageAddonOptions);
constructor(sharedExports: ISharedExports, options?: IImageAddonOptions);
public activate(terminal: Terminal): void;
public dispose(): void;

Expand Down
16 changes: 8 additions & 8 deletions addons/addon-progress/src/ProgressAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
* @license MIT
*/

import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm';
import { Terminal, ITerminalAddon, IDisposable, IEmitter, IEvent, ISharedExports } from '@xterm/xterm';
import type { ProgressAddon as IProgressApi, IProgressState } from '@xterm/addon-progress';
import type { Emitter, Event } from 'vs/base/common/event';


const enum ProgressType {
Expand Down Expand Up @@ -37,9 +36,13 @@ export class ProgressAddon implements ITerminalAddon, IProgressApi {
private _seqHandler: IDisposable | undefined;
private _st: ProgressType = ProgressType.REMOVE;
private _pr = 0;
// HACK: This uses ! to align with the API, this should be fixed when 5283 is resolved
private _onChange!: Emitter<IProgressState>;
public onChange!: Event<IProgressState>;
private _onChange: IEmitter<IProgressState>;
public onChange: IEvent<IProgressState>;

constructor(sharedExports: ISharedExports) {
this._onChange = new sharedExports.Emitter<IProgressState>();
this.onChange = this._onChange.event;
}

public dispose(): void {
this._seqHandler?.dispose();
Expand Down Expand Up @@ -81,9 +84,6 @@ export class ProgressAddon implements ITerminalAddon, IProgressApi {
}
return true;
});
// FIXME: borrow emitter ctor from xterm, to be changed once #5283 is resolved
this._onChange = new (terminal as any)._core._onData.constructor();
this.onChange = this._onChange!.event;
}

public get progress(): IProgressState {
Expand Down
2 changes: 1 addition & 1 deletion addons/addon-progress/test/ProgressAddon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.describe('ProgressAddon', () => {
window.progressStack = [];
window.term.reset();
window.progressAddon?.dispose();
window.progressAddon = new ProgressAddon();
window.progressAddon = new ProgressAddon(sharedExports);
window.term.loadAddon(window.progressAddon);
window.progressAddon.onChange(progress => window.progressStack.push(progress));
`);
Expand Down
5 changes: 3 additions & 2 deletions addons/addon-progress/typings/addon-progress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* @license MIT
*/

import { Terminal, ITerminalAddon, IDisposable, IEvent } from '@xterm/xterm';
import { Terminal, ITerminalAddon, IDisposable, IEvent, ISharedExports } from '@xterm/xterm';


declare module '@xterm/addon-progress' {
/**
Expand All @@ -15,7 +16,7 @@ declare module '@xterm/addon-progress' {
/**
* Creates a new progress addon
*/
constructor();
constructor(sharedExports: ISharedExports);

/**
* Activates the addon
Expand Down
49 changes: 33 additions & 16 deletions addons/addon-search/src/SearchAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
* @license MIT
*/

import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
import type { Terminal, IDisposable, ITerminalAddon, IDecoration, ISharedExports, IEmitter, IEvent, IMutableDisposable } from '@xterm/xterm';
import type { SearchAddon as ISearchApi } from '@xterm/addon-search';
import { Emitter } from 'vs/base/common/event';
import { combinedDisposable, Disposable, dispose, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { AddonDisposable } from 'common/shared/AddonDisposable';


export interface ISearchOptions {
regex?: boolean;
Expand Down Expand Up @@ -62,12 +62,12 @@ const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
const DEFAULT_HIGHLIGHT_LIMIT = 1000;

export class SearchAddon extends Disposable implements ITerminalAddon , ISearchApi {
export class SearchAddon extends AddonDisposable implements ITerminalAddon , ISearchApi {
private _terminal: Terminal | undefined;
private _cachedSearchTerm: string | undefined;
private _highlightedLines: Set<number> = new Set();
private _highlightDecorations: IHighlight[] = [];
private _selectedDecoration: MutableDisposable<IHighlight> = this._register(new MutableDisposable());
private _selectedDecoration: IMutableDisposable<IHighlight>;
private _highlightLimit: number;
private _lastSearchOptions: ISearchOptions | undefined;
private _highlightTimeout: number | undefined;
Expand All @@ -78,13 +78,18 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
*/
private _linesCache: LineCacheEntry[] | undefined;
private _linesCacheTimeoutId = 0;
private _linesCacheDisposables = new MutableDisposable();
private _linesCacheDisposables: IMutableDisposable<IDisposable>;

private readonly _onDidChangeResults: IEmitter<{ resultIndex: number, resultCount: number }>;
public readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number }>;

private readonly _onDidChangeResults = this._register(new Emitter<{ resultIndex: number, resultCount: number }>());
public readonly onDidChangeResults = this._onDidChangeResults.event;
constructor(private _sharedExports: ISharedExports, options?: Partial<ISearchAddonOptions>) {
super(_sharedExports);
this._onDidChangeResults = this._register(new _sharedExports.Emitter<{ resultIndex: number, resultCount: number }>());
this.onDidChangeResults = this._onDidChangeResults.event;

constructor(options?: Partial<ISearchAddonOptions>) {
super();
this._selectedDecoration = this._register(new _sharedExports.MutableDisposable());
this._linesCacheDisposables = new _sharedExports.MutableDisposable();

this._highlightLimit = options?.highlightLimit ?? DEFAULT_HIGHLIGHT_LIMIT;
}
Expand All @@ -93,7 +98,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
this._terminal = terminal;
this._register(this._terminal.onWriteParsed(() => this._updateMatches()));
this._register(this._terminal.onResize(() => this._updateMatches()));
this._register(toDisposable(() => this.clearDecorations()));
this._register(this._sharedExports.toDisposable(() => this.clearDecorations()));
}

private _updateMatches(): void {
Expand All @@ -111,7 +116,9 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA

public clearDecorations(retainCachedSearchTerm?: boolean): void {
this._selectedDecoration.clear();
dispose(this._highlightDecorations);
for (let i = 0; i < this._highlightDecorations.length; ++i) {
this._highlightDecorations[i].dispose();
}
this._highlightDecorations = [];
this._highlightedLines.clear();
if (!retainCachedSearchTerm) {
Expand Down Expand Up @@ -426,11 +433,15 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
const terminal = this._terminal!;
if (!this._linesCache) {
this._linesCache = new Array(terminal.buffer.active.length);
this._linesCacheDisposables.value = combinedDisposable(
const disposables = [
terminal.onLineFeed(() => this._destroyLinesCache()),
terminal.onCursorMove(() => this._destroyLinesCache()),
terminal.onResize(() => this._destroyLinesCache())
);
];
this._linesCacheDisposables.value = this._sharedExports.toDisposable(() => {
for (let i = 0; i < disposables.length; ++i) disposables[i].dispose();
disposables.length = 0;
});
}

window.clearTimeout(this._linesCacheTimeoutId);
Expand Down Expand Up @@ -682,7 +693,10 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
const disposables: IDisposable[] = [];
disposables.push(marker);
disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));
disposables.push(decoration.onDispose(() => dispose(disposables)));
disposables.push(decoration.onDispose(() => {
for (let i = 0; i < disposables.length; ++i) disposables[i].dispose();
disposables.length = 0;
}));
this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } };
}
}
Expand Down Expand Up @@ -744,7 +758,10 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
const disposables: IDisposable[] = [];
disposables.push(marker);
disposables.push(findResultDecoration.onRender((e) => this._applyStyles(e, options.matchBorder, false)));
disposables.push(findResultDecoration.onDispose(() => dispose(disposables)));
disposables.push(findResultDecoration.onDispose(() => {
for (let i = 0; i < disposables.length; ++i) disposables[i].dispose();
disposables.length = 0;
}));
}
return findResultDecoration;
}
Expand Down
2 changes: 1 addition & 1 deletion addons/addon-search/test/SearchAddon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test.describe('Search Tests', () => {
await ctx.page.evaluate(`
window.term.reset()
window.search?.dispose();
window.search = new SearchAddon();
window.search = new SearchAddon(sharedExports);
window.term.loadAddon(window.search);
`);
});
Expand Down
4 changes: 2 additions & 2 deletions addons/addon-search/typings/addon-search.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import { Terminal, ITerminalAddon, IEvent } from '@xterm/xterm';
import { Terminal, ITerminalAddon, IEvent, ISharedExports } from '@xterm/xterm';

declare module '@xterm/addon-search' {
/**
Expand Down Expand Up @@ -95,7 +95,7 @@ declare module '@xterm/addon-search' {
* Creates a new search addon.
* @param options Options for the search addon.
*/
constructor(options?: Partial<ISearchAddonOptions>);
constructor(sharedExports: ISharedExports, options?: Partial<ISearchAddonOptions>);

/**
* Activates the addon
Expand Down
5 changes: 3 additions & 2 deletions addons/addon-webgl/src/CharAtlasCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { TextureAtlas } from './TextureAtlas';
import { ITerminalOptions, Terminal } from '@xterm/xterm';
import { ISharedExports, ITerminalOptions, Terminal } from '@xterm/xterm';
import { ITerminal, ReadonlyColorSet } from 'browser/Types';
import { ICharAtlasConfig, ITextureAtlas } from './Types';
import { generateConfig, configEquals } from './CharAtlasUtils';
Expand All @@ -24,6 +24,7 @@ const charAtlasCache: ITextureAtlasCacheEntry[] = [];
* one that is in use by another terminal.
*/
export function acquireTextureAtlas(
sharedExports: ISharedExports,
terminal: Terminal,
options: Required<ITerminalOptions>,
colors: ReadonlyColorSet,
Expand Down Expand Up @@ -67,7 +68,7 @@ export function acquireTextureAtlas(

const core: ITerminal = (terminal as any)._core;
const newEntry: ITextureAtlasCacheEntry = {
atlas: new TextureAtlas(document, newConfig, core.unicodeService),
atlas: new TextureAtlas(sharedExports, document, newConfig, core.unicodeService),
config: newConfig,
ownedBy: [terminal]
};
Expand Down
6 changes: 3 additions & 3 deletions addons/addon-webgl/src/DevicePixelObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
* @license MIT
*/

import { toDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { ISharedExports, IDisposable } from '@xterm/xterm';

export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
export function observeDevicePixelDimensions(sharedExports: ISharedExports, element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
// Observe any resizes to the element and extract the actual pixel size of the element if the
// devicePixelContentBoxSize API is supported. This allows correcting rounding errors when
// converting between CSS pixels and device pixels which causes blurry rendering when device
Expand Down Expand Up @@ -36,5 +36,5 @@ export function observeDevicePixelDimensions(element: HTMLElement, parentWindow:
observer.disconnect();
observer = undefined;
}
return toDisposable(() => observer?.disconnect());
return sharedExports.toDisposable(() => observer?.disconnect());
}
Loading
Loading