Merge branch 'master' into typedarray_BufferLine

This commit is contained in:
Jörg Breitbart
2018-10-07 22:08:46 +02:00
23 changed files with 1208 additions and 61 deletions
+1
View File
@@ -170,6 +170,7 @@ computational environment for Jupyter, supporting interactive data science and s
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
- [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere.
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
-1
View File
@@ -149,7 +149,6 @@ function runRealTerminal(): void {
term._initialized = true;
}
// TODO: Maybe fake terminal should be removed? Not sure it's useful anymore
function runFakeTerminal(): void {
if (term._initialized) {
return;
+4 -2
View File
@@ -1,7 +1,7 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
"version": "3.7.0",
"version": "3.8.0",
"main": "lib/public/Terminal.js",
"types": "typings/xterm.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
@@ -50,6 +50,7 @@
"start": "node demo/start",
"start-zmodem": "node demo/zmodem/app",
"lint": "tslint 'src/**/*.ts' './demo/**/*.ts'",
"pretest": "npm run layering",
"test": "npm run mocha",
"posttest": "npm run lint",
"test-debug": "node --inspect-brk node_modules/.bin/gulp test",
@@ -64,6 +65,7 @@
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"webpack": "gulp webpack",
"watch": "concurrently --kill-others-on-fail --names \"lib,css\" \"tsc -w\" \"gulp watch-css\"",
"watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\""
"watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\"",
"layering": "concurrently --kill-others-on-fail --names \"common,core\" \"tsc -p ./src/common\" \"tsc -p ./src/core\""
}
}
+32 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { assert } from 'chai';
import { assert, expect } from 'chai';
import { ITerminal } from './Types';
import { Buffer, DEFAULT_ATTR, CHAR_DATA_CHAR_INDEX } from './Buffer';
import { CircularList } from './common/CircularList';
@@ -518,4 +518,35 @@ describe('Buffer', () => {
}
});
});
describe('BufferStringIterator', function(): void {
it('iterator does not ovrflow buffer limits', function(): void {
const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
const data = [
'aaaaaaaaaa',
'aaaaaaaaa\n',
'aaaaaaaaaa',
'aaaaaaaaa\n',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaa\n',
'aaaaaaaaaa',
'aaaaaaaaaa'
];
terminal.writeSync(data.join(''));
// brute force test with insane values
expect(() => {
for (let overscan = 0; overscan < 20; ++overscan) {
for (let start = -10; start < 20; ++start) {
for (let end = -10; end < 20; ++end) {
const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
while (it.hasNext()) {
it.next();
}
}
}
}
}).to.not.throw();
});
});
});
+33 -3
View File
@@ -403,8 +403,8 @@ export class Buffer implements IBuffer {
this.markers.splice(this.markers.indexOf(marker), 1);
}
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
return new BufferStringIterator(this, trimRight, startIndex, endIndex);
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator {
return new BufferStringIterator(this, trimRight, startIndex, endIndex, startOverscan, endOverscan);
}
}
@@ -433,6 +433,18 @@ export class Marker extends EventEmitter implements IMarker {
}
}
/**
* Iterator to get unwrapped content strings from the buffer.
* The iterator returns at least the string data between the borders
* `startIndex` and `endIndex` (exclusive) and will expand the lines
* by `startOverscan` to the top and by `endOverscan` to the bottom,
* if no new line was found in between.
* It will never read/return string data beyond `startIndex - startOverscan`
* or `endIndex + endOverscan`. Therefore the first and last line might be truncated.
* It is possible to always get the full string for the first and last line as well
* by setting the overscan values to the actual buffer length. This not recommended
* since it might return the whole buffer within a single string in a worst case scenario.
*/
export class BufferStringIterator implements IBufferStringIterator {
private _current: number;
@@ -440,8 +452,16 @@ export class BufferStringIterator implements IBufferStringIterator {
private _buffer: IBuffer,
private _trimRight: boolean,
private _startIndex: number = 0,
private _endIndex: number = _buffer.lines.length
private _endIndex: number = _buffer.lines.length,
private _startOverscan: number = 0,
private _endOverscan: number = 0
) {
if (this._startIndex < 0) {
this._startIndex = 0;
}
if (this._endIndex > this._buffer.lines.length) {
this._endIndex = this._buffer.lines.length;
}
this._current = this._startIndex;
}
@@ -451,6 +471,16 @@ export class BufferStringIterator implements IBufferStringIterator {
public next(): IBufferStringIteratorResult {
const range = this._buffer.getWrappedRangeForLine(this._current);
// limit search window to overscan value at both borders
if (range.first < this._startIndex - this._startOverscan) {
range.first = this._startIndex - this._startOverscan;
}
if (range.last > this._endIndex + this._endOverscan) {
range.last = this._endIndex + this._endOverscan;
}
// limit to current buffer length
range.first = Math.max(range.first, 0);
range.last = Math.min(range.last, this._buffer.lines.length);
let result = '';
for (let i = range.first; i <= range.last; ++i) {
// TODO: always apply trimRight after fixing #1685
+20 -13
View File
@@ -21,6 +21,13 @@ export class Linkifier extends EventEmitter implements ILinkifier {
*/
protected static readonly TIME_BEFORE_LINKIFY = 200;
/**
* Limit of the unwrapping line expansion (overscan) at the top and bottom
* of the actual viewport in ASCII characters.
* A limit of 2000 should match most sane urls.
*/
protected static readonly OVERSCAN_CHAR_LIMIT = 2000;
protected _linkMatchers: ILinkMatcher[] = [];
private _mouseZoneManager: IMouseZoneManager;
@@ -92,11 +99,19 @@ export class Linkifier extends EventEmitter implements ILinkifier {
// Invalidate bad end row values (if a resize happened)
const absoluteRowIndexEnd = buffer.ydisp + Math.min(this._rowsToLinkify.end, this._terminal.rows) + 1;
// iterate over the range of unwrapped content strings within start..end (excluding)
// _doLinkifyRow gets full unwrapped lines with the start row as buffer offset for every matcher
// for wrapped content over several rows the iterator might return rows outside the viewport
// we skip those later in _doLinkifyRow
const iterator = buffer.iterator(false, absoluteRowIndexStart, absoluteRowIndexEnd);
// Iterate over the range of unwrapped content strings within start..end
// (excluding).
// _doLinkifyRow gets full unwrapped lines with the start row as buffer offset
// for every matcher.
// The unwrapping is needed to also match content that got wrapped across
// several buffer lines. To avoid a worst case scenario where the whole buffer
// contains just a single unwrapped string we limit this line expansion beyond
// the viewport to +OVERSCAN_CHAR_LIMIT chars (overscan) at top and bottom.
// This comes with the tradeoff that matches longer than OVERSCAN_CHAR_LIMIT
// chars will not match anymore at the viewport borders.
const overscanLineLimit = Math.ceil(Linkifier.OVERSCAN_CHAR_LIMIT / this._terminal.cols);
const iterator = this._terminal.buffer.iterator(
false, absoluteRowIndexStart, absoluteRowIndexEnd, overscanLineLimit, overscanLineLimit);
while (iterator.hasNext()) {
const lineData: IBufferStringIteratorResult = iterator.next();
for (let i = 0; i < this._linkMatchers.length; i++) {
@@ -208,14 +223,6 @@ export class Linkifier extends EventEmitter implements ILinkifier {
// get the buffer index as [absolute row, col] for the match
const bufferIndex = this._terminal.buffer.stringIndexToBufferIndex(rowIndex, stringIndex);
// skip rows outside of the viewport
if (bufferIndex[0] - this._terminal.buffer.ydisp < 0) {
continue;
}
if (bufferIndex[0] - this._terminal.buffer.ydisp > this._terminal.rows) {
break;
}
const line = this._terminal.buffer.lines.get(bufferIndex[0]);
const char = line.get(bufferIndex[1]);
let fg: number | undefined;
+1 -1
View File
@@ -297,7 +297,7 @@ export interface IBuffer {
prevStop(x?: number): number;
getBlankLine(attr: number, isWrapped?: boolean): IBufferLine;
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator;
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
}
export interface IBufferSet extends IEventEmitter {
+7 -4
View File
@@ -4,17 +4,20 @@
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/fit/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+7 -4
View File
@@ -4,17 +4,20 @@
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/fullscreen/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+1 -2
View File
@@ -8,7 +8,6 @@ import * as search from './search';
import { SearchHelper } from './SearchHelper';
import { ISearchOptions, ISearchResult } from './Interfaces';
class MockTerminalPlain {}
class MockTerminal {
@@ -16,7 +15,7 @@ class MockTerminal {
public searchHelper: TestSearchHelper;
public cols: number;
constructor(options: any) {
this._core = new (require('../../../lib/Terminal').Terminal)(options);
this._core = new (require('../../../lib/Terminal')).Terminal(options);
this.searchHelper = new TestSearchHelper(this as any);
this.cols = options.cols;
}
+10 -5
View File
@@ -3,18 +3,23 @@
"module": "commonjs",
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/search/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
],
"exclude": [
"**/*.test.ts"
]
}
+7 -5
View File
@@ -3,18 +3,20 @@
"module": "commonjs",
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/terminado/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+7 -4
View File
@@ -4,17 +4,20 @@
"target": "es5",
"lib": [
"dom",
"es6",
"es5",
],
"rootDir": ".",
"outDir": "../../../lib/addons/webLinks/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+7 -5
View File
@@ -3,18 +3,20 @@
"module": "commonjs",
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/winptyCompat/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+7 -5
View File
@@ -3,18 +3,20 @@
"module": "commonjs",
"target": "es5",
"lib": [
"dom",
"es6",
"es5"
],
"rootDir": ".",
"outDir": "../../../lib/addons/zmodem/",
"sourceMap": true,
"removeComments": true,
"declaration": true,
"preserveWatchOutput": true
"preserveWatchOutput": true,
"types": [
"../../node_modules/@types/mocha",
"../.."
]
},
"include": [
"**/*.ts",
"../../../typings/xterm.d.ts"
"**/*.ts"
]
}
+1 -1
View File
@@ -14,7 +14,7 @@ export class EventEmitter extends Disposable implements IEventEmitter, IDisposab
super();
// Restore the previous events if available, this will happen if the
// constructor is called multiple times on the same object (terminal reset).
this._events = this._events || {};
this._events = (<any>this)._events || {};
}
public on(type: string, listener: XtermListener): void {
+19
View File
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"es5"
],
"rootDir": ".",
"noEmit": true,
"strict": true,
"pretty": true,
"types": [
"../../node_modules/@types/mocha",
"../../"
]
},
"include": [
"./**/*"
]
}
+2 -2
View File
@@ -10,12 +10,12 @@ import { ICharset } from '../Types';
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
* for a discussion on character sets. Only VT100 character sets are supported.
*/
export const CHARSETS: { [key: string]: ICharset } = {};
export const CHARSETS: { [key: string]: ICharset | null } = {};
/**
* The default character set, US.
*/
export const DEFAULT_CHARSET: ICharset = CHARSETS['B'];
export const DEFAULT_CHARSET: ICharset | null = CHARSETS['B'];
/**
* DEC Special Character and Line Drawing Set.
+3 -2
View File
@@ -2,6 +2,7 @@
import { assert } from 'chai';
import { evaluateKeyboardEvent } from './Keyboard';
import { IKeyboardResult } from '../Types';
import { IKeyboardEvent } from '../../common/Types';
/**
* A helper function for testing which allows passing in a partial event and defaults will be filled
@@ -20,12 +21,12 @@ function testEvaluateKeyboardEvent(partialEvent: {
isMac?: boolean;
macOptionIsMeta?: boolean;
} = {}): IKeyboardResult {
const event = {
const event: IKeyboardEvent = {
altKey: partialEvent.altKey || false,
ctrlKey: partialEvent.ctrlKey || false,
shiftKey: partialEvent.shiftKey || false,
metaKey: partialEvent.metaKey || false,
keyCode: partialEvent.keyCode !== undefined ? partialEvent.keyCode : undefined,
keyCode: partialEvent.keyCode !== undefined ? partialEvent.keyCode : 0,
key: partialEvent.key || '',
type: partialEvent.type || ''
};
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"es5"
],
"rootDir": ".",
"noEmit": true,
"strict": true,
"pretty": true,
"types": [
"../../node_modules/@types/mocha",
"../../"
]
},
"include": [
"./**/*",
"../common/**/*"
]
}

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