Merge pull request #1338 from Tyriar/1325_marker_api

Implement Markers API
This commit is contained in:
Daniel Imms
2018-03-20 19:14:10 -07:00
committed by GitHub
8 changed files with 219 additions and 2 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');
}
}
+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 -1
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 = {
@@ -1238,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.
@@ -1346,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.
*/
@@ -1379,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:
+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.');
+32
View File
@@ -236,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;
@@ -266,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.
*/
@@ -404,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.
*/
@@ -425,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.
*/
@@ -452,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.
*/