mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into alt_buffer
This commit is contained in:
+2
-1
@@ -30,7 +30,8 @@ opening an issue, read these pointers.
|
||||
|
||||
## Contributing code
|
||||
|
||||
- Make sure you have a [GitHub account](https://github.com/join)
|
||||
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
|
||||
|
||||
- Fork [xterm.js](https://github.com/sourcelair/xterm.js/)
|
||||
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
||||
- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running
|
||||
|
||||
+2
-5
@@ -94,11 +94,8 @@ export class BufferLine implements IBufferLine {
|
||||
}
|
||||
}
|
||||
|
||||
public copyFrom(line: IBufferLine): void {
|
||||
this._data = [];
|
||||
for (let i = 0; i < line.length; ++i) {
|
||||
this._push(line.get(i));
|
||||
}
|
||||
public copyFrom(line: BufferLine): void {
|
||||
this._data = line._data.slice(0);
|
||||
this.length = line.length;
|
||||
this.isWrapped = line.isWrapped;
|
||||
}
|
||||
|
||||
+176
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
import { TestTerminal } from './utils/TestUtils.test';
|
||||
import { assert } from 'chai';
|
||||
import { getStringCellWidth } from './CharWidth';
|
||||
import { getStringCellWidth, wcwidth } from './CharWidth';
|
||||
import { IBuffer } from './Types';
|
||||
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
|
||||
@@ -77,3 +77,178 @@ describe('getStringCellWidth', function(): void {
|
||||
});
|
||||
// TODO: multiline tests once #1685 is resolved
|
||||
});
|
||||
|
||||
it('wcwidth should match all values from the old implementation', function(): void {
|
||||
// old implementation
|
||||
const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING_BMP = [
|
||||
[0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
|
||||
[0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
|
||||
[0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],
|
||||
[0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],
|
||||
[0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],
|
||||
[0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],
|
||||
[0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],
|
||||
[0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],
|
||||
[0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],
|
||||
[0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],
|
||||
[0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
|
||||
[0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
|
||||
[0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],
|
||||
[0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],
|
||||
[0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
|
||||
[0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],
|
||||
[0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],
|
||||
[0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
|
||||
[0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],
|
||||
[0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],
|
||||
[0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],
|
||||
[0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],
|
||||
[0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],
|
||||
[0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],
|
||||
[0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],
|
||||
[0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],
|
||||
[0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],
|
||||
[0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],
|
||||
[0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],
|
||||
[0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],
|
||||
[0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],
|
||||
[0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],
|
||||
[0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],
|
||||
[0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],
|
||||
[0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],
|
||||
[0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],
|
||||
[0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],
|
||||
[0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],
|
||||
[0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],
|
||||
[0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
|
||||
[0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
|
||||
[0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
|
||||
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB]
|
||||
];
|
||||
const COMBINING_HIGH = [
|
||||
[0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
|
||||
[0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],
|
||||
[0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
|
||||
[0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
|
||||
[0xE0100, 0xE01EF]
|
||||
];
|
||||
// binary search
|
||||
function bisearch(ucs: number, data: number[][]): boolean {
|
||||
let min = 0;
|
||||
let max = data.length - 1;
|
||||
let mid;
|
||||
if (ucs < data[0][0] || ucs > data[max][1]) {
|
||||
return false;
|
||||
}
|
||||
while (max >= min) {
|
||||
mid = (min + max) >> 1;
|
||||
if (ucs > data[mid][1]) {
|
||||
min = mid + 1;
|
||||
} else if (ucs < data[mid][0]) {
|
||||
max = mid - 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function wcwidthBMP(ucs: number): number {
|
||||
// test for 8-bit control characters
|
||||
if (ucs === 0) {
|
||||
return opts.nul;
|
||||
}
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) {
|
||||
return opts.control;
|
||||
}
|
||||
// binary search in table of non-spacing characters
|
||||
if (bisearch(ucs, COMBINING_BMP)) {
|
||||
return 0;
|
||||
}
|
||||
// if we arrive here, ucs is not a combining or C0/C1 control character
|
||||
if (isWideBMP(ucs)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
function isWideBMP(ucs: number): boolean {
|
||||
return (
|
||||
ucs >= 0x1100 && (
|
||||
ucs <= 0x115f || // Hangul Jamo init. consonants
|
||||
ucs === 0x2329 ||
|
||||
ucs === 0x232a ||
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
|
||||
(ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs
|
||||
(ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6)));
|
||||
}
|
||||
function wcwidthHigh(ucs: number): 0 | 1 | 2 {
|
||||
if (bisearch(ucs, COMBINING_HIGH)) {
|
||||
return 0;
|
||||
}
|
||||
if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
const control = opts.control | 0;
|
||||
let table: number[] | Uint32Array = null;
|
||||
function initTable(): number[] | Uint32Array {
|
||||
// lookup table for BMP
|
||||
const CODEPOINTS = 65536; // BMP holds 65536 codepoints
|
||||
const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2
|
||||
const ITEMSIZE = 32; // using uint32_t
|
||||
const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE;
|
||||
const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH;
|
||||
table = (typeof Uint32Array === 'undefined')
|
||||
? new Array(CONTAINERSIZE)
|
||||
: new Uint32Array(CONTAINERSIZE);
|
||||
for (let i = 0; i < CONTAINERSIZE; ++i) {
|
||||
let num = 0;
|
||||
let pos = CODEPOINTS_PER_ITEM;
|
||||
while (pos--) {
|
||||
num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos);
|
||||
}
|
||||
table[i] = num;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
// get width from lookup table
|
||||
// position in container : num / CODEPOINTS_PER_ITEM
|
||||
// ==> n = table[Math.floor(num / 16)]
|
||||
// ==> n = table[num >> 4]
|
||||
// 16 codepoints per number: FFEEDDCCBBAA99887766554433221100
|
||||
// position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH
|
||||
// ==> m = (n % 16) * 2
|
||||
// ==> m = (num & 15) << 1
|
||||
// right shift to position m
|
||||
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
// we are only interested in 2 LSBs, cut off higher bits
|
||||
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
return function (num: number): number {
|
||||
num = num | 0; // get asm.js like optimization under V8
|
||||
if (num < 32) {
|
||||
return control | 0;
|
||||
}
|
||||
if (num < 127) {
|
||||
return 1;
|
||||
}
|
||||
const t = table || initTable();
|
||||
if (num < 65536) {
|
||||
return t[num >> 4] >> ((num & 15) << 1) & 3;
|
||||
}
|
||||
// do a full search for high codepoints
|
||||
return wcwidthHigh(num);
|
||||
};
|
||||
})({nul: 0, control: 0}); // configurable options
|
||||
|
||||
// test full BMP range old vs new implmenetation
|
||||
for (let i = 0; i < 65536; ++i) {
|
||||
assert.equal(wcwidth(i), wcwidthOld(i), `mismatch for i: ${i}`);
|
||||
}
|
||||
});
|
||||
|
||||
+144
-166
@@ -3,171 +3,139 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { fill } from './core/TypedArrayUtils';
|
||||
|
||||
export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING_BMP = [
|
||||
[0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
|
||||
[0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
|
||||
[0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],
|
||||
[0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],
|
||||
[0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],
|
||||
[0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],
|
||||
[0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],
|
||||
[0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],
|
||||
[0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],
|
||||
[0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],
|
||||
[0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
|
||||
[0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
|
||||
[0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],
|
||||
[0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],
|
||||
[0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
|
||||
[0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],
|
||||
[0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],
|
||||
[0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
|
||||
[0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],
|
||||
[0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],
|
||||
[0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],
|
||||
[0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],
|
||||
[0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],
|
||||
[0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],
|
||||
[0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],
|
||||
[0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],
|
||||
[0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],
|
||||
[0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],
|
||||
[0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],
|
||||
[0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],
|
||||
[0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],
|
||||
[0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],
|
||||
[0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],
|
||||
[0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],
|
||||
[0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],
|
||||
[0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],
|
||||
[0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],
|
||||
[0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],
|
||||
[0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],
|
||||
[0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
|
||||
[0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
|
||||
[0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
|
||||
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB]
|
||||
];
|
||||
const COMBINING_HIGH = [
|
||||
[0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
|
||||
[0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],
|
||||
[0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
|
||||
[0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
|
||||
[0xE0100, 0xE01EF]
|
||||
];
|
||||
// binary search
|
||||
function bisearch(ucs: number, data: number[][]): boolean {
|
||||
let min = 0;
|
||||
let max = data.length - 1;
|
||||
let mid;
|
||||
if (ucs < data[0][0] || ucs > data[max][1]) {
|
||||
return false;
|
||||
}
|
||||
while (max >= min) {
|
||||
mid = (min + max) >> 1;
|
||||
if (ucs > data[mid][1]) {
|
||||
min = mid + 1;
|
||||
} else if (ucs < data[mid][0]) {
|
||||
max = mid - 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING_BMP = [
|
||||
[0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
|
||||
[0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
|
||||
[0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],
|
||||
[0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],
|
||||
[0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],
|
||||
[0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],
|
||||
[0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],
|
||||
[0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],
|
||||
[0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],
|
||||
[0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],
|
||||
[0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
|
||||
[0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
|
||||
[0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],
|
||||
[0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],
|
||||
[0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
|
||||
[0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],
|
||||
[0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],
|
||||
[0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
|
||||
[0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],
|
||||
[0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],
|
||||
[0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],
|
||||
[0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],
|
||||
[0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],
|
||||
[0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],
|
||||
[0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],
|
||||
[0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],
|
||||
[0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],
|
||||
[0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],
|
||||
[0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],
|
||||
[0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],
|
||||
[0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],
|
||||
[0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],
|
||||
[0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],
|
||||
[0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],
|
||||
[0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],
|
||||
[0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],
|
||||
[0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],
|
||||
[0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],
|
||||
[0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],
|
||||
[0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
|
||||
[0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
|
||||
[0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
|
||||
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB]
|
||||
];
|
||||
const COMBINING_HIGH = [
|
||||
[0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
|
||||
[0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],
|
||||
[0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
|
||||
[0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
|
||||
[0xE0100, 0xE01EF]
|
||||
];
|
||||
// binary search
|
||||
function bisearch(ucs: number, data: number[][]): boolean {
|
||||
let min = 0;
|
||||
let max = data.length - 1;
|
||||
let mid;
|
||||
if (ucs < data[0][0] || ucs > data[max][1]) {
|
||||
return false;
|
||||
}
|
||||
function wcwidthBMP(ucs: number): number {
|
||||
// test for 8-bit control characters
|
||||
if (ucs === 0) {
|
||||
return opts.nul;
|
||||
}
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) {
|
||||
return opts.control;
|
||||
}
|
||||
// binary search in table of non-spacing characters
|
||||
if (bisearch(ucs, COMBINING_BMP)) {
|
||||
return 0;
|
||||
}
|
||||
// if we arrive here, ucs is not a combining or C0/C1 control character
|
||||
if (isWideBMP(ucs)) {
|
||||
return 2;
|
||||
while (max >= min) {
|
||||
mid = (min + max) >> 1;
|
||||
if (ucs > data[mid][1]) {
|
||||
min = mid + 1;
|
||||
} else if (ucs < data[mid][0]) {
|
||||
max = mid - 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function wcwidthHigh(ucs: number): 0 | 1 | 2 {
|
||||
if (bisearch(ucs, COMBINING_HIGH)) {
|
||||
return 0;
|
||||
}
|
||||
if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
const control = opts.control | 0;
|
||||
|
||||
// create lookup table for BMP plane
|
||||
const table = new Uint8Array(65536);
|
||||
fill(table, 1);
|
||||
table[0] = opts.nul;
|
||||
// control chars
|
||||
fill(table, opts.control, 1, 32);
|
||||
fill(table, opts.control, 0x7f, 0xa0);
|
||||
|
||||
// apply wide char rules first
|
||||
// wide chars
|
||||
fill(table, 2, 0x1100, 0x1160);
|
||||
table[0x2329] = 2;
|
||||
table[0x232a] = 2;
|
||||
fill(table, 2, 0x2e80, 0xa4d0);
|
||||
table[0x303f] = 1; // wrongly in last line
|
||||
|
||||
fill(table, 2, 0xac00, 0xd7a4);
|
||||
fill(table, 2, 0xf900, 0xfb00);
|
||||
fill(table, 2, 0xfe10, 0xfe1a);
|
||||
fill(table, 2, 0xfe30, 0xfe70);
|
||||
fill(table, 2, 0xff00, 0xff61);
|
||||
fill(table, 2, 0xffe0, 0xffe7);
|
||||
|
||||
// apply combining last to ensure we overwrite
|
||||
// wrongly wide set chars:
|
||||
// the original algo evals combining first and falls
|
||||
// through to wide check so we simply do here the opposite
|
||||
// combining 0
|
||||
for (let r = 0; r < COMBINING_BMP.length; ++r) {
|
||||
fill(table, 0, COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1);
|
||||
}
|
||||
|
||||
return function (num: number): number {
|
||||
if (num < 32) {
|
||||
return control | 0;
|
||||
}
|
||||
if (num < 127) {
|
||||
return 1;
|
||||
}
|
||||
function isWideBMP(ucs: number): boolean {
|
||||
return (
|
||||
ucs >= 0x1100 && (
|
||||
ucs <= 0x115f || // Hangul Jamo init. consonants
|
||||
ucs === 0x2329 ||
|
||||
ucs === 0x232a ||
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
|
||||
(ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs
|
||||
(ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6)));
|
||||
if (num < 65536) {
|
||||
return table[num];
|
||||
}
|
||||
function wcwidthHigh(ucs: number): 0 | 1 | 2 {
|
||||
if (bisearch(ucs, COMBINING_HIGH)) {
|
||||
return 0;
|
||||
}
|
||||
if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
const control = opts.control | 0;
|
||||
let table: number[] | Uint32Array = null;
|
||||
function initTable(): number[] | Uint32Array {
|
||||
// lookup table for BMP
|
||||
const CODEPOINTS = 65536; // BMP holds 65536 codepoints
|
||||
const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2
|
||||
const ITEMSIZE = 32; // using uint32_t
|
||||
const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE;
|
||||
const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH;
|
||||
table = (typeof Uint32Array === 'undefined')
|
||||
? new Array(CONTAINERSIZE)
|
||||
: new Uint32Array(CONTAINERSIZE);
|
||||
for (let i = 0; i < CONTAINERSIZE; ++i) {
|
||||
let num = 0;
|
||||
let pos = CODEPOINTS_PER_ITEM;
|
||||
while (pos--) {
|
||||
num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos);
|
||||
}
|
||||
table[i] = num;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
// get width from lookup table
|
||||
// position in container : num / CODEPOINTS_PER_ITEM
|
||||
// ==> n = table[Math.floor(num / 16)]
|
||||
// ==> n = table[num >> 4]
|
||||
// 16 codepoints per number: FFEEDDCCBBAA99887766554433221100
|
||||
// position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH
|
||||
// ==> m = (n % 16) * 2
|
||||
// ==> m = (num & 15) << 1
|
||||
// right shift to position m
|
||||
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
// we are only interested in 2 LSBs, cut off higher bits
|
||||
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
return function (num: number): number {
|
||||
num = num | 0; // get asm.js like optimization under V8
|
||||
if (num < 32) {
|
||||
return control | 0;
|
||||
}
|
||||
if (num < 127) {
|
||||
return 1;
|
||||
}
|
||||
const t = table || initTable();
|
||||
if (num < 65536) {
|
||||
return t[num >> 4] >> ((num & 15) << 1) & 3;
|
||||
}
|
||||
// do a full search for high codepoints
|
||||
return wcwidthHigh(num);
|
||||
};
|
||||
// do a full search for high codepoints
|
||||
return wcwidthHigh(num);
|
||||
};
|
||||
})({nul: 0, control: 0}); // configurable options
|
||||
|
||||
/**
|
||||
@@ -175,17 +143,27 @@ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: nu
|
||||
*/
|
||||
export function getStringCellWidth(s: string): number {
|
||||
let result = 0;
|
||||
for (let i = 0; i < s.length; ++i) {
|
||||
const length = s.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let code = s.charCodeAt(i);
|
||||
// surrogate pair first
|
||||
if (0xD800 <= code && code <= 0xDBFF) {
|
||||
const low = s.charCodeAt(i + 1);
|
||||
if (isNaN(low)) {
|
||||
return result;
|
||||
if (++i >= length) {
|
||||
// this should not happen with strings retrieved from
|
||||
// Buffer.translateToString as it converts from UTF-32
|
||||
// and therefore always should contain the second part
|
||||
// for any other string we still have to handle it somehow:
|
||||
// simply treat the lonely surrogate first as a single char (UCS-2 behavior)
|
||||
return result + wcwidth(code);
|
||||
}
|
||||
const second = s.charCodeAt(i);
|
||||
// convert surrogate pair to high codepoint only for valid second part (UTF-16)
|
||||
// otherwise treat them independently (UCS-2 behavior)
|
||||
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||
code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||
} else {
|
||||
result += wcwidth(second);
|
||||
}
|
||||
code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
|
||||
}
|
||||
if (0xDC00 <= code && code <= 0xDFFF) {
|
||||
continue;
|
||||
}
|
||||
result += wcwidth(code);
|
||||
}
|
||||
|
||||
+22
-18
@@ -113,7 +113,7 @@ class DECRQSS implements IDcsHandler {
|
||||
* each function's header comment.
|
||||
*/
|
||||
export class InputHandler extends Disposable implements IInputHandler {
|
||||
private _surrogateHigh: string;
|
||||
private _surrogateFirst: string;
|
||||
|
||||
constructor(
|
||||
protected _terminal: IInputHandlingTerminal,
|
||||
@@ -123,7 +123,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
this.register(this._parser);
|
||||
|
||||
this._surrogateHigh = '';
|
||||
this._surrogateFirst = '';
|
||||
|
||||
/**
|
||||
* custom fallback handlers
|
||||
@@ -311,9 +311,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// apply leftover surrogate high from last write
|
||||
if (this._surrogateHigh) {
|
||||
data = this._surrogateHigh + data;
|
||||
this._surrogateHigh = '';
|
||||
if (this._surrogateFirst) {
|
||||
data = this._surrogateFirst + data;
|
||||
this._surrogateFirst = '';
|
||||
}
|
||||
|
||||
this._parser.parse(data);
|
||||
@@ -327,7 +327,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public print(data: string, start: number, end: number): void {
|
||||
let char: string;
|
||||
let code: number;
|
||||
let low: number;
|
||||
let chWidth: number;
|
||||
const buffer: IBuffer = this._terminal.buffer;
|
||||
const charset: ICharset = this._terminal.charset;
|
||||
@@ -345,20 +344,25 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
// surrogate pair handling
|
||||
if (0xD800 <= code && code <= 0xDBFF) {
|
||||
// we got a surrogate high
|
||||
// get surrogate low (next 2 bytes)
|
||||
low = data.charCodeAt(stringPosition + 1);
|
||||
if (isNaN(low)) {
|
||||
// end of data stream, save surrogate high
|
||||
this._surrogateHigh = char;
|
||||
if (++stringPosition >= end) {
|
||||
// end of input:
|
||||
// handle pairs as true UTF-16 and wait for the second part
|
||||
// since we expect the input comming from a stream there is
|
||||
// a small chance that the surrogate pair got split
|
||||
// therefore we dont process the first char here, instead
|
||||
// it gets added as first char to the next processed chunk
|
||||
this._surrogateFirst = char;
|
||||
continue;
|
||||
}
|
||||
code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
|
||||
char += data.charAt(stringPosition + 1);
|
||||
}
|
||||
// surrogate low - already handled above
|
||||
if (0xDC00 <= code && code <= 0xDFFF) {
|
||||
continue;
|
||||
const second = data.charCodeAt(stringPosition);
|
||||
// if the second part is in surrogate pair range create the high codepoint
|
||||
// otherwise fall back to UCS-2 behavior (handle codepoints independently)
|
||||
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||
code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||
char += data.charAt(stringPosition);
|
||||
} else {
|
||||
stringPosition--;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate print space
|
||||
|
||||
@@ -30,6 +30,7 @@ class TestSelectionManager extends SelectionManager {
|
||||
|
||||
public selectLineAt(line: number): void { this._selectLineAt(line); }
|
||||
public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); }
|
||||
public areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean { return this._areCoordsInSelection(coords, start, end); }
|
||||
|
||||
// Disable DOM interaction
|
||||
public enable(): void {}
|
||||
@@ -478,5 +479,17 @@ describe('SelectionManager', () => {
|
||||
assert.equal(selectionManager.selectionText, 'a\n😁\nc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_areCoordsInSelection', () => {
|
||||
it('should return whether coords are in the selection', () => {
|
||||
assert.isFalse(selectionManager.areCoordsInSelection([0, 0], [2, 0], [2, 1]));
|
||||
assert.isFalse(selectionManager.areCoordsInSelection([1, 0], [2, 0], [2, 1]));
|
||||
assert.isTrue(selectionManager.areCoordsInSelection([2, 0], [2, 0], [2, 1]));
|
||||
assert.isTrue(selectionManager.areCoordsInSelection([10, 0], [2, 0], [2, 1]));
|
||||
assert.isTrue(selectionManager.areCoordsInSelection([0, 1], [2, 0], [2, 1]));
|
||||
assert.isTrue(selectionManager.areCoordsInSelection([1, 1], [2, 0], [2, 1]));
|
||||
assert.isFalse(selectionManager.areCoordsInSelection([2, 1], [2, 0], [2, 1]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -289,9 +289,14 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._areCoordsInSelection(coords, start, end);
|
||||
}
|
||||
|
||||
protected _areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean {
|
||||
return (coords[1] > start[1] && coords[1] < end[1]) ||
|
||||
(start[1] === end[1] && coords[1] === start[1] && coords[0] > start[0] && coords[0] < end[0]) ||
|
||||
(start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]);
|
||||
(start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) ||
|
||||
(start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]) ||
|
||||
(start[1] < end[1] && coords[1] === start[1] && coords[0] >= start[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+22
-16
@@ -12,7 +12,19 @@ import { ITerminal, ISoundManager } from './Types';
|
||||
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
|
||||
|
||||
export class SoundManager implements ISoundManager {
|
||||
private _audioContext: AudioContext;
|
||||
private static _audioContext: AudioContext;
|
||||
|
||||
static get audioContext(): AudioContext | null {
|
||||
if (!SoundManager._audioContext) {
|
||||
const audioContextCtor: typeof AudioContext = (<any>window).AudioContext || (<any>window).webkitAudioContext;
|
||||
if (!audioContextCtor) {
|
||||
console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version');
|
||||
return null;
|
||||
}
|
||||
SoundManager._audioContext = new audioContextCtor();
|
||||
}
|
||||
return SoundManager._audioContext;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal
|
||||
@@ -20,22 +32,16 @@ export class SoundManager implements ISoundManager {
|
||||
}
|
||||
|
||||
public playBellSound(): void {
|
||||
const audioContextCtor: typeof AudioContext = (<any>window).AudioContext || (<any>window).webkitAudioContext;
|
||||
if (!this._audioContext && audioContextCtor) {
|
||||
this._audioContext = new audioContextCtor();
|
||||
}
|
||||
|
||||
if (this._audioContext) {
|
||||
const bellAudioSource = this._audioContext.createBufferSource();
|
||||
const context = this._audioContext;
|
||||
this._audioContext.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => {
|
||||
bellAudioSource.buffer = buffer;
|
||||
bellAudioSource.connect(context.destination);
|
||||
bellAudioSource.start(0);
|
||||
});
|
||||
} else {
|
||||
console.warn('Sorry, but the Web Audio API is not supported by your browser. Please, consider upgrading to the latest version');
|
||||
const ctx = SoundManager.audioContext;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
const bellAudioSource = ctx.createBufferSource();
|
||||
ctx.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => {
|
||||
bellAudioSource.buffer = buffer;
|
||||
bellAudioSource.connect(ctx.destination);
|
||||
bellAudioSource.start(0);
|
||||
});
|
||||
}
|
||||
|
||||
private _base64ToArrayBuffer(base64: string): ArrayBuffer {
|
||||
|
||||
+32
-8
@@ -21,11 +21,11 @@
|
||||
* http://linux.die.net/man/7/urxvt
|
||||
*/
|
||||
|
||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler } from './Types';
|
||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types';
|
||||
import { IMouseZoneManager } from './ui/Types';
|
||||
import { IRenderer } from './renderer/Types';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer';
|
||||
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_ATTR_INDEX } from './Buffer';
|
||||
import { CompositionHelper } from './CompositionHelper';
|
||||
import { EventEmitter } from './common/EventEmitter';
|
||||
import { Viewport } from './Viewport';
|
||||
@@ -207,6 +207,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
private _theme: ITheme;
|
||||
|
||||
// bufferline to clone/copy from for new blank lines
|
||||
private _blankLine: IBufferLine = null;
|
||||
|
||||
public cols: number;
|
||||
public rows: number;
|
||||
|
||||
@@ -496,6 +499,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
case 'experimentalBufferLineImpl':
|
||||
this.buffers.normal.setBufferLineFactory(value);
|
||||
this.buffers.alt.setBufferLineFactory(value);
|
||||
this._blankLine = null;
|
||||
break;
|
||||
}
|
||||
// Inform renderer of changes
|
||||
@@ -1173,20 +1177,40 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
* Scroll the terminal down 1 row, creating a blank line.
|
||||
* @param isWrapped Whether the new line is wrapped from the previous line.
|
||||
*/
|
||||
public scroll(isWrapped?: boolean): void {
|
||||
const newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
|
||||
public scroll(isWrapped: boolean = false): void {
|
||||
let newLine: IBufferLine;
|
||||
const useRecycling = this.options.experimentalBufferLineImpl === 'TypedArray';
|
||||
if (useRecycling) {
|
||||
newLine = this._blankLine;
|
||||
if (!newLine || newLine.length !== this.cols || newLine.get(0)[CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) {
|
||||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
|
||||
this._blankLine = newLine;
|
||||
}
|
||||
newLine.isWrapped = isWrapped;
|
||||
} else {
|
||||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
|
||||
}
|
||||
|
||||
const topRow = this.buffer.ybase + this.buffer.scrollTop;
|
||||
const bottomRow = this.buffer.ybase + this.buffer.scrollBottom;
|
||||
|
||||
if (this.buffer.scrollTop === 0) {
|
||||
// Determine whether the buffer is going to be trimmed after insertion.
|
||||
const willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength;
|
||||
const willBufferBeTrimmed = this.buffer.lines.isFull;
|
||||
|
||||
// Insert the line using the fastest method
|
||||
if (bottomRow === this.buffer.lines.length - 1) {
|
||||
this.buffer.lines.push(newLine);
|
||||
if (useRecycling) {
|
||||
if (willBufferBeTrimmed) {
|
||||
this.buffer.lines.recycle().copyFrom(newLine);
|
||||
} else {
|
||||
this.buffer.lines.push(newLine.clone());
|
||||
}
|
||||
} else {
|
||||
this.buffer.lines.push(newLine);
|
||||
}
|
||||
} else {
|
||||
this.buffer.lines.splice(bottomRow + 1, 0, newLine);
|
||||
this.buffer.lines.splice(bottomRow + 1, 0, (useRecycling) ? newLine.clone() : newLine);
|
||||
}
|
||||
|
||||
// Only adjust ybase and ydisp when the buffer is not trimmed
|
||||
@@ -1208,7 +1232,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
// scrollback, instead we can just shift them in-place.
|
||||
const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/;
|
||||
this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
|
||||
this.buffer.lines.set(bottomRow, newLine);
|
||||
this.buffer.lines.set(bottomRow, (useRecycling) ? newLine.clone() : newLine);
|
||||
}
|
||||
|
||||
// Move the viewport to the bottom of the buffer unless the user is
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as attach from './attach';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as fit from './fit';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as fullscreen from './fullscreen';
|
||||
|
||||
|
||||
@@ -11,17 +11,18 @@ import { Terminal } from 'xterm';
|
||||
* @param fullscreen Toggle fullscreen on (true) or off (false)
|
||||
*/
|
||||
export function toggleFullScreen(term: Terminal, fullscreen: boolean): void {
|
||||
let fn: string;
|
||||
let fn: (...tokens: string[]) => void;
|
||||
|
||||
if (typeof fullscreen === 'undefined') {
|
||||
fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add';
|
||||
fn = (term.element.classList.contains('fullscreen')) ?
|
||||
term.element.classList.remove : term.element.classList.add;
|
||||
} else if (!fullscreen) {
|
||||
fn = 'remove';
|
||||
fn = term.element.classList.remove;
|
||||
} else {
|
||||
fn = 'add';
|
||||
fn = term.element.classList.add;
|
||||
}
|
||||
|
||||
term.element.classList[fn]('fullscreen');
|
||||
fn('fullscreen');
|
||||
}
|
||||
|
||||
export function apply(terminalConstructor: typeof Terminal): void {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
declare var require: any;
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import * as search from './search';
|
||||
|
||||
@@ -18,8 +18,5 @@
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.test.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as terminado from './terminado';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as webLinks from './webLinks';
|
||||
|
||||
@@ -39,4 +39,28 @@ describe('webLinks addon', () => {
|
||||
|
||||
assert.equal(uri, 'http://foo.com/a~b#c~d?e~f');
|
||||
});
|
||||
|
||||
it('should allow : character in URI path', () => {
|
||||
const term = new MockTerminal();
|
||||
webLinks.webLinksInit(<any>term);
|
||||
|
||||
const row = ' http://foo.com/colon:test ';
|
||||
|
||||
const match = row.match(term.regex);
|
||||
const uri = match[term.options.matchIndex];
|
||||
|
||||
assert.equal(uri, 'http://foo.com/colon:test');
|
||||
});
|
||||
|
||||
it('should not allow : character at the end of a URI path', () => {
|
||||
const term = new MockTerminal();
|
||||
webLinks.webLinksInit(<any>term);
|
||||
|
||||
const row = ' http://foo.com/colon:test: ';
|
||||
|
||||
const match = row.match(term.regex);
|
||||
const uri = match[term.options.matchIndex];
|
||||
|
||||
assert.equal(uri, 'http://foo.com/colon:test');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ 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\\.\\-%~]*)*';
|
||||
const pathClause = '(\\/[\\/\\w\\.\\-%~:]*)*([^:\\s])';
|
||||
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
|
||||
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
||||
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as winptyCompat from './winptyCompat';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import * as zmodem from './zmodem';
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user