Merge branch 'master' into typo_intefaces
This commit is contained in:
+4
-2
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { ICharset, ILinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, LineData } from './Types';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, LineData, FontWeight } from './Types';
|
||||
import { IColorSet, IRenderer } from './renderer/Interfaces';
|
||||
import { IMouseZoneManager } from './input/Interfaces';
|
||||
|
||||
@@ -137,12 +137,14 @@ export interface ITerminalOptions {
|
||||
cursorStyle?: string;
|
||||
debug?: boolean;
|
||||
disableStdin?: boolean;
|
||||
enableBold?: boolean;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
fontWeight?: FontWeight;
|
||||
fontWeightBold?: FontWeight;
|
||||
handler?: (data: string) => void;
|
||||
letterSpacing?: number;
|
||||
lineHeight?: number;
|
||||
macOptionIsMeta?: boolean;
|
||||
rows?: number;
|
||||
screenKeys?: boolean;
|
||||
scrollback?: number;
|
||||
|
||||
+3
-3
@@ -82,12 +82,12 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
}
|
||||
|
||||
// Increase range to linkify
|
||||
if (!this._rowsToLinkify.start) {
|
||||
if (this._rowsToLinkify.start === null) {
|
||||
this._rowsToLinkify.start = start;
|
||||
this._rowsToLinkify.end = end;
|
||||
} else {
|
||||
this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start;
|
||||
this._rowsToLinkify.end = this._rowsToLinkify.end > end ? this._rowsToLinkify.end : end;
|
||||
this._rowsToLinkify.start = Math.min(this._rowsToLinkify.start, start);
|
||||
this._rowsToLinkify.end = Math.max(this._rowsToLinkify.end, end);
|
||||
}
|
||||
|
||||
// Clear out any existing links on this row range
|
||||
|
||||
@@ -503,6 +503,9 @@ describe('term.js addons', () => {
|
||||
it('should return \\x1b[5C for alt+right', () => {
|
||||
assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C
|
||||
});
|
||||
it('should return \\x1ba for alt+a', () => {
|
||||
assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, '\x1ba');
|
||||
});
|
||||
});
|
||||
|
||||
describe('On macOS platforms', () => {
|
||||
@@ -515,6 +518,19 @@ describe('term.js addons', () => {
|
||||
it('should return \\x1bf for alt+right', () => {
|
||||
assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1bf'); // CSI 5 C
|
||||
});
|
||||
it('should return undefined for alt+a', () => {
|
||||
assert.strictEqual(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with macOptionIsMeta', () => {
|
||||
beforeEach(() => {
|
||||
term.browser.isMac = true;
|
||||
term.setOption('macOptionIsMeta', true);
|
||||
});
|
||||
it('should return \\x1ba for alt+a', () => {
|
||||
assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, '\x1ba');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return \\x1b[5A for alt+up', () => {
|
||||
@@ -597,6 +613,22 @@ describe('term.js addons', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('with macOptionIsMeta', () => {
|
||||
beforeEach(() => {
|
||||
term.browser.isMac = true;
|
||||
term.setOption('macOptionIsMeta', true);
|
||||
});
|
||||
|
||||
it('should interfere with the alt key on keyDown', () => {
|
||||
evKeyDown.altKey = true;
|
||||
evKeyDown.keyCode = 81;
|
||||
assert.equal(term.keyDown(evKeyDown), false);
|
||||
evKeyDown.altKey = true;
|
||||
evKeyDown.keyCode = 192;
|
||||
assert.equal(term.keyDown(evKeyDown), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('On Mac OS', () => {
|
||||
beforeEach(() => {
|
||||
term.browser.isMac = true;
|
||||
|
||||
+35
-21
@@ -72,14 +72,16 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
|
||||
cursorStyle: 'block',
|
||||
bellSound: BELL_SOUND,
|
||||
bellStyle: 'none',
|
||||
enableBold: true,
|
||||
fontFamily: 'courier-new, courier, monospace',
|
||||
fontSize: 15,
|
||||
fontWeight: 'normal',
|
||||
fontWeightBold: 'bold',
|
||||
lineHeight: 1.0,
|
||||
letterSpacing: 0,
|
||||
scrollback: 1000,
|
||||
screenKeys: false,
|
||||
debug: false,
|
||||
macOptionIsMeta: false,
|
||||
cancelEvents: false,
|
||||
disableStdin: false,
|
||||
useFlowControl: false,
|
||||
@@ -362,6 +364,16 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
value = 'block';
|
||||
}
|
||||
break;
|
||||
case 'fontWeight':
|
||||
if (!value) {
|
||||
value = 'normal';
|
||||
}
|
||||
break;
|
||||
case 'fontWeightBold':
|
||||
if (!value) {
|
||||
value = 'bold';
|
||||
}
|
||||
break;
|
||||
case 'lineHeight':
|
||||
if (value < 1) {
|
||||
console.warn(`${key} cannot be less than 1, value: ${value}`);
|
||||
@@ -412,14 +424,16 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
this.renderer.clear();
|
||||
this.charMeasure.measure(this.options);
|
||||
break;
|
||||
case 'enableBold':
|
||||
case 'letterSpacing':
|
||||
case 'lineHeight':
|
||||
case 'fontWeight':
|
||||
case 'fontWeightBold':
|
||||
const didCharSizeChange = (key === 'fontWeight' || key === 'fontWeightBold');
|
||||
|
||||
// When the font changes the size of the cells may change which requires a renderer clear
|
||||
this.renderer.clear();
|
||||
this.renderer.onResize(this.cols, this.rows, false);
|
||||
this.renderer.onResize(this.cols, this.rows, didCharSizeChange);
|
||||
this.refresh(0, this.rows - 1);
|
||||
// this.charMeasure.measure(this.options);
|
||||
case 'scrollback':
|
||||
this.buffers.resize(this.cols, this.rows);
|
||||
this.viewport.syncScrollArea();
|
||||
@@ -1374,7 +1388,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
return this.cancel(ev, true);
|
||||
}
|
||||
|
||||
if (isThirdLevelShift(this.browser, ev)) {
|
||||
if (this._isThirdLevelShift(this.browser, ev)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1395,6 +1409,19 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
return this.cancel(ev, true);
|
||||
}
|
||||
|
||||
private _isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {
|
||||
const thirdLevelKey =
|
||||
(browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
|
||||
(browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
|
||||
|
||||
if (ev.type === 'keypress') {
|
||||
return thirdLevelKey;
|
||||
}
|
||||
|
||||
// Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)
|
||||
return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object that determines how a KeyboardEvent should be handled. The key of the
|
||||
* returned value is the new key code to pass to the PTY.
|
||||
@@ -1699,8 +1726,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
// ^] - Operating System Command (OSC)
|
||||
result.key = String.fromCharCode(29);
|
||||
}
|
||||
} else if (!this.browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) {
|
||||
// On Mac this is a third level shift. Use <Esc> instead.
|
||||
} else if ((!this.browser.isMac || this.options.macOptionIsMeta) && ev.altKey && !ev.ctrlKey && !ev.metaKey) {
|
||||
// On macOS this is a third level shift when !macOptionIsMeta. Use <Esc> instead.
|
||||
if (ev.keyCode >= 65 && ev.keyCode <= 90) {
|
||||
result.key = C0.ESC + String.fromCharCode(ev.keyCode + 32);
|
||||
} else if (ev.keyCode === 192) {
|
||||
@@ -1766,7 +1793,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
}
|
||||
|
||||
if (!key || (
|
||||
(ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev)
|
||||
(ev.altKey || ev.ctrlKey || ev.metaKey) && !this._isThirdLevelShift(this.browser, ev)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@@ -2158,19 +2185,6 @@ function off(el: any, type: string, handler: (event: Event) => any, capture: boo
|
||||
el.removeEventListener(type, handler, capture);
|
||||
}
|
||||
|
||||
function isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {
|
||||
const thirdLevelKey =
|
||||
(browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
|
||||
(browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
|
||||
|
||||
if (ev.type === 'keypress') {
|
||||
return thirdLevelKey;
|
||||
}
|
||||
|
||||
// Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)
|
||||
return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);
|
||||
}
|
||||
|
||||
function wasMondifierKeyOnlyEvent(ev: KeyboardEvent): boolean {
|
||||
return ev.keyCode === 16 || // Shift
|
||||
ev.keyCode === 17 || // Ctrl
|
||||
|
||||
@@ -16,3 +16,5 @@ export enum LinkHoverEventTypes {
|
||||
TOOLTIP = 'linktooltip',
|
||||
LEAVE = 'linkleave'
|
||||
}
|
||||
|
||||
export type FontWeight = 'normal' | 'bold' | 'bolder' | 'lighter' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
|
||||
@@ -201,7 +201,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
* @param color The color of the character.
|
||||
*/
|
||||
protected fillCharTrueColor(terminal: ITerminal, charData: CharData, x: number, y: number): void {
|
||||
this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
this._ctx.font = this._getFont(terminal, false);
|
||||
this._ctx.textBaseline = 'top';
|
||||
this._clipRow(terminal, y);
|
||||
this._ctx.fillText(
|
||||
@@ -230,7 +230,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
colorIndex = fg + 2;
|
||||
} else {
|
||||
// If default color and bold
|
||||
if (bold && terminal.options.enableBold) {
|
||||
if (bold) {
|
||||
colorIndex = 1;
|
||||
}
|
||||
}
|
||||
@@ -251,13 +251,6 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.globalAlpha = DIM_OPACITY;
|
||||
}
|
||||
|
||||
// Draw the non-bold version of the same color if bold is not enabled
|
||||
if (bold && !terminal.options.enableBold) {
|
||||
// Ignore default color as it's not touched above
|
||||
if (colorIndex > 1) {
|
||||
colorIndex -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
this._ctx.drawImage(this._charAtlas,
|
||||
code * charAtlasCellWidth,
|
||||
@@ -289,10 +282,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
*/
|
||||
private _drawUncachedChar(terminal: ITerminal, char: string, width: number, fg: number, x: number, y: number, bold: boolean, dim: boolean): void {
|
||||
this._ctx.save();
|
||||
this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
if (bold && terminal.options.enableBold) {
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
}
|
||||
this._ctx.font = this._getFont(terminal, bold);
|
||||
this._ctx.textBaseline = 'top';
|
||||
|
||||
if (fg === INVERTED_DEFAULT_COLOR) {
|
||||
@@ -332,5 +322,16 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._scaledCellHeight);
|
||||
this._ctx.clip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current font.
|
||||
* @param terminal The terminal.
|
||||
* @param isBold If we should use the bold fontWeight.
|
||||
*/
|
||||
protected _getFont(terminal: ITerminal, isBold: boolean): string {
|
||||
const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
|
||||
|
||||
return `${fontWeight} ${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ export const CHAR_ATLAS_CELL_SPACING = 1;
|
||||
interface ICharAtlasConfig {
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
fontWeight: string;
|
||||
fontWeightBold: string;
|
||||
scaledCharWidth: number;
|
||||
scaledCharHeight: number;
|
||||
colors: IColorSet;
|
||||
@@ -64,7 +66,7 @@ export function acquireCharAtlas(terminal: ITerminal, colors: IColorSet, scaledC
|
||||
}
|
||||
|
||||
const newEntry: ICharAtlasCacheEntry = {
|
||||
bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, colors.background, colors.foreground, colors.ansi),
|
||||
bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, terminal.options.fontWeight, terminal.options.fontWeightBold, colors.background, colors.foreground, colors.ansi),
|
||||
config: newConfig,
|
||||
ownedBy: [terminal]
|
||||
};
|
||||
@@ -86,6 +88,8 @@ function generateConfig(scaledCharWidth: number, scaledCharHeight: number, termi
|
||||
scaledCharHeight,
|
||||
fontFamily: terminal.options.fontFamily,
|
||||
fontSize: terminal.options.fontSize,
|
||||
fontWeight: terminal.options.fontWeight,
|
||||
fontWeightBold: terminal.options.fontWeightBold,
|
||||
colors: clonedColors
|
||||
};
|
||||
}
|
||||
@@ -98,6 +102,8 @@ function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {
|
||||
}
|
||||
return a.fontFamily === b.fontFamily &&
|
||||
a.fontSize === b.fontSize &&
|
||||
a.fontWeight === b.fontWeight &&
|
||||
a.fontWeightBold === b.fontWeightBold &&
|
||||
a.scaledCharWidth === b.scaledCharWidth &&
|
||||
a.scaledCharHeight === b.scaledCharHeight &&
|
||||
a.colors.foreground === b.colors.foreground &&
|
||||
@@ -126,7 +132,7 @@ class CharAtlasGenerator {
|
||||
this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
}
|
||||
|
||||
public generate(scaledCharWidth: number, scaledCharHeight: number, fontSize: number, fontFamily: string, background: string, foreground: string, ansiColors: string[]): HTMLCanvasElement | Promise<ImageBitmap> {
|
||||
public generate(scaledCharWidth: number, scaledCharHeight: number, fontSize: number, fontFamily: string, fontWeight: string, fontWeightBold: string, background: string, foreground: string, ansiColors: string[]): HTMLCanvasElement | Promise<ImageBitmap> {
|
||||
const cellWidth = scaledCharWidth + CHAR_ATLAS_CELL_SPACING;
|
||||
const cellHeight = scaledCharHeight + CHAR_ATLAS_CELL_SPACING;
|
||||
this._canvas.width = 255 * cellWidth;
|
||||
@@ -137,7 +143,7 @@ class CharAtlasGenerator {
|
||||
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = foreground;
|
||||
this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;
|
||||
this._ctx.font = this._getFont(fontWeight, fontSize, fontFamily);
|
||||
this._ctx.textBaseline = 'top';
|
||||
|
||||
// Default color
|
||||
@@ -151,7 +157,7 @@ class CharAtlasGenerator {
|
||||
}
|
||||
// Default color bold
|
||||
this._ctx.save();
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
this._ctx.font = this._getFont(fontWeightBold, fontSize, fontFamily);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
@@ -163,11 +169,11 @@ class CharAtlasGenerator {
|
||||
this._ctx.restore();
|
||||
|
||||
// Colors 0-15
|
||||
this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;
|
||||
this._ctx.font = this._getFont(fontWeight, fontSize, fontFamily);
|
||||
for (let colorIndex = 0; colorIndex < 16; colorIndex++) {
|
||||
// colors 8-15 are bold
|
||||
if (colorIndex === 8) {
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
this._ctx.font = this._getFont(fontWeightBold, fontSize, fontFamily);
|
||||
}
|
||||
const y = (colorIndex + 2) * cellHeight;
|
||||
// Draw ascii characters
|
||||
@@ -219,4 +225,8 @@ class CharAtlasGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getFont(fontWeight: string, fontSize: number, fontFamily: string): string {
|
||||
return `${fontWeight} ${fontSize * window.devicePixelRatio}px ${fontFamily}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
super.resize(terminal, dim, charSizeChanged);
|
||||
|
||||
// Clear the character width cache if the font or width has changed
|
||||
const terminalFont = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
const terminalFont = this._getFont(terminal, false);
|
||||
if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {
|
||||
this._characterWidth = dim.scaledCharWidth;
|
||||
this._characterFont = terminalFont;
|
||||
@@ -166,7 +166,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
|
||||
this._ctx.save();
|
||||
if (flags & FLAGS.BOLD) {
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
this._ctx.font = this._getFont(terminal, true);
|
||||
// Convert the FG color to the bold variant
|
||||
if (fg < 8) {
|
||||
fg += 8;
|
||||
|
||||
Reference in New Issue
Block a user