Merge branch 'master' into master

This commit is contained in:
Charles Wei
2019-07-16 20:03:57 +08:00
committed by GitHub
61 changed files with 3117 additions and 1966 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ export function createProgram(gl: WebGLRenderingContext, vertexSource: string, f
return program;
}
console.log(gl.getProgramInfoLog(program));
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
@@ -37,7 +37,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st
return shader;
}
console.log(gl.getShaderInfoLog(shader));
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
@@ -3,12 +3,12 @@
* @license MIT
*/
import { ILinkifierEvent, ILinkifierAccessor } from '../../../../src/Types';
import { ILinkifierAccessor } from '../../../../src/Types';
import { Terminal } from 'xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { is256Color } from '../atlas/CharAtlasUtils';
import { IColorSet } from 'browser/Types';
import { IColorSet, ILinkifierEvent } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
export class LinkRenderLayer extends BaseRenderLayer {
@@ -45,9 +45,9 @@ export class LinkRenderLayer extends BaseRenderLayer {
private _onLinkHover(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._colors.background.css;
} else if (is256Color(e.fg)) {
} else if (e.fg !== undefined && is256Color(e.fg)) {
// 256 color support
this._ctx.fillStyle = this._colors.ansi[e.fg].css;
this._ctx.fillStyle = this._colors.ansi[e.fg!].css;
} else {
this._ctx.fillStyle = this._colors.foreground.css;
}
+1 -1
View File
@@ -223,7 +223,6 @@ function initOptions(term: TerminalType): void {
// Internal only options
'cancelEvents',
'convertEol',
'debug',
'handler',
'screenKeys',
'termName',
@@ -238,6 +237,7 @@ function initOptions(term: TerminalType): void {
fontFamily: null,
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
logLevel: ['debug', 'info', 'warn', 'error', 'off'],
rendererType: ['dom', 'canvas'],
wordSeparator: null
};
@@ -0,0 +1,73 @@
from glob import glob
import os
import sys
import termios
import atexit
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
def enable_echo(fd, enabled):
(iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(fd)
if enabled:
lflag |= termios.ECHO
else:
lflag &= ~termios.ECHO
new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
termios.tcsetattr(fd, termios.TCSANOW, new_attr)
atexit.register(enable_echo, sys.stdin.fileno(), True)
output = []
def log(append=False, *s):
if append:
output[-1] += ' ' + ' '.join(str(part) for part in s)
else:
output.append(' '.join(str(part) for part in s))
def reset_terminal():
sys.stdout.write('\x1bc\x1b[H')
sys.stdout.flush()
def test():
count = 0
passed = 0
for i, testfile in enumerate(sorted(glob(os.path.join(BASE_DIR, '*.in')))):
count += 1
log(False, os.path.basename(testfile))
reset_terminal()
with open(testfile) as test:
sys.stdout.write('\x1b]0;%s\x07' % os.path.basename(testfile))
sys.stdout.write(test.read()+'\x1bt')
sys.stdout.flush()
with open(os.path.join(os.path.dirname(testfile),
os.path.basename(testfile).split('.')[0]+'.text')) as expected:
terminal_output = sys.stdin.read()
if not terminal_output:
# we are in xterm
continue
if terminal_output != expected.read():
log(True, '\x1b[31merror\x1b[0m')
with open(os.path.join(os.path.dirname(testfile), 'output',
os.path.basename(testfile)), 'w') as t_out:
t_out.write(terminal_output)
else:
passed += 1
log(True, '\x1b[32mpass\x1b[0m')
return count, passed
if __name__ == '__main__':
enable_echo(sys.stdin.fileno(), False)
count, passed = test()
enable_echo(sys.stdin.fileno(), True)
reset_terminal()
for i in range(len(output)/2+1):
if not (i+1) % 25:
sys.stdin.read()
print ''.join(i.ljust(40) for i in output[i*2:i*2+2])
print '\x1b[33mcoverage: %s/%s (%d%%) tests passed.\x1b[0m' % (passed, count, passed*100/count)
@@ -1,24 +1,25 @@
a
b
c
d
f
g
h
i
b
c
d
f
g
h
i
j
k
l
m
j
k
l
m
n
o
p
q
r
s
w
x
n
o
p
q
r
s
w
x
@@ -1,3 +1,4 @@
6 C
8 ^^^^
9 vvvv DL on line 11, expected: ACD_
10 A
@@ -12,14 +13,14 @@
19 vvvv IL on line 21, expected: A_
20 A
22 ^^^^
23 vvvv IL on line 24, expected: _A
24 A
25 B
26 ^^^^
28 A
27 vvvv DL on line 28, expected: B_
28 A
29 B
30 ^^^^
31
32
32
@@ -1,25 +1,25 @@
n
o
p
q
r
s
t
u
v
w
x
y
z
1
2
3
4
5
6
7
8
9
10
o
p
q
r
s
t
u
v
w
x
y
z
1
2
3
4
5
6
7
8
9
10
11
11
@@ -2,7 +2,7 @@
efgh
-------- set: wraparound ----------------------------------------------abcd
efgh
-------- unset: no wraparound -------------------------------------------abcd
-------- unset: no wraparound -------------------------------------------abch
this should be immediately below "no wraparound"
@@ -0,0 +1,35 @@
Test of autowrap, mixing control and print characters.
The left/right margins should have letters in order:
[?6hAa
aBB b
C cC

DdEe
eFF f
G gG

HhIi
iJJ j
K kK

LlMm
mNN n
O oO

PpQq
qRR r
S sS

TtUu
uVV v
W wW

XxYy
yZZ z
[?6lPush <RETURN>
@@ -0,0 +1,25 @@
Test of autowrap, mixing control and print characters.
I i
J j
K k
L l
M m
N n
O o
P p
Q q
R r
S s
T t
U u
V v
W w
X x
Y y
Z z
Push <RETURN>
+6 -6
View File
@@ -11,7 +11,7 @@
"prepackage": "npm run build",
"package": "webpack",
"start": "node demo/start",
"lint": "tslint 'src/**/*.ts' './demo/**/*.ts' './addons/**/*.ts'",
"lint": "tslint 'src/**/*.ts' 'addons/**/*.ts'",
"test": "npm run test-unit",
"posttest": "npm run lint",
"test-api": "mocha \"**/*.api.js\"",
@@ -36,20 +36,20 @@
"@types/webpack": "^4.4.11",
"@types/ws": "^6.0.1",
"chai": "3.5.0",
"express": "4.13.4",
"express-ws": "2.0.0-rc.1",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"glob": "^7.0.5",
"jsdom": "^11.11.0",
"mocha": "^6.1.4",
"node-pty": "0.7.6",
"puppeteer": "^1.15.0",
"source-map-loader": "^0.2.4",
"ts-loader": "^4.5.0",
"tslint": "^5.9.1",
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"tslint-consistent-codestyle": "^1.13.0",
"typescript": "3.5",
"utf8": "^3.0.0",
"webpack": "^4.17.1",
"webpack": "^4.35.3",
"webpack-cli": "^3.1.0",
"ws": "^7.0.0",
"xterm-benchmark": "^0.1.3"
+6 -10
View File
@@ -11,7 +11,7 @@ import { RenderDebouncer } from 'browser/RenderDebouncer';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { IRenderDimensions } from 'browser/renderer/Types';
import { IRenderService } from 'browser/services/Services';
const MAX_ROWS_TO_READ = 20;
@@ -47,8 +47,8 @@ export class AccessibilityManager extends Disposable {
private _charsToAnnounce: string = '';
constructor(
private _terminal: ITerminal,
private _dimensions: IRenderDimensions
private readonly _terminal: ITerminal,
private readonly _renderService: IRenderService
) {
super();
this._accessibilityTreeRoot = document.createElement('div');
@@ -90,6 +90,7 @@ export class AccessibilityManager extends Disposable {
this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount)));
this.register(this._terminal.onKey(e => this._onKey(e.key)));
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
this._screenDprMonitor = new ScreenDprMonitor();
this.register(this._screenDprMonitor);
@@ -271,7 +272,7 @@ export class AccessibilityManager extends Disposable {
}
private _refreshRowsDimensions(): void {
if (!this._dimensions.actualCellHeight) {
if (!this._renderService.dimensions.actualCellHeight) {
return;
}
if (this._rowElements.length !== this._terminal.rows) {
@@ -282,13 +283,8 @@ export class AccessibilityManager extends Disposable {
}
}
public setDimensions(dimensions: IRenderDimensions): void {
this._dimensions = dimensions;
this._refreshRowsDimensions();
}
private _refreshRowDimensions(element: HTMLElement): void {
element.style.height = `${this._dimensions.actualCellHeight}px`;
element.style.height = `${this._renderService.dimensions.actualCellHeight}px`;
}
private _announceCharacters(): void {
+670 -174
View File
File diff suppressed because it is too large Load Diff
+387 -337
View File
File diff suppressed because it is too large Load Diff
+132 -9
View File
@@ -4,19 +4,18 @@
*/
import { assert, expect } from 'chai';
import { Terminal } from './Terminal';
import { MockViewport, MockCompositionHelper, MockRenderer } from './TestUtils.test';
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { wcwidth } from 'common/CharWidth';
import { IBufferService } from 'common/services/Services';
import { Linkifier } from 'browser/Linkifier';
import { MockLogService } from 'common/TestUtils.test';
import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types';
const INIT_COLS = 80;
const INIT_ROWS = 24;
class TestTerminal extends Terminal {
public keyDown(ev: any): boolean { return this._keyDown(ev); }
public keyPress(ev: any): boolean { return this._keyPress(ev); }
}
describe('Terminal', () => {
let term: TestTerminal;
const termOptions = {
@@ -750,10 +749,14 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.buffer.x = term.cols - 1;
term.wraparoundMode = false;
const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000);
if (width !== 1) {
continue;
}
term.write('a' + high + String.fromCharCode(i));
// auto wraparound mode should cut off the rest of the line
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(1);
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1).loadCell(1, cell).getChars()).eql('');
term.reset();
}
@@ -1019,4 +1022,124 @@ describe('Terminal', () => {
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
});
});
describe('Linkifier unicode handling', () => {
let terminal: TestTerminal;
let linkifier: TestLinkifier;
let mouseZoneManager: TestMouseZoneManager;
// other than the tests above unicode testing needs the full terminal instance
// to get the special handling of fullwidth, surrogate and combining chars in the input handler
beforeEach(() => {
terminal = new TestTerminal({ cols: 10, rows: 5 });
linkifier = new TestLinkifier((terminal as any)._bufferService);
mouseZoneManager = new TestMouseZoneManager();
linkifier.attachToDom({} as any, mouseZoneManager);
});
function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void {
terminal.writeSync(rowText);
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
linkifier.linkifyRows();
// Allow linkify to happen
setTimeout(() => {
assert.equal(mouseZoneManager.zones.length, links.length);
links.forEach((l, i) => {
assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1);
assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1);
assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1);
assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1);
});
done();
}, 0);
}
describe('unicode before the match', () => {
it('combining - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done);
});
it('combining - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done);
});
it('surrogate - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done);
});
it('surrogate - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done);
});
it('combining surrogate - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done);
});
it('combining surrogate - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done);
});
it('fullwidth - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done);
});
it('fullwidth - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done);
});
it('combining fullwidth - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done);
});
it('combining fullwidth - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done);
});
});
describe('unicode within the match', () => {
it('combining - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done);
});
it('combining - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done);
});
it('surrogate - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done);
});
it('surrogate - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done);
});
it('combining surrogate - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done);
});
it('combining surrogate - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done);
});
it('fullwidth - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('test ab', /ab/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done);
});
it('fullwidth - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('testtest ab', /ab/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done);
});
it('combining fullwidth - match within one line', function(done: () => void): void {
assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done);
});
it('combining fullwidth - match over two lines', function(done: () => void): void {
assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done);
});
});
});
});
class TestLinkifier extends Linkifier {
constructor(bufferService: IBufferService) {
super(bufferService, new MockLogService());
Linkifier._timeBeforeLatency = 0;
}
public get linkMatchers(): IRegisteredLinkMatcher[] { return this._linkMatchers; }
public linkifyRows(): void { super.linkifyRows(0, this._bufferService.buffer.lines.length - 1); }
}
class TestMouseZoneManager implements IMouseZoneManager {
dispose(): void {
}
public clears: number = 0;
public zones: IMouseZone[] = [];
add(zone: IMouseZone): void {
this.zones.push(zone);
}
clearAll(): void {
this.clears++;
}
}
+65 -153
View File
@@ -21,21 +21,21 @@
* http://linux.die.net/man/7/urxvt
*/
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, IMouseZoneManager } from './Types';
import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types';
import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
import { CompositionHelper } from './CompositionHelper';
import { Viewport } from './Viewport';
import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './Clipboard';
import { CompositionHelper } from 'browser/input/CompositionHelper';
import { Viewport } from 'browser/Viewport';
import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from 'browser/Clipboard';
import { C0 } from 'common/data/EscapeSequences';
import { InputHandler } from './InputHandler';
import { Renderer } from './renderer/Renderer';
import { Linkifier } from './Linkifier';
import { SelectionService } from './browser/services/SelectionService';
import { Linkifier } from 'browser/Linkifier';
import { SelectionService } from 'browser/services/SelectionService';
import * as Browser from 'common/Platform';
import { addDisposableDomListener } from 'browser/Lifecycle';
import * as Strings from './browser/LocalizableStrings';
import * as Strings from 'browser/LocalizableStrings';
import { SoundService } from 'browser/services/SoundService';
import { MouseZoneManager } from './MouseZoneManager';
import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache';
@@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { applyWindowsMode } from './WindowsMode';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
import { IOptionsService, IBufferService, ICoreService } from 'common/services/Services';
import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services';
import { OptionsService } from 'common/services/OptionsService';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
@@ -58,6 +58,10 @@ import { Attributes } from 'common/buffer/Constants';
import { MouseService } from 'browser/services/MouseService';
import { IParams } from 'common/parser/Types';
import { CoreService } from 'common/services/CoreService';
import { LogService } from 'common/services/LogService';
import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types';
import { DirtyRowService } from 'common/services/DirtyRowService';
import { InstantiationService } from 'common/services/InstantiationService';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
@@ -87,7 +91,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* The HTMLElement that the terminal is created in, set by Terminal.open.
*/
private _parent: HTMLElement;
private _context: Window;
private _document: Document;
private _viewportScrollArea: HTMLElement;
private _viewportElement: HTMLElement;
@@ -110,6 +113,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// common services
private _bufferService: IBufferService;
private _coreService: ICoreService;
private _dirtyRowService: IDirtyRowService;
private _instantiationService: IInstantiationService;
private _logService: ILogService;
public optionsService: IOptionsService;
// browser services
@@ -146,8 +152,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
public urxvtMouse: boolean;
// misc
private _refreshStart: number;
private _refreshEnd: number;
public savedCols: number;
public curAttrData: IAttributeData;
@@ -237,10 +241,18 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
super();
// Setup and initialize common services
this._instantiationService = new InstantiationService();
this.optionsService = new OptionsService(options);
this._bufferService = new BufferService(this.optionsService);
this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService);
this._instantiationService.setService(IOptionsService, this.optionsService);
this._bufferService = this._instantiationService.createInstance(BufferService);
this._instantiationService.setService(IBufferService, this._bufferService);
this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
this._instantiationService.setService(ICoreService, this._coreService);
this._coreService.onData(e => this._onData.fire(e));
this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
this._logService = this._instantiationService.createInstance(LogService);
this._instantiationService.setService(ILogService, this._logService);
this._setupOptionsListeners();
this._setup();
@@ -297,14 +309,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._userScrolling = false;
// Register input handler and refire/handle events
this._inputHandler = new InputHandler(this, this._coreService);
this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._dirtyRowService, this._logService, this.optionsService);
this._inputHandler.onCursorMove(() => this._onCursorMove.fire());
this._inputHandler.onLineFeed(() => this._onLineFeed.fire());
this.register(this._inputHandler);
this._selectionService = this._selectionService || null;
this.linkifier = this.linkifier || new Linkifier(this);
this._mouseZoneManager = this._mouseZoneManager || null;
this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService);
if (this.options.windowsMode) {
this._windowsMode = applyWindowsMode(this);
@@ -384,7 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
case 'screenReaderMode':
if (this.optionsService.options.screenReaderMode) {
if (!this._accessibilityManager && this._renderService) {
this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions);
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
} else {
if (this._accessibilityManager) {
@@ -534,8 +544,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
throw new Error('Terminal requires a parent element.');
}
// Grab global elements
this._context = this._parent.ownerDocument.defaultView;
this._document = this._parent.ownerDocument;
// Create main element container
@@ -577,11 +585,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));
this._helperContainer.appendChild(this.textarea);
this._charSizeService = new CharSizeService(this._document, this._helperContainer, this.optionsService);
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
this._instantiationService.setService(ICharSizeService, this._charSizeService);
this._compositionView = document.createElement('div');
this._compositionView.classList.add('composition-view');
this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this, this._charSizeService, this._coreService);
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
this._helperContainer.appendChild(this._compositionView);
// Performance: Add viewport and helper elements from the fragment
@@ -593,19 +602,21 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._colorManager.setTheme(this._theme);
const renderer = this._createRenderer();
this._renderService = new RenderService(renderer, this.rows, this.screenElement, this.optionsService, this._charSizeService);
this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement);
this._instantiationService.setService(IRenderService, this._renderService);
this._renderService.onRender(e => this._onRender.fire(e));
this.onResize(e => this._renderService.resize(e.cols, e.rows));
this._soundService = new SoundService(this.optionsService);
this._mouseService = new MouseService(this._renderService, this._charSizeService);
this._soundService = this._instantiationService.createInstance(SoundService);
this._instantiationService.setService(ISoundService, this._soundService);
this._mouseService = this._instantiationService.createInstance(MouseService);
this._instantiationService.setService(IMouseService, this._mouseService);
this._mouseZoneManager = new MouseZoneManager(this, this._mouseService);
this.register(this._mouseZoneManager);
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
this.linkifier.attachToDom(this._mouseZoneManager);
this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this._renderService.dimensions, this._charSizeService);
this.viewport = this._instantiationService.createInstance(Viewport,
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
this._viewportElement,
this._viewportScrollArea
);
this.viewport.onThemeChange(this._colorManager.colors);
this.register(this.viewport);
@@ -615,11 +626,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.register(this.onFocus(() => this._renderService.onFocus()));
this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea()));
this._selectionService = new SelectionService(
this._selectionService = this._instantiationService.createInstance(SelectionService,
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
this.element, this.screenElement, this._charSizeService, this._bufferService, this._coreService,
this._mouseService, this.optionsService
);
this.element,
this.screenElement);
this._instantiationService.setService(ISelectionService, this._selectionService);
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
@@ -637,6 +648,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}));
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh()));
this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement);
this.register(this._mouseZoneManager);
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
// apply mouse event classes set by escape codes before terminal was attached
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
if (this.mouseEvents) {
@@ -644,12 +660,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
} else {
this._selectionService.enable();
}
this._inputHandler.setBrowserServices(this._selectionService);
if (this.options.screenReaderMode) {
// Note that this must be done *after* the renderer is created in order to
// ensure the correct order of the dprchange event
this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions);
this._accessibilityManager.register(this._renderService.onDimensionsChange(e => this._accessibilityManager.setDimensions(e)));
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
// Measure the character size
@@ -669,8 +685,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _createRenderer(): IRenderer {
switch (this.options.rendererType) {
case 'canvas': return new Renderer(this, this._colorManager.colors, this._charSizeService); break;
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); break;
case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService);
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService);
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
}
@@ -1129,8 +1145,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
// Flag rows that need updating
this.updateRange(this.buffer.scrollTop);
this.updateRange(this.buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(this.buffer.scrollTop, this.buffer.scrollBottom);
this._onScroll.fire(this.buffer.ydisp);
}
@@ -1251,19 +1266,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._xoffSentToCatchUp = false;
}
this._refreshStart = this.buffer.y;
this._refreshEnd = this.buffer.y;
// HACK: Set the parser state based on it's state at the time of return.
// This works around the bug #662 which saw the parser state reset in the
// middle of parsing escape sequence in two chunks. For some reason the
// state of the parser resets to 0 after exiting parser.parse. This change
// just sets the state back based on the correct return statement.
this._inputHandler.parseUtf8(data);
this.updateRange(this.buffer.y);
this.refresh(this._refreshStart, this._refreshEnd);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
@@ -1338,19 +1343,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._xoffSentToCatchUp = false;
}
this._refreshStart = this.buffer.y;
this._refreshEnd = this.buffer.y;
// HACK: Set the parser state based on it's state at the time of return.
// This works around the bug #662 which saw the parser state reset in the
// middle of parsing escape sequence in two chunks. For some reason the
// state of the parser resets to 0 after exiting parser.parse. This change
// just sets the state back based on the correct return statement.
this._inputHandler.parse(data);
this.updateRange(this.buffer.y);
this.refresh(this._refreshStart, this._refreshEnd);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
@@ -1675,24 +1670,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
/**
* Log the current state to the console.
*/
public log(text: string, data?: any): void {
if (!this.options.debug) return;
if (!this._context.console || !this._context.console.log) return;
this._context.console.log(text, data);
}
/**
* Log the current state as error to the console.
*/
public error(text: string, data?: any): void {
if (!this.options.debug) return;
if (!this._context.console || !this._context.console.error) return;
this._context.console.error(text, data);
}
/**
* Resizes the terminal.
*
@@ -1728,29 +1705,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._onResize.fire({ cols: x, rows: y });
}
/**
* Updates the range of rows to refresh
* @param y The number of rows to refresh next.
*/
public updateRange(y: number): void {
if (y < this._refreshStart) this._refreshStart = y;
if (y > this._refreshEnd) this._refreshEnd = y;
// if (y > this.refreshEnd) {
// this.refreshEnd = y;
// if (y > this.rows - 1) {
// this.refreshEnd = this.rows - 1;
// }
// }
}
/**
* Set the range of refreshing to the maximum value
*/
public maxRange(): void {
this._refreshStart = 0;
this._refreshEnd = this.rows - 1;
}
/**
* Clear the entire buffer, making the prompt line the new first line.
*/
@@ -1810,46 +1764,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
/**
* ESC
*/
/**
* ESC D Index (IND is 0x84).
*/
public index(): void {
this.buffer.y++;
if (this.buffer.y > this.buffer.scrollBottom) {
this.buffer.y--;
this.scroll();
}
// If the end of the line is hit, prevent this action from wrapping around to the next line.
if (this.buffer.x >= this.cols) {
this.buffer.x--;
}
}
/**
* ESC M Reverse Index (RI is 0x8d).
*
* Move the cursor up one row, inserting a new blank line if necessary.
*/
public reverseIndex(): void {
if (this.buffer.y === this.buffer.scrollTop) {
// possibly move the code below to term.reverseScroll();
// test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
// blankLine(true) is xterm/linux behavior
const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;
this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);
this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttrData()));
this.updateRange(this.buffer.scrollTop);
this.updateRange(this.buffer.scrollBottom);
} else {
this.buffer.y--;
}
}
/**
* ESC c Full Reset (RIS).
* Reset terminal.
* Note: Calling this directly from JS is synchronous but does not clear
* input buffers and does not reset the parser, thus the terminal will
* continue to apply pending input data.
* If you need in band reset (synchronous with input data) consider
* using DECSTR (soft reset, CSI ! p) or RIS instead (hard reset, ESC c).
*/
public reset(): void {
/**
@@ -1891,14 +1811,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
/**
* ESC H Tab Set (HTS is 0x88).
*/
public tabSet(): void {
this.buffer.tabs[this.buffer.x] = true;
}
// TODO: Remove cancel function and cancelEvents option
public cancel(ev: Event, force?: boolean): boolean {
if (!this.options.cancelEvents && !force) {
+92 -112
View File
@@ -1,45 +1,106 @@
/**
* Copyright (c) 2016 The xterm.js authors. All rights reserved.
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* This file contains integration tests for xterm.js.
*/
import * as glob from 'glob';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as pty from 'node-pty';
import { Terminal } from './Terminal';
import { IViewport } from './Types';
import { CellData } from 'common/buffer/CellData';
import { WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
import { IDisposable } from 'xterm';
class TestTerminal extends Terminal {
innerWrite(): void { this._innerWrite(); }
// all test files expect terminal in 80x25
const COLS = 80;
const ROWS = 25;
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')});
const SKIP_FILES = [
't0070-DECSTBM_LF.in', // lineFeed not working correctly
't0071-DECSTBM_IND.in',
't0072-DECSTBM_NEL.in',
't0075-DECSTBM_CUU_CUD.in',
't0076-DECSTBM_IL_DL.in', // not working due to lineFeed
't0077-DECSTBM_quirks.in',
't0084-CBT.in',
't0101-NLM.in',
't0103-reverse_wrap.in',
't0504-vim.in'
];
if (os.platform() === 'darwin') {
// These are failing on macOS only (termios related?)
SKIP_FILES.push(
't0003-line_wrap.in',
't0005-CR.in',
't0009-NEL.in',
't0503-zsh_ls_color.in'
);
}
// filter skipFilenames
const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1);
let primitivePty: any;
// fake sychronous pty write - read
// we just pipe the data from slave to master as a child program would do
// pty.js opens pipe fds with O_NONBLOCK
// just wait 10ms instead of setting fds to blocking mode
function ptyWriteRead(data: string, cb: (result: string) => void): void {
fs.writeSync(primitivePty.slave, data);
setTimeout(() => {
const b = new Buffer(64000);
const bytes = fs.readSync(primitivePty.master, b, 0, 64000, null);
cb(b.toString('utf8', 0, bytes));
describe('Escape Sequence Files', function(): void {
this.timeout(20000);
let ptyTerm: any;
let slaveEnd: any;
let term: Terminal;
let customHandler: IDisposable | undefined;
before(() => {
ptyTerm = (pty as any).open({cols: COLS, rows: ROWS});
slaveEnd = ptyTerm._slave;
term = new Terminal({cols: COLS, rows: ROWS});
ptyTerm._master.on('data', (data: string) => term.write(data));
});
}
// make sure raw pty is at x=0 and has no pending data
function ptyReset(cb: (result: string) => void): void {
ptyWriteRead('\r\n', cb);
}
after(() => {
ptyTerm._master.end();
ptyTerm._master.destroy();
});
FILES.forEach(filename => {
it(filename.split('/').slice(-1)[0], async () => {
// reset terminal and handler
if (customHandler) {
customHandler.dispose();
}
slaveEnd.write('\r\n');
term.reset();
slaveEnd.write('\x1bc\x1b[H');
// register handler to trigger viewport scraping, wait for it to finish
let content = '';
const OSC_CODE = 12345;
await new Promise(resolve => {
customHandler = term.addOscHandler(OSC_CODE, () => {
// grab terminal viewport content
content = terminalToString(term);
resolve();
return true;
});
// write file to slave
slaveEnd.write(fs.readFileSync(filename, 'utf8'));
// trigger custom sequence
slaveEnd.write(`\x1b]${OSC_CODE};\x07`);
});
// compare with expected output (right trimmed)
const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n');
if (content !== expectedRightTrimmed) {
throw new Error(formatError(fs.readFileSync(filename, 'utf8'), content, expected));
}
});
});
});
/**
* Helpers
*/
/* debug helpers */
// generate colorful noisy output to compare xterm and emulator cell states
function formatError(input: string, output: string, expected: string): string {
function addLineNumber(start: number, color: string): (s: string) => string {
@@ -51,10 +112,10 @@ function formatError(input: string, output: string, expected: string): string {
}
const line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890';
let s = '';
s += '\n\x1b[34m' + JSON.stringify(input);
s += '\n\x1b[33m ' + line80 + '\n';
s += `\n\x1b[34m${JSON.stringify(input)}`;
s += `\n\x1b[33m ${line80}\n`;
s += output.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n');
s += '\n\x1b[33m ' + line80 + '\n';
s += `\n\x1b[33m ${line80}\n`;
s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n');
return s;
}
@@ -64,10 +125,7 @@ function terminalToString(term: Terminal): string {
let result = '';
let lineText = '';
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
lineText = '';
for (let cell = 0; cell < term.cols; ++cell) {
lineText += term.buffer.lines.get(line).loadCell(cell, new CellData()).getChars() || WHITESPACE_CELL_CHAR;
}
lineText = term.buffer.lines.get(line).translateToString(true);
// rtrim empty cells as xterm does
lineText = lineText.replace(/\s+$/, '');
result += lineText;
@@ -75,81 +133,3 @@ function terminalToString(term: Terminal): string {
}
return result;
}
// Skip tests on Windows since pty.open isn't supported
if (os.platform() !== 'win32') {
const consoleLog = console.log;
// expect files need terminal at 80x25!
const cols = 80;
const rows = 25;
/** some helpers for pty interaction */
// we need a pty in between to get the termios decorations
// for the basic test cases a raw pty device is enough
primitivePty = (<any>pty).native.open(cols, rows);
/** tests */
describe('xterm output comparison', function(): void {
this.timeout(10000);
let xterm: TestTerminal;
beforeEach(() => {
xterm = new TestTerminal({ cols: cols, rows: rows });
xterm.refresh = () => {};
xterm.viewport = <IViewport>{
syncScrollArea: () => {}
};
});
// omit stack trace for escape sequence files
Error.stackTraceLimit = 0;
const files = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')});
// for (let i = 0; i < files.length; ++i) console.debug(i, files[i]);
// only successful tests for now
const skip = [
10, 16, 17, 19, 32, 34, 35, 36, 39,
40, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61,
63, 68
];
// These are failing on macOS only
if (os.platform() === 'darwin') {
skip.push(3, 7, 11, 67);
}
for (let i = 0; i < files.length; i++) {
if (skip.indexOf(i) >= 0) {
continue;
}
((filename: string) => {
const inFile = fs.readFileSync(filename, 'utf8');
it(filename.split('/').slice(-1)[0], done => {
ptyReset(() => {
ptyWriteRead(inFile, fromPty => {
// uncomment this to get log from terminal
// console.log = function(){};
// Perform a synchronous .write(data)
xterm.writeBuffer.push(fromPty);
xterm.innerWrite();
const fromEmulator = terminalToString(xterm);
console.log = consoleLog;
const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
// Some of the tests have whitespace on the right of lines, we trim all the linex
// from xterm.js so ignore this for now at least.
const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n');
if (fromEmulator !== expectedRightTrimmed) {
// uncomment to get noisy output
throw new Error(formatError(inFile, fromEmulator, expected));
// throw new Error('mismatch');
}
done();
});
});
});
})(files[i]);
}
});
}
+6 -12
View File
@@ -4,15 +4,15 @@
*/
import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types';
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions, ILinkifier, ILinkMatcherOptions } from './Types';
import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types';
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener } from 'common/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
import { Buffer } from 'common/buffer/Buffer';
import * as Browser from 'common/Platform';
import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm';
import { Terminal } from './Terminal';
import { AttributeData } from 'common/buffer/AttributeData';
import { IColorManager, IColorSet } from 'browser/Types';
import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport } from 'browser/Types';
import { IOptionsService } from 'common/services/Services';
import { EventEmitter } from 'common/EventEmitter';
import { IParams } from 'common/parser/Types';
@@ -23,6 +23,8 @@ export class TestTerminal extends Terminal {
this.writeBuffer.push(data);
this._innerWrite();
}
keyDown(ev: any): boolean { return this._keyDown(ev); }
keyPress(ev: any): boolean { return this._keyPress(ev); }
}
export class MockTerminal implements ITerminal {
@@ -295,21 +297,12 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
addDisposableListener(type: string, handler: XtermListener): IDisposable {
throw new Error('Method not implemented.');
}
tabSet(): void {
throw new Error('Method not implemented.');
}
handler(data: string): void {
throw new Error('Method not implemented.');
}
handleTitle(title: string): void {
throw new Error('Method not implemented.');
}
index(): void {
throw new Error('Method not implemented.');
}
reverseIndex(): void {
throw new Error('Method not implemented.');
}
}
export class MockBuffer implements IBuffer {
@@ -329,6 +322,7 @@ export class MockBuffer implements IBuffer {
scrollTop: number;
savedY: number;
savedX: number;
savedCharset: ICharset | null;
savedCurAttrData = new AttributeData();
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
+2 -105
View File
@@ -6,7 +6,7 @@
import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { ICharset, IAttributeData, CharData } from 'common/Types';
import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IColorSet } from 'browser/Types';
import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport } from 'browser/Types';
import { IOptionsService } from 'common/services/Services';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IParams } from 'common/parser/Types';
@@ -15,9 +15,6 @@ export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
export type LineData = CharData[];
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
/**
* This interface encapsulates everything needed from the Terminal by the
* InputHandler. This cleanly separates the large amount of methods needed by
@@ -58,32 +55,16 @@ export interface IInputHandlingTerminal {
bell(): void;
focus(): void;
updateRange(y: number): void;
scroll(isWrapped?: boolean): void;
setgLevel(g: number): void;
eraseAttrData(): IAttributeData;
is(term: string): boolean;
setgCharset(g: number, charset: ICharset): void;
resize(x: number, y: number): void;
log(text: string, data?: any): void;
reset(): void;
showCursor(): void;
refresh(start: number, end: number): void;
error(text: string, data?: any): void;
tabSet(): void;
handleTitle(title: string): void;
index(): void;
reverseIndex(): void;
}
export interface IViewport extends IDisposable {
scrollBarWidth: number;
syncScrollArea(): void;
getLinesScrolled(ev: WheelEvent): number;
onWheel(ev: WheelEvent): void;
onTouchStart(ev: TouchEvent): void;
onTouchMove(ev: TouchEvent): void;
onThemeChange(colors: IColorSet): void;
}
export interface ICompositionHelper {
@@ -169,27 +150,7 @@ export interface IInputHandler {
ESC |
ESC }
ESC ~ */ setgLevel(level: number): void;
}
export interface ILinkMatcher {
id: number;
regex: RegExp;
handler: LinkMatcherHandler;
hoverTooltipCallback?: LinkMatcherHandler;
hoverLeaveCallback?: () => void;
matchIndex?: number;
validationCallback?: LinkMatcherValidationCallback;
priority?: number;
willLinkActivate?: (event: MouseEvent, uri: string) => boolean;
}
export interface ILinkifierEvent {
x1: number;
y1: number;
x2: number;
y2: number;
cols: number;
fg: number;
/** ESC # 8 */ screenAlignmentPattern(): void;
}
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
@@ -214,7 +175,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
scrollLines(disp: number, suppressScrollEvent?: boolean): void;
cancel(ev: Event, force?: boolean): boolean | void;
log(text: string): void;
showCursor(): void;
}
@@ -284,58 +244,12 @@ export interface ITerminalOptions extends IPublicTerminalOptions {
[key: string]: any;
cancelEvents?: boolean;
convertEol?: boolean;
debug?: boolean;
handler?: (data: string) => void;
screenKeys?: boolean;
termName?: string;
useFlowControl?: boolean;
}
export interface ILinkifier {
onLinkHover: IEvent<ILinkifierEvent>;
onLinkLeave: IEvent<ILinkifierEvent>;
onLinkTooltip: IEvent<ILinkifierEvent>;
attachToDom(mouseZoneManager: IMouseZoneManager): void;
linkifyRows(start: number, end: number): void;
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): boolean;
}
export interface ILinkMatcherOptions {
/**
* The index of the link from the regex.match(text) call. This defaults to 0
* (for regular expressions without capture groups).
*/
matchIndex?: number;
/**
* A callback that validates an individual link, returning true if valid and
* false if invalid.
*/
validationCallback?: LinkMatcherValidationCallback;
/**
* A callback that fires when the mouse hovers over a link.
*/
tooltipCallback?: LinkMatcherHandler;
/**
* A callback that fires when the mouse leaves a link that was hovered.
*/
leaveCallback?: () => void;
/**
* The priority of the link matcher, this defines the order in which the link
* matcher is evaluated relative to others, from highest to lowest. The
* default value is 0.
*/
priority?: number;
/**
* A callback that fires when the mousedown and click events occur that
* determines whether a link will be activated upon click. This enables
* only activating a link when a certain modifier is held down, if not the
* mouse event will continue propagation (eg. double click to select word).
*/
willLinkActivate?: (event: MouseEvent, uri: string) => boolean;
}
export interface IBrowser {
isNode: boolean;
userAgent: string;
@@ -346,20 +260,3 @@ export interface IBrowser {
isIphone: boolean;
isWindows: boolean;
}
export interface IMouseZoneManager extends IDisposable {
add(zone: IMouseZone): void;
clearAll(start?: number, end?: number): void;
}
export interface IMouseZone {
x1: number;
x2: number;
y1: number;
y2: number;
clickCallback: (e: MouseEvent) => any;
hoverCallback: (e: MouseEvent) => any | undefined;
tooltipCallback: (e: MouseEvent) => any | undefined;
leaveCallback: () => any | undefined;
willLinkActivate: (e: MouseEvent) => boolean;
}
@@ -4,7 +4,7 @@
*/
import { assert } from 'chai';
import * as Clipboard from './Clipboard';
import * as Clipboard from 'browser/Clipboard';
describe('evaluatePastedTextProcessing', () => {
it('should replace carriage return and/or line feed with carriage return', () => {

Some files were not shown because too many files have changed in this diff Show More