Skip to content

Commit

Permalink
Prototype with using addon config in ctor
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyriar committed Jan 8, 2019
1 parent 7ba17ca commit 9822953
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 5 deletions.
17 changes: 16 additions & 1 deletion demo/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { Terminal } from '../lib/public/Terminal';
import { AttachAddon } from 'xterm-addon-attach';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { WebLinksAddonWithConfig } from '../src/Terminal';

import * as fit from '../lib/addons/fit/fit';
import * as fullscreen from '../lib/addons/fullscreen/fullscreen';
Expand Down Expand Up @@ -87,7 +88,21 @@ function createTerminal(): void {

// Load addons
const typedTerm = term as TerminalType;
typedTerm.loadAddon(WebLinksAddon).init();

// Works
typedTerm.loadAddon(WebLinksAddon);

// Doesn't work since it extends ITerminalAddonWithConfig
typedTerm.loadAddon(WebLinksAddonWithConfig);

// Works
typedTerm.loadAddonWithConfig(WebLinksAddonWithConfig, { handler: () => {} });

// Doesn't work since { a: 1 } isn't a IWebLinksAddonConfig
typedTerm.loadAddonWithConfig(WebLinksAddonWithConfig, {
a: 1
});

attachAddon = typedTerm.loadAddon(AttachAddon);

window.term = term; // Expose `term` to window for debugging purposes
Expand Down
50 changes: 49 additions & 1 deletion src/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
import { MouseZoneManager } from './ui/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ScreenDprMonitor } from './ui/ScreenDprMonitor';
import { ITheme, IMarker, IDisposable, ITerminalAddon, ITerminalAddonConstructor } from 'xterm';
import { ITheme, IMarker, IDisposable, ITerminalAddon, ITerminalAddonConstructor, ITerminalAddonWithConfig, ITerminalAddonWithConfigConstructor } from 'xterm';
import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache';
import { DomRenderer } from './renderer/dom/DomRenderer';
import { IKeyboardEvent } from './common/Types';
Expand Down Expand Up @@ -1941,6 +1941,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
return this._addonManager.loadAddon(this, addonConstructor);
}

public loadAddonWithConfig<T extends ITerminalAddonWithConfig<K>, K>(addonConstructor: ITerminalAddonWithConfigConstructor<T, K>, config: K): T {
return this._addonManager.loadAddonWithConfig(this, addonConstructor, config);
}

public disposeAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): void {
this._addonManager.disposeAddon(addonConstructor);
}
Expand All @@ -1950,6 +1954,50 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
}
}

const protocolClause = '(https?:\\/\\/)';
const domainCharacterSet = '[\\da-z\\.-]+';
const negatedDomainCharacterSet = '[^\\da-z\\.-]+';
const domainBodyClause = '(' + domainCharacterSet + ')';
const tldClause = '([a-z\\.]{2,6})';
const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
const localHostClause = '(localhost)';
const portClause = '(:\\d{1,5})';
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
const pathClause = '(\\/[\\/\\w\\.\\-%~:]*)*([^:"\'\\s])';
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;
const start = '(?:^|' + negatedDomainCharacterSet + ')(';
const end = ')($|' + negatedPathCharacterSet + ')';
const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);

function handleLink(event: MouseEvent, uri: string): void {
window.open(uri, '_blank');
}

export interface IWebLinksAddonConfig {
handler?: (event: MouseEvent, uri: string) => void;
options?: ILinkMatcherOptions;
}

export class WebLinksAddonWithConfig implements ITerminalAddonWithConfig<IWebLinksAddonConfig> {
private _linkMatcherId: number;

constructor(private _terminal: Terminal, config: IWebLinksAddonConfig) {
}

public init(handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void {
options.matchIndex = 1;
this._linkMatcherId = this._terminal.registerLinkMatcher(strictUrlRegex, handler, options);
}

public dispose(): void {
this._terminal.deregisterLinkMatcher(this._linkMatcherId);
}
}

/**
* Helpers
*/
Expand Down
5 changes: 4 additions & 1 deletion src/ui/AddonManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import { ITerminalAddon, ITerminalAddonConstructor, IDisposable, Terminal } from 'xterm';
import { ITerminalAddon, ITerminalAddonConstructor, IDisposable, Terminal, ITerminalAddonWithConfigConstructor } from 'xterm';

export interface ILoadedAddon {
ctor: ITerminalAddonConstructor<ITerminalAddon>;
Expand Down Expand Up @@ -34,6 +34,9 @@ export class AddonManager implements IDisposable {
instance.dispose = () => this._wrappedAddonDispose(loadedAddon);
return instance;
}
public loadAddonWithConfig<T extends ITerminalAddon, K>(terminal: Terminal, addonConstructor: ITerminalAddonWithConfigConstructor<T, K>, config: K): T {
return null;
}

public disposeAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): void {
const match = this._addons.find(value => value.ctor === addonConstructor);
Expand Down
24 changes: 22 additions & 2 deletions typings/xterm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -755,14 +755,19 @@ declare module 'xterm' {
static applyAddon(addon: any): void;

loadAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T;
disposeAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): void;
getAddon<T extends ITerminalAddon>(addonConstructor: ITerminalAddonConstructor<T>): T;
loadAddonWithConfig<T extends ITerminalAddonWithConfig<K>, K>(addonConstructor: ITerminalAddonWithConfigConstructor<T, K>, config: K): T;
// disposeAddon<T extends ITerminalAddon<K>, K>(addonConstructor: ITerminalAddonConstructor<T, K>): void;
// getAddon<T extends ITerminalAddon<K>, K>(addonConstructor: ITerminalAddonConstructor<T, K>): T;
}

export interface ITerminalAddonConstructor<T extends ITerminalAddon> {
new(terminal: Terminal): T;
}

export interface ITerminalAddonWithConfigConstructor<T extends ITerminalAddonWithConfig<K>, K> {
new(terminal: Terminal, config: K): T;
}

export interface ITerminalAddon {
/**
* This property declares all addon dependencies that must be intialized
Expand All @@ -777,4 +782,19 @@ declare module 'xterm' {
*/
dispose(): void;
}

export interface ITerminalAddonWithConfig<K> {
/**
* This property declares all addon dependencies that must be intialized
* before this addon can be constructed. For addons with no dependencies
* just don't include this property.
*/
// readonly DEPENDENCIES?: ITerminalAddonConstructor<ITerminalAddon>[];

/**
* This function includes anything that needs to happen to clean up when
* the addon is being disposed.
*/
dispose(): void;
}
}

0 comments on commit 9822953

Please sign in to comment.