Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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