540 lines
17 KiB
TypeScript
540 lines
17 KiB
TypeScript
/**
|
|
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*/
|
|
|
|
import { Terminal as PublicTerminal, ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable } from 'xterm';
|
|
import { IColorSet, IRenderer } from './renderer/Types';
|
|
import { IMouseZoneManager } from './ui/Types';
|
|
import { ICharset } from './core/Types';
|
|
import { ICircularList } from './common/Types';
|
|
|
|
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
|
|
|
export type CharData = [number, string, number, number];
|
|
export type LineData = CharData[];
|
|
|
|
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
|
|
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
|
|
|
export type CharacterJoinerHandler = (text: string) => [number, number][];
|
|
|
|
// BufferIndex denotes a position in the buffer: [rowIndex, colIndex]
|
|
export type BufferIndex = [number, number];
|
|
|
|
export const enum LinkHoverEventTypes {
|
|
HOVER = 'linkhover',
|
|
TOOLTIP = 'linktooltip',
|
|
LEAVE = 'linkleave'
|
|
}
|
|
|
|
/**
|
|
* This interface encapsulates everything needed from the Terminal by the
|
|
* InputHandler. This cleanly separates the large amount of methods needed by
|
|
* InputHandler cleanly from the ITerminal interface.
|
|
*/
|
|
export interface IInputHandlingTerminal extends IEventEmitter {
|
|
element: HTMLElement;
|
|
options: ITerminalOptions;
|
|
cols: number;
|
|
rows: number;
|
|
charset: ICharset;
|
|
gcharset: number;
|
|
glevel: number;
|
|
charsets: ICharset[];
|
|
applicationKeypad: boolean;
|
|
applicationCursor: boolean;
|
|
originMode: boolean;
|
|
insertMode: boolean;
|
|
wraparoundMode: boolean;
|
|
bracketedPasteMode: boolean;
|
|
curAttr: number;
|
|
savedCols: number;
|
|
x10Mouse: boolean;
|
|
vt200Mouse: boolean;
|
|
normalMouse: boolean;
|
|
mouseEvents: boolean;
|
|
sendFocus: boolean;
|
|
utfMouse: boolean;
|
|
sgrMouse: boolean;
|
|
urxvtMouse: boolean;
|
|
cursorHidden: boolean;
|
|
|
|
buffers: IBufferSet;
|
|
buffer: IBuffer;
|
|
viewport: IViewport;
|
|
selectionManager: ISelectionManager;
|
|
|
|
bell(): void;
|
|
focus(): void;
|
|
updateRange(y: number): void;
|
|
scroll(isWrapped?: boolean): void;
|
|
setgLevel(g: number): void;
|
|
eraseAttr(): number;
|
|
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;
|
|
matchColor(r1: number, g1: number, b1: number): number;
|
|
error(text: string, data?: any): void;
|
|
setOption(key: string, value: any): void;
|
|
tabSet(): void;
|
|
handler(data: string): 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;
|
|
onThemeChanged(colors: IColorSet): void;
|
|
}
|
|
|
|
export interface ICompositionHelper {
|
|
compositionstart(): void;
|
|
compositionupdate(ev: CompositionEvent): void;
|
|
compositionend(): void;
|
|
updateCompositionElements(dontRecurse?: boolean): void;
|
|
keydown(ev: KeyboardEvent): boolean;
|
|
}
|
|
|
|
/**
|
|
* Calls the parser and handles actions generated by the parser.
|
|
*/
|
|
export interface IInputHandler {
|
|
parse(data: string): void;
|
|
print(data: Uint32Array, start: number, end: number): void;
|
|
|
|
/** C0 BEL */ bell(): void;
|
|
/** C0 LF */ lineFeed(): void;
|
|
/** C0 CR */ carriageReturn(): void;
|
|
/** C0 BS */ backspace(): void;
|
|
/** C0 HT */ tab(): void;
|
|
/** C0 SO */ shiftOut(): void;
|
|
/** C0 SI */ shiftIn(): void;
|
|
|
|
/** CSI @ */ insertChars(params?: number[]): void;
|
|
/** CSI A */ cursorUp(params?: number[]): void;
|
|
/** CSI B */ cursorDown(params?: number[]): void;
|
|
/** CSI C */ cursorForward(params?: number[]): void;
|
|
/** CSI D */ cursorBackward(params?: number[]): void;
|
|
/** CSI E */ cursorNextLine(params?: number[]): void;
|
|
/** CSI F */ cursorPrecedingLine(params?: number[]): void;
|
|
/** CSI G */ cursorCharAbsolute(params?: number[]): void;
|
|
/** CSI H */ cursorPosition(params?: number[]): void;
|
|
/** CSI I */ cursorForwardTab(params?: number[]): void;
|
|
/** CSI J */ eraseInDisplay(params?: number[]): void;
|
|
/** CSI K */ eraseInLine(params?: number[]): void;
|
|
/** CSI L */ insertLines(params?: number[]): void;
|
|
/** CSI M */ deleteLines(params?: number[]): void;
|
|
/** CSI P */ deleteChars(params?: number[]): void;
|
|
/** CSI S */ scrollUp(params?: number[]): void;
|
|
/** CSI T */ scrollDown(params?: number[], collect?: string): void;
|
|
/** CSI X */ eraseChars(params?: number[]): void;
|
|
/** CSI Z */ cursorBackwardTab(params?: number[]): void;
|
|
/** CSI ` */ charPosAbsolute(params?: number[]): void;
|
|
/** CSI a */ hPositionRelative(params?: number[]): void;
|
|
/** CSI b */ repeatPrecedingCharacter(params?: number[]): void;
|
|
/** CSI c */ sendDeviceAttributes(params?: number[], collect?: string): void;
|
|
/** CSI d */ linePosAbsolute(params?: number[]): void;
|
|
/** CSI e */ vPositionRelative(params?: number[]): void;
|
|
/** CSI f */ hVPosition(params?: number[]): void;
|
|
/** CSI g */ tabClear(params?: number[]): void;
|
|
/** CSI h */ setMode(params?: number[], collect?: string): void;
|
|
/** CSI l */ resetMode(params?: number[], collect?: string): void;
|
|
/** CSI m */ charAttributes(params?: number[]): void;
|
|
/** CSI n */ deviceStatus(params?: number[], collect?: string): void;
|
|
/** CSI p */ softReset(params?: number[], collect?: string): void;
|
|
/** CSI q */ setCursorStyle(params?: number[], collect?: string): void;
|
|
/** CSI r */ setScrollRegion(params?: number[], collect?: string): void;
|
|
/** CSI s */ saveCursor(params?: number[]): void;
|
|
/** CSI u */ restoreCursor(params?: number[]): void;
|
|
/** OSC 0
|
|
OSC 2 */ setTitle(data: string): void;
|
|
/** ESC E */ nextLine(): void;
|
|
/** ESC = */ keypadApplicationMode(): void;
|
|
/** ESC > */ keypadNumericMode(): void;
|
|
/** ESC % G
|
|
ESC % @ */ selectDefaultCharset(): void;
|
|
/** ESC ( C
|
|
ESC ) C
|
|
ESC * C
|
|
ESC + C
|
|
ESC - C
|
|
ESC . C
|
|
ESC / C */ selectCharset(collectAndFlag: string): void;
|
|
/** ESC D */ index(): void;
|
|
/** ESC H */ tabSet(): void;
|
|
/** ESC M */ reverseIndex(): void;
|
|
/** ESC c */ reset(): void;
|
|
/** ESC n
|
|
ESC o
|
|
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 ILinkHoverEvent {
|
|
x1: number;
|
|
y1: number;
|
|
x2: number;
|
|
y2: number;
|
|
cols: number;
|
|
fg: number;
|
|
}
|
|
|
|
export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
|
|
screenElement: HTMLElement;
|
|
selectionManager: ISelectionManager;
|
|
charMeasure: ICharMeasure;
|
|
renderer: IRenderer;
|
|
browser: IBrowser;
|
|
writeBuffer: string[];
|
|
cursorHidden: boolean;
|
|
cursorState: number;
|
|
options: ITerminalOptions;
|
|
buffer: IBuffer;
|
|
buffers: IBufferSet;
|
|
isFocused: boolean;
|
|
mouseHelper: IMouseHelper;
|
|
viewport: IViewport;
|
|
bracketedPasteMode: boolean;
|
|
applicationCursor: boolean;
|
|
|
|
/**
|
|
* Emit the 'data' event and populate the given data.
|
|
* @param data The data to populate in the event.
|
|
*/
|
|
handler(data: string): void;
|
|
scrollLines(disp: number, suppressScrollEvent?: boolean): void;
|
|
cancel(ev: Event, force?: boolean): boolean | void;
|
|
log(text: string): void;
|
|
showCursor(): void;
|
|
}
|
|
|
|
export interface IBufferAccessor {
|
|
buffer: IBuffer;
|
|
}
|
|
|
|
export interface IElementAccessor {
|
|
readonly element: HTMLElement;
|
|
}
|
|
|
|
export interface ILinkifierAccessor {
|
|
linkifier: ILinkifier;
|
|
}
|
|
|
|
export interface IMouseHelper {
|
|
getCoords(event: { clientX: number, clientY: number }, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number];
|
|
getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number };
|
|
}
|
|
|
|
export interface ICharMeasure {
|
|
width: number;
|
|
height: number;
|
|
measure(options: ITerminalOptions): void;
|
|
}
|
|
|
|
// TODO: The options that are not in the public API should be reviewed
|
|
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 IBufferStringIteratorResult {
|
|
range: {first: number, last: number};
|
|
content: string;
|
|
}
|
|
|
|
export interface IBufferStringIterator {
|
|
hasNext(): boolean;
|
|
next(): IBufferStringIteratorResult;
|
|
}
|
|
|
|
export interface IBuffer {
|
|
readonly lines: ICircularList<IBufferLine>;
|
|
ydisp: number;
|
|
ybase: number;
|
|
y: number;
|
|
x: number;
|
|
tabs: any;
|
|
scrollBottom: number;
|
|
scrollTop: number;
|
|
hasScrollback: boolean;
|
|
savedY: number;
|
|
savedX: number;
|
|
savedCurAttr: number;
|
|
isCursorInViewport: boolean;
|
|
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
|
getWrappedRangeForLine(y: number): { first: number, last: number };
|
|
nextStop(x?: number): number;
|
|
prevStop(x?: number): number;
|
|
getBlankLine(attr: number, isWrapped?: boolean): IBufferLine;
|
|
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
|
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
|
}
|
|
|
|
export interface IBufferSet extends IEventEmitter {
|
|
alt: IBuffer;
|
|
normal: IBuffer;
|
|
active: IBuffer;
|
|
|
|
activateNormalBuffer(): void;
|
|
activateAltBuffer(fillAttr?: number): void;
|
|
}
|
|
|
|
export interface ISelectionManager {
|
|
selectionText: string;
|
|
selectionStart: [number, number];
|
|
selectionEnd: [number, number];
|
|
|
|
disable(): void;
|
|
enable(): void;
|
|
setSelection(row: number, col: number, length: number): void;
|
|
isClickInSelection(event: MouseEvent): boolean;
|
|
selectWordAtCursor(event: MouseEvent): void;
|
|
}
|
|
|
|
export interface ILinkifier extends IEventEmitter {
|
|
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;
|
|
platform: string;
|
|
isFirefox: boolean;
|
|
isMSIE: boolean;
|
|
isMac: boolean;
|
|
isIpad: boolean;
|
|
isIphone: boolean;
|
|
isMSWindows: boolean;
|
|
}
|
|
|
|
export interface ISoundManager {
|
|
playBellSound(): void;
|
|
}
|
|
|
|
/**
|
|
* Internal states of EscapeSequenceParser.
|
|
*/
|
|
export const enum ParserState {
|
|
GROUND = 0,
|
|
ESCAPE = 1,
|
|
ESCAPE_INTERMEDIATE = 2,
|
|
CSI_ENTRY = 3,
|
|
CSI_PARAM = 4,
|
|
CSI_INTERMEDIATE = 5,
|
|
CSI_IGNORE = 6,
|
|
SOS_PM_APC_STRING = 7,
|
|
OSC_STRING = 8,
|
|
DCS_ENTRY = 9,
|
|
DCS_PARAM = 10,
|
|
DCS_IGNORE = 11,
|
|
DCS_INTERMEDIATE = 12,
|
|
DCS_PASSTHROUGH = 13
|
|
}
|
|
|
|
/**
|
|
* Internal actions of EscapeSequenceParser.
|
|
*/
|
|
export const enum ParserAction {
|
|
IGNORE = 0,
|
|
ERROR = 1,
|
|
PRINT = 2,
|
|
EXECUTE = 3,
|
|
OSC_START = 4,
|
|
OSC_PUT = 5,
|
|
OSC_END = 6,
|
|
CSI_DISPATCH = 7,
|
|
PARAM = 8,
|
|
COLLECT = 9,
|
|
ESC_DISPATCH = 10,
|
|
CLEAR = 11,
|
|
DCS_HOOK = 12,
|
|
DCS_PUT = 13,
|
|
DCS_UNHOOK = 14
|
|
}
|
|
|
|
/**
|
|
* Internal state of EscapeSequenceParser.
|
|
* Used as argument of the error handler to allow
|
|
* introspection at runtime on parse errors.
|
|
* Return it with altered values to recover from
|
|
* faulty states (not yet supported).
|
|
* Set `abort` to `true` to abort the current parsing.
|
|
*/
|
|
export interface IParsingState {
|
|
// position in parse string
|
|
position: number;
|
|
// actual character code
|
|
code: number;
|
|
// current parser state
|
|
currentState: ParserState;
|
|
// print buffer start index (-1 for not set)
|
|
print: number;
|
|
// dcs buffer start index (-1 for not set)
|
|
dcs: number;
|
|
// osc string buffer
|
|
osc: string;
|
|
// collect buffer with intermediate characters
|
|
collect: string;
|
|
// params buffer
|
|
params: number[];
|
|
// should abort (default: false)
|
|
abort: boolean;
|
|
}
|
|
|
|
/**
|
|
* DCS handler signature for EscapeSequenceParser.
|
|
* EscapeSequenceParser handles DCS commands via separate
|
|
* subparsers that get hook/unhooked and can handle
|
|
* arbitrary amount of data.
|
|
*
|
|
* On entering a DSC sequence `hook` is called by
|
|
* `EscapeSequenceParser`. Use it to initialize or reset
|
|
* states needed to handle the current DCS sequence.
|
|
* Note: A DCS parser is only instantiated once, therefore
|
|
* you cannot rely on the ctor to reinitialize state.
|
|
*
|
|
* EscapeSequenceParser will call `put` several times if the
|
|
* parsed data got split, therefore you might have to collect
|
|
* `data` until `unhook` is called.
|
|
* Note: `data` is borrowed, if you cannot process the data
|
|
* in chunks you have to copy it, doing otherwise will lead to
|
|
* data losses or corruption.
|
|
*
|
|
* `unhook` marks the end of the current DCS sequence.
|
|
*/
|
|
export interface IDcsHandler {
|
|
hook(collect: string, params: number[], flag: number): void;
|
|
put(data: Uint32Array, start: number, end: number): void;
|
|
unhook(): void;
|
|
}
|
|
|
|
/**
|
|
* EscapeSequenceParser interface.
|
|
*/
|
|
export interface IEscapeSequenceParser extends IDisposable {
|
|
/**
|
|
* Reset the parser to its initial state (handlers are kept).
|
|
*/
|
|
reset(): void;
|
|
|
|
/**
|
|
* Parse string `data`.
|
|
* @param data The data to parse.
|
|
*/
|
|
parse(data: Uint32Array, length: number): void;
|
|
|
|
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void;
|
|
clearPrintHandler(): void;
|
|
|
|
setExecuteHandler(flag: string, callback: () => void): void;
|
|
clearExecuteHandler(flag: string): void;
|
|
setExecuteHandlerFallback(callback: (code: number) => void): void;
|
|
|
|
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void;
|
|
clearCsiHandler(flag: string): void;
|
|
setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void;
|
|
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable;
|
|
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
|
|
|
setEscHandler(collectAndFlag: string, callback: () => void): void;
|
|
clearEscHandler(collectAndFlag: string): void;
|
|
setEscHandlerFallback(callback: (collect: string, flag: number) => void): void;
|
|
|
|
setOscHandler(ident: number, callback: (data: string) => void): void;
|
|
clearOscHandler(ident: number): void;
|
|
setOscHandlerFallback(callback: (identifier: number, data: string) => void): void;
|
|
|
|
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void;
|
|
clearDcsHandler(collectAndFlag: string): void;
|
|
setDcsHandlerFallback(handler: IDcsHandler): void;
|
|
|
|
setErrorHandler(callback: (state: IParsingState) => IParsingState): void;
|
|
clearErrorHandler(): void;
|
|
}
|
|
|
|
/**
|
|
* Interface for a line in the terminal buffer.
|
|
*/
|
|
export interface IBufferLine {
|
|
length: number;
|
|
isWrapped: boolean;
|
|
get(index: number): CharData;
|
|
set(index: number, value: CharData): void;
|
|
insertCells(pos: number, n: number, ch: CharData): void;
|
|
deleteCells(pos: number, n: number, fill: CharData): void;
|
|
replaceCells(start: number, end: number, fill: CharData): void;
|
|
resize(cols: number, fill: CharData): void;
|
|
fill(fillCharData: CharData): void;
|
|
copyFrom(line: IBufferLine): void;
|
|
clone(): IBufferLine;
|
|
getTrimmedLength(): number;
|
|
translateToString(trimRight?: boolean, startCol?: number, endCol?: number): string;
|
|
}
|