mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #3963 from Tyriar/fallback_ligatures
Support fallback ligatures and correctly dispose of char joiner
This commit is contained in:
@@ -33,15 +33,16 @@ This package locates the font file on disk for the font currently in use by the
|
||||
|
||||
Since this package depends on being able to find and resolve a system font from disk, it has to have system access that isn't available in the web browser. As a result, this package is mainly useful in environments that combine browser and Node.js runtimes (such as [Electron]).
|
||||
|
||||
### Fallback Ligatures
|
||||
|
||||
When ligatures cannot be fetched from the environment, a set of "fallback" ligatures is used to get the most common ligatures working. These fallback ligatures can be customized with options passed to `LigatureAddon.constructor`.
|
||||
|
||||
### Fonts
|
||||
|
||||
This package makes use of the following fonts for testing:
|
||||
|
||||
* [Fira Code][Fira Code] - [Licensed under the OFL][Fira Code License] by Nikita
|
||||
Prokopov, Mozilla Foundation with reserved names Fira Code, Fira Mono, and
|
||||
Fira Sans
|
||||
* [Iosevka] - [Licensed under the OFL][Iosevka License] by Belleve Invis with
|
||||
reserved name Iosevka
|
||||
* [Fira Code][Fira Code] - [Licensed under the OFL][Fira Code License] by Nikita Prokopov, Mozilla Foundation with reserved names Fira Code, Fira Mono, and Fira Sans
|
||||
* [Iosevka] - [Licensed under the OFL][Iosevka License] by Belleve Invis with reserved name Iosevka
|
||||
|
||||
[xterm.js]: https://github.com/xtermjs/xterm.js
|
||||
[Electron]: https://electronjs.org/
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { Terminal } from 'xterm';
|
||||
import { enableLigatures } from '.';
|
||||
import { ILigatureOptions } from './Types';
|
||||
|
||||
export interface ITerminalAddon {
|
||||
activate(terminal: Terminal): void;
|
||||
@@ -12,12 +13,31 @@ export interface ITerminalAddon {
|
||||
}
|
||||
|
||||
export class LigaturesAddon implements ITerminalAddon {
|
||||
constructor() {}
|
||||
private readonly _fallbackLigatures: string[];
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
enableLigatures(terminal);
|
||||
private _terminal: Terminal | undefined;
|
||||
private _characterJoinerId: number | undefined;
|
||||
|
||||
constructor(options?: Partial<ILigatureOptions>) {
|
||||
this._fallbackLigatures = (options?.fallbackLigatures || [
|
||||
'<--', '<---', '<<-', '<-', '->', '->>', '-->', '--->',
|
||||
'<==', '<===', '<<=', '<=', '=>', '=>>', '==>', '===>', '>=', '>>=',
|
||||
'<->', '<-->', '<--->', '<---->', '<=>', '<==>', '<===>', '<====>', '-------->',
|
||||
'<~~', '<~', '~>', '~~>', '::', ':::', '==', '!=', '===', '!==',
|
||||
':=', ':-', ':+', '<*', '<*>', '*>', '<|', '<|>', '|>', '+:', '-:', '=:', ':>',
|
||||
'++', '+++', '<!--', '<!---', '<***>'
|
||||
]).sort((a, b) => b.length - a.length);
|
||||
}
|
||||
|
||||
public dispose(): void {}
|
||||
}
|
||||
public activate(terminal: Terminal): void {
|
||||
this._terminal = terminal;
|
||||
this._characterJoinerId = enableLigatures(terminal, this._fallbackLigatures);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._characterJoinerId !== undefined) {
|
||||
this._terminal?.deregisterCharacterJoiner(this._characterJoinerId);
|
||||
this._characterJoinerId = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export interface ILigatureOptions {
|
||||
fallbackLigatures: string[];
|
||||
}
|
||||
@@ -144,7 +144,6 @@ describe('xterm-addon-ligatures', () => {
|
||||
assert.deepEqual(term.joiner!(input), []);
|
||||
await delay(500);
|
||||
assert.isTrue(onRefresh.notCalled);
|
||||
assert.throws(() => term.joiner!(input));
|
||||
});
|
||||
|
||||
it('returns nothing if the font is not present on the system', async () => {
|
||||
@@ -176,7 +175,6 @@ describe('xterm-addon-ligatures', () => {
|
||||
assert.deepEqual(term.joiner!(input), []);
|
||||
await delay(500);
|
||||
assert.isTrue(onRefresh.notCalled);
|
||||
assert.throws(() => term.joiner!(input));
|
||||
});
|
||||
|
||||
it('ensures no empty errors are thrown', async () => {
|
||||
@@ -185,7 +183,6 @@ describe('xterm-addon-ligatures', () => {
|
||||
assert.deepEqual(term.joiner!(input), []);
|
||||
await delay(500);
|
||||
assert.isTrue(onRefresh.notCalled);
|
||||
assert.throws(() => term.joiner!(input), 'Failure while loading font');
|
||||
(fontLigatures.loadFile as sinon.SinonStub).restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,13 +26,13 @@ const CACHE_SIZE = 100000;
|
||||
* start to render them.
|
||||
* @param term Terminal instance from xterm.js
|
||||
*/
|
||||
export function enableLigatures(term: Terminal): void {
|
||||
export function enableLigatures(term: Terminal, fallbackLigatures: string[] = []): number {
|
||||
let currentFontName: string | undefined = undefined;
|
||||
let font: Font | undefined = undefined;
|
||||
let loadingState: LoadingState = LoadingState.UNLOADED;
|
||||
let loadError: any | undefined = undefined;
|
||||
|
||||
term.registerCharacterJoiner((text: string): [number, number][] => {
|
||||
return term.registerCharacterJoiner((text: string): [number, number][] => {
|
||||
// If the font hasn't been loaded yet, load it and return an empty result
|
||||
const termFont = term.options.fontFamily;
|
||||
if (
|
||||
@@ -63,6 +63,9 @@ export function enableLigatures(term: Terminal): void {
|
||||
// sure our font is still vaild.
|
||||
if (currentCallFontName === term.options.fontFamily) {
|
||||
loadingState = LoadingState.FAILED;
|
||||
if (term.options.logLevel === 'debug') {
|
||||
console.debug(loadError, new Error('Failure while loading font'));
|
||||
}
|
||||
font = undefined;
|
||||
loadError = e;
|
||||
}
|
||||
@@ -76,10 +79,21 @@ export function enableLigatures(term: Terminal): void {
|
||||
range => [range[0], range[1]]
|
||||
);
|
||||
}
|
||||
if (loadingState === LoadingState.FAILED) {
|
||||
throw loadError || new Error('Failure while loading font');
|
||||
}
|
||||
|
||||
return [];
|
||||
return getFallbackRanges(text, fallbackLigatures);
|
||||
});
|
||||
}
|
||||
|
||||
function getFallbackRanges(text: string, fallbackLigatures: string[]): [number, number][] {
|
||||
const ranges: [number, number][] = [];
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
for (let j = 0; j < fallbackLigatures.length; j++) {
|
||||
if (text.startsWith(fallbackLigatures[j], i)) {
|
||||
ranges.push([i, i + fallbackLigatures[j].length]);
|
||||
i += fallbackLigatures[j].length - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,14 @@ declare module 'xterm-addon-ligatures' {
|
||||
export class LigaturesAddon implements ITerminalAddon {
|
||||
/**
|
||||
* Creates a new ligatures addon.
|
||||
*
|
||||
* @param options Options for the ligatures addon.
|
||||
*/
|
||||
constructor();
|
||||
constructor(options?: Partial<ILigatureOptions>);
|
||||
|
||||
/**
|
||||
* Activates the addon
|
||||
*
|
||||
* @param terminal The terminal the addon is being loaded in.
|
||||
*/
|
||||
public activate(terminal: Terminal): void;
|
||||
@@ -31,4 +34,25 @@ declare module 'xterm-addon-ligatures' {
|
||||
*/
|
||||
public dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the ligatures addon.
|
||||
*/
|
||||
export interface ILigatureOptions {
|
||||
/**
|
||||
* Fallback ligatures to use when the font access API is either not supported by the browser or
|
||||
* access is denied. The default set of ligatures is taken from Iosevka's default "calt"
|
||||
* ligation set: https://typeof.net/Iosevka/
|
||||
*
|
||||
* ```
|
||||
* <-- <--- <<- <- -> ->> --> --->
|
||||
* <== <=== <<= <= => =>> ==> ===> >= >>=
|
||||
* <-> <--> <---> <----> <=> <==> <===> <====> -------->
|
||||
* <~~ <~ ~> ~~> :: ::: == != === !==
|
||||
* := :- :+ <* <*> *> <| <|> |> +: -: =: :>
|
||||
* ++ +++ <!-- <!--- <***>
|
||||
* ```
|
||||
*/
|
||||
fallbackLigatures: string[]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user