Merge remote-tracking branch 'origin/master' into 24_multi_line_links

This commit is contained in:
Daniel Imms
2018-03-21 07:32:32 -07:00
18 changed files with 250 additions and 31 deletions
+24
View File
@@ -188,4 +188,28 @@ describe('Buffer', () => {
assert.equal(buffer.lines.maxLength, INIT_ROWS / 2);
});
});
describe('addMarker', () => {
it('should adjust a marker line when the buffer is trimmed', () => {
terminal.options.scrollback = 0;
buffer = new Buffer(terminal, true);
buffer.fillViewportRows();
const marker = buffer.addMarker(buffer.lines.length - 1);
assert.equal(marker.line, buffer.lines.length - 1);
buffer.lines.emit('trim', 1);
assert.equal(marker.line, buffer.lines.length - 2);
});
it('should dispose of a marker if it is trimmed off the buffer', () => {
terminal.options.scrollback = 0;
buffer = new Buffer(terminal, true);
buffer.fillViewportRows();
assert.equal(buffer.markers.length, 0);
const marker = buffer.addMarker(0);
assert.equal(marker.isDisposed, false);
assert.equal(buffer.markers.length, 1);
buffer.lines.emit('trim', 1);
assert.equal(marker.isDisposed, true);
assert.equal(buffer.markers.length, 0);
});
});
});
+48
View File
@@ -5,6 +5,8 @@
import { CircularList } from './utils/CircularList';
import { LineData, CharData, ITerminal, IBuffer } from './Types';
import { EventEmitter } from './EventEmitter';
import { IDisposable, IMarker } from 'xterm';
export const CHAR_DATA_ATTR_INDEX = 0;
export const CHAR_DATA_CHAR_INDEX = 1;
@@ -31,6 +33,7 @@ export class Buffer implements IBuffer {
public tabs: any;
public savedY: number;
public savedX: number;
public markers: Marker[] = [];
/**
* Create a new Buffer.
@@ -303,4 +306,49 @@ export class Buffer implements IBuffer {
while (!this.tabs[++x] && x < this._terminal.cols);
return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x;
}
public addMarker(y: number): Marker {
const marker = new Marker(y);
this.markers.push(marker);
marker.disposables.push(this._lines.addDisposableListener('trim', amount => {
marker.line -= amount;
// The marker should be disposed when the line is trimmed from the buffer
if (marker.line < 0) {
marker.dispose();
}
}));
marker.on('dispose', () => this._removeMarker(marker));
return marker;
}
private _removeMarker(marker: Marker): void {
// TODO: This could probably be optimized by relying on sort order and trimming the array using .length
this.markers.splice(this.markers.indexOf(marker), 1);
}
}
export class Marker extends EventEmitter implements IMarker {
private static NEXT_ID = 1;
private _id: number = Marker.NEXT_ID++;
public isDisposed: boolean = false;
public disposables: IDisposable[] = [];
public get id(): number { return this._id; }
constructor(
public line: number
) {
super();
}
public dispose(): void {
if (this.isDisposed) {
return;
}
this.isDisposed = true;
this.disposables.forEach(d => d.dispose());
this.disposables.length = 0;
this.emit('dispose');
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: nu
[0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
[0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
[0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB],
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB]
];
const COMBINING_HIGH = [
[0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
+1 -1
View File
@@ -17,7 +17,7 @@ describe('CompositionHelper', () => {
compositionView = {
classList: {
add: () => {},
remove: () => {},
remove: () => {}
},
getBoundingClientRect: () => {
return { width: 0 };
+1 -1
View File
@@ -132,7 +132,7 @@ export class CompositionHelper {
// fire before the setTimeout executes.
const currentCompositionPosition = {
start: this._compositionPosition.start,
end: this._compositionPosition.end,
end: this._compositionPosition.end
};
// Since composition* events happen before the changes take place in the textarea on most
+2 -2
View File
@@ -261,7 +261,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
},
e => {
this.emit(LinkHoverEventTypes.HOVER, this._createLinkHoverEvent(x1, y1, x2, y2));
this._terminal.element.style.cursor = 'pointer';
this._terminal.element.classList.add('xterm-cursor-pointer');
},
e => {
this.emit(LinkHoverEventTypes.TOOLTIP, this._createLinkHoverEvent(x1, y1, x2, y2));
@@ -271,7 +271,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
},
() => {
this.emit(LinkHoverEventTypes.LEAVE, this._createLinkHoverEvent(x1, y1, x2, y2));
this._terminal.element.style.cursor = '';
this._terminal.element.classList.remove('xterm-cursor-pointer');
if (matcher.hoverLeaveCallback) {
matcher.hoverLeaveCallback();
}
+39
View File
@@ -314,6 +314,45 @@ describe('SelectionManager', () => {
});
});
describe('selectLines', () => {
it('should select a single line', () => {
buffer.lines.length = 3;
buffer.lines.set(0, stringToRow('1'));
buffer.lines.set(1, stringToRow('2'));
buffer.lines.set(2, stringToRow('3'));
selectionManager.selectLines(1, 1);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]);
});
it('should select multiple lines', () => {
buffer.lines.length = 5;
buffer.lines.set(0, stringToRow('1'));
buffer.lines.set(1, stringToRow('2'));
buffer.lines.set(2, stringToRow('3'));
buffer.lines.set(3, stringToRow('4'));
buffer.lines.set(4, stringToRow('5'));
selectionManager.selectLines(1, 3);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 3]);
});
it('should select the to the start when requesting a negative row', () => {
buffer.lines.length = 2;
buffer.lines.set(0, stringToRow('1'));
buffer.lines.set(1, stringToRow('2'));
selectionManager.selectLines(-1, 0);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0]);
});
it('should select the to the end when requesting beyond the final row', () => {
buffer.lines.length = 2;
buffer.lines.set(0, stringToRow('1'));
buffer.lines.set(1, stringToRow('2'));
selectionManager.selectLines(1, 2);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]);
});
});
describe('hasSelection', () => {
it('should return whether there is a selection', () => {
selectionManager.model.selectionStart = [0, 0];
+10
View File
@@ -292,6 +292,16 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
this._terminal.emit('selection');
}
public selectLines(start: number, end: number): void {
this._model.clearSelection();
start = Math.max(start, 0);
end = Math.min(end, this._terminal.buffer.lines.length - 1);
this._model.selectionStart = [0, start];
this._model.selectionEnd = [this._terminal.cols, end];
this.refresh();
this._terminal.emit('selection');
}
/**
* Handle the buffer being trimmed, adjust the selection position.
* @param amount The amount the buffer is being trimmed.
+28
View File
@@ -258,6 +258,34 @@ describe('term.js addons', () => {
});
});
describe('scrollToLine', () => {
let startYDisp;
beforeEach(() => {
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
}
startYDisp = (term.rows * 2) + 1;
});
it('should scroll to requested line', () => {
assert.equal(term.buffer.ydisp, startYDisp);
term.scrollToLine(0);
assert.equal(term.buffer.ydisp, 0);
term.scrollToLine(10);
assert.equal(term.buffer.ydisp, 10);
term.scrollToLine(startYDisp);
assert.equal(term.buffer.ydisp, startYDisp);
term.scrollToLine(20);
assert.equal(term.buffer.ydisp, 20);
});
it('should not scroll beyond boundary lines', () => {
assert.equal(term.buffer.ydisp, startYDisp);
term.scrollToLine(-1);
assert.equal(term.buffer.ydisp, 0);
term.scrollToLine(startYDisp + 1);
assert.equal(term.buffer.ydisp, startYDisp);
});
});
describe('keyDown', () => {
it('should scroll down, when a key is pressed and terminal is scrolled up', () => {
// Override _evaluateKeyEscapeSequence to return cancel code
+27 -4
View File
@@ -45,7 +45,7 @@ import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
import { MouseZoneManager } from './input/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ScreenDprMonitor } from './utils/ScreenDprMonitor';
import { ITheme, ILocalizableStrings } from 'xterm';
import { ITheme, ILocalizableStrings, IMarker } from 'xterm';
// reg + shift key mappings for digits and special chars
const KEYCODE_KEY_MAPPINGS = {
@@ -138,7 +138,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
private _viewportElement: HTMLElement;
private _helperContainer: HTMLElement;
private _compositionView: HTMLElement;
private _charSizeStyleElement: HTMLStyleElement;
private _visualBellTimer: number;
@@ -668,8 +667,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this);
this._helperContainer.appendChild(this._compositionView);
this._charSizeStyleElement = document.createElement('style');
this._helperContainer.appendChild(this._charSizeStyleElement);
this.charMeasure = new CharMeasure(document, this._helperContainer);
// Performance: Add viewport and helper elements from the fragment
@@ -1241,6 +1238,13 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.scrollLines(this.buffer.ybase - this.buffer.ydisp);
}
public scrollToLine(line: number): void {
const scrollAmount = line - this.buffer.ydisp;
if (scrollAmount !== 0) {
this.scrollLines(scrollAmount);
}
}
/**
* Writes text to the terminal.
* @param {string} data The text to write to the terminal.
@@ -1349,6 +1353,19 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
}
}
public get markers(): IMarker[] {
return this.buffer.markers;
}
public addMarker(cursorYOffset: number): IMarker {
// Disallow markers on the alt buffer
if (this.buffer !== this.buffers.normal) {
return;
}
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
}
/**
* Gets whether the terminal has an active selection.
*/
@@ -1382,6 +1399,12 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
}
}
public selectLines(start: number, end: number): void {
if (this.selectionManager) {
this.selectionManager.selectLines(start, end);
}
}
/**
* Handle a keydown event
* Key Resources:
-1
View File
@@ -277,7 +277,6 @@ export interface IBufferSet extends IEventEmitter {
export interface ICircularList<T> extends IEventEmitter {
length: number;
maxLength: number;
forEach: (callbackfn: (value: T, index: number) => void) => void;
get(index: number): T;
set(index: number, value: T): void;
+3 -3
View File
@@ -33,7 +33,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
y: null,
isFocused: null,
style: null,
width: null,
width: null
};
this._cursorRenderers = {
'bar': this._renderBarCursor.bind(this),
@@ -51,7 +51,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
y: null,
isFocused: null,
style: null,
width: null,
width: null
};
}
@@ -183,7 +183,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
y: null,
isFocused: null,
style: null,
width: null,
width: null
};
}
}
+2 -6
View File
@@ -23,10 +23,7 @@ export class CharMeasure extends EventEmitter implements ICharMeasure {
this._document = document;
this._parentElement = parentElement;
this._measureElement = this._document.createElement('span');
this._measureElement.style.position = 'absolute';
this._measureElement.style.top = '0';
this._measureElement.style.left = '-9999em';
this._measureElement.style.lineHeight = 'normal';
this._measureElement.classList.add('xterm-char-measure-element');
this._measureElement.textContent = 'W';
this._measureElement.setAttribute('aria-hidden', 'true');
this._parentElement.appendChild(this._measureElement);
@@ -41,7 +38,7 @@ export class CharMeasure extends EventEmitter implements ICharMeasure {
}
public measure(options: ITerminalOptions): void {
this._measureElement.style.fontFamily = options.fontFamily;
this._measureElement.style.fontFamily = options.fontFamily;
this._measureElement.style.fontSize = `${options.fontSize}px`;
const geometry = this._measureElement.getBoundingClientRect();
// The element is likely currently display:none, we should retain the
@@ -55,5 +52,4 @@ export class CharMeasure extends EventEmitter implements ICharMeasure {
this.emit('charsizechanged');
}
}
}
-9
View File
@@ -58,15 +58,6 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
this._length = newLength;
}
public get forEach(): (callbackfn: (value: T, index: number) => void) => void {
return (callbackfn: (value: T, index: number) => void) => {
let length = this.length;
for (let i = 0; i < length; i++) {
callbackfn(this.get(i), i);
}
};
}
/**
* Gets the value at an index.
*
+11 -1
View File
@@ -7,9 +7,19 @@ import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../rende
import { LineData, IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ICircularList, ILinkifier, IMouseHelper, ILinkMatcherOptions, XtermListener } from '../Types';
import { Buffer } from '../Buffer';
import * as Browser from '../shared/utils/Browser';
import { ITheme, IDisposable } from 'xterm';
import { ITheme, IDisposable, IMarker } from 'xterm';
export class MockTerminal implements ITerminal {
markers: IMarker[];
addMarker(cursorYOffset: number): IMarker {
throw new Error('Method not implemented.');
}
selectLines(start: number, end: number): void {
throw new Error('Method not implemented.');
}
scrollToLine(line: number): void {
throw new Error('Method not implemented.');
}
static string: any;
getOption(key: any): any {
throw new Error('Method not implemented.');
+7 -1
View File
@@ -117,11 +117,13 @@
visibility: hidden;
}
.xterm .xterm-char-measure-element {
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm.enable-mouse-events {
@@ -151,3 +153,7 @@
height: 1px;
overflow: hidden;
}
.xterm-cursor-pointer {
cursor: pointer;
}
+12
View File
@@ -43,6 +43,18 @@
true,
"always"
],
"trailing-comma": [
true,
{
"multiline": {
"objects": "never",
"arrays": "never",
"functions": "never",
"typeLiterals": "ignore"
},
"esSpecCompliant": true
}
],
"triple-equals": [
true,
"allow-null-check"
+34 -1
View File
@@ -23,6 +23,7 @@ declare module 'xterm' {
* Warning: Enabling this option can reduce performances somewhat.
*/
allowTransparency?: boolean;
/**
* A data uri of the sound to use for the bell (needs bellStyle = 'sound').
*/
@@ -55,7 +56,7 @@ declare module 'xterm' {
/**
* Whether to enable the rendering of bold text.
*
*
* @deprecated Use fontWeight and fontWeightBold instead.
*/
enableBold?: boolean;
@@ -235,6 +236,12 @@ declare module 'xterm' {
dispose(): void;
}
export interface IMarker extends IDisposable {
readonly id: number;
readonly isDisposed: boolean;
readonly line: number;
}
export interface ILocalizableStrings {
blankLine: string;
promptLabel: string;
@@ -265,6 +272,12 @@ declare module 'xterm' {
*/
cols: number;
/**
* (EXPERIMENTAL) Get all markers registered against the buffer. If the alt
* buffer is active this will always return [].
*/
markers: IMarker[];
/**
* Natural language strings that can be localized.
*/
@@ -403,6 +416,13 @@ declare module 'xterm' {
*/
deregisterLinkMatcher(matcherId: number): void;
/**
* (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the
* alt buffer is active, undefined is returned.
* @param cursorYOffset The y position offset of the marker from the cursor.
*/
addMarker(cursorYOffset: number): IMarker;
/**
* Gets whether the terminal has an active selection.
*/
@@ -424,6 +444,13 @@ declare module 'xterm' {
*/
selectAll(): void;
/**
* Selects text in the buffer between 2 lines.
* @param start The 0-based line index to select from (inclusive).
* @param end The 0-based line index to select to (inclusive).
*/
selectLines(start: number, end: number): void;
/**
* Destroys the terminal and detaches it from the DOM.
*/
@@ -451,6 +478,12 @@ declare module 'xterm' {
*/
scrollToBottom(): void;
/**
* Scrolls to a line within the buffer.
* @param line The 0-based line index to scroll to.
*/
scrollToLine(line: number): void;
/**
* Clear the entire buffer, making the prompt line the new first line.
*/