-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
hooks for custom control sequences #1813
Changes from 9 commits
4d660de
b689745
0311563
30a667c
8ad2d1b
ffb2708
53fd04a
8ceea11
6351a5b
5af4626
8a5a032
6b65ebd
7173577
9f603a2
a4cc87d
2551a53
d3d3064
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
*/ | ||
|
||
import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types'; | ||
import { IDisposable } from 'xterm'; | ||
import { Disposable } from './common/Lifecycle'; | ||
|
||
/** | ||
|
@@ -41,7 +42,7 @@ export class TransitionTable { | |
* @param action parser action to be done | ||
* @param next next parser state | ||
*/ | ||
add(code: number, state: number, action: number | null, next: number | null): void { | ||
add(code: number, state: number, action: number | null, next: number | null): void { | ||
this.table[state << 8 | code] = ((action | 0) << 4) | ((next === undefined) ? state : next); | ||
} | ||
|
||
|
@@ -303,6 +304,32 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP | |
this._executeHandlerFb = callback; | ||
} | ||
|
||
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { | ||
const index = flag.charCodeAt(0); | ||
const oldHead = this._csiHandlers[index]; | ||
const newHead = Object.assign( | ||
(params: number[], collect: string): void => { | ||
if (callback(params, collect)) { } | ||
else if (newHead.nextHandler) { newHead.nextHandler(params, collect); } | ||
else { this._csiHandlerFb(collect, params, index); } | ||
}, | ||
{ nextHandler: oldHead, | ||
dispose(): void { | ||
let previous = null; let cur = this._csiHandlers[index]; | ||
for (; cur && cur.nextHandler; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this still work with cycles, like someone added a handler twice by accident? Also an cycle might prevent the autoclean up by the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it should be ok. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yupp found no issues with it (handler is recreated anyway). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Much cleaner with this management method. 👍 |
||
previous = cur, cur = cur.nextHandler) { | ||
if (cur === newHead) { | ||
if (previous) { previous.nextHandler = cur.nextHandler; } | ||
else { this._csiHandlers[index] = cur.nextHandler; } | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
this._csiHandlers[index] = newHead; | ||
return newHead; | ||
} | ||
|
||
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void { | ||
this._csiHandlers[flag.charCodeAt(0)] = callback; | ||
} | ||
|
@@ -323,6 +350,30 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP | |
this._escHandlerFb = callback; | ||
} | ||
|
||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable { | ||
const oldHead = this._oscHandlers[ident]; | ||
const newHead = Object.assign( | ||
Tyriar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(data: string): void => { | ||
if (callback(data)) { } | ||
Tyriar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else if (newHead.nextHandler) { newHead.nextHandler(data); } | ||
else { this._oscHandlerFb(ident, data); } | ||
}, | ||
{ nextHandler: oldHead, | ||
dispose(): void { | ||
let previous = null; let cur = this._oscHandlers[ident]; | ||
for (; cur && cur.nextHandler; | ||
previous = cur, cur = cur.nextHandler) { | ||
if (cur === newHead) { | ||
if (previous) { previous.nextHandler = cur.nextHandler; } | ||
else { this._oscHandlers[ident] = cur.nextHandler; } | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
this._oscHandlers[ident] = newHead; | ||
return newHead; | ||
} | ||
setOscHandler(ident: number, callback: (data: string) => void): void { | ||
this._oscHandlers[ident] = callback; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,6 +182,14 @@ export interface IInputHandler { | |
ESC ~ */ setgLevel(level: number): void; | ||
} | ||
|
||
/* | ||
* An InputHandler for VT-style terminals | ||
*/ | ||
export interface IVtInputHandler extends IInputHandler { | ||
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; | ||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; | ||
} | ||
|
||
export interface ILinkMatcher { | ||
id: number; | ||
regex: RegExp; | ||
|
@@ -492,6 +500,8 @@ export interface IEscapeSequenceParser extends IDisposable { | |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jerch are the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Tyriar, @PerBothner: Not future proof, its still a subject to change, but imho good to go for now. I will address this with one of the typed array transitions to come. |
||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; | ||
|
||
setEscHandler(collectAndFlag: string, callback: () => void): void; | ||
clearEscHandler(collectAndFlag: string): void; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
*/ | ||
|
||
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings } from 'xterm'; | ||
import { ITerminal } from '../Types'; | ||
import { ITerminal, IInputHandler } from '../Types'; | ||
import { Terminal as TerminalCore } from '../Terminal'; | ||
import * as Strings from '../Strings'; | ||
|
||
|
@@ -15,6 +15,9 @@ export class Terminal implements ITerminalApi { | |
this._core = new TerminalCore(options); | ||
} | ||
|
||
public get inputHandler(): IInputHandler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's change this to expose the following interface (pending @jerch's comments on whether the types are future proof): class Terminal {
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable;
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
} You'll then call through to You should also merge There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, they will most likely change type to a borrowed typed array, but not until 3.11 since I have to wait for the JS Array buffer to be gone. So not sure if we should include it yet in the public API, well marking them "experimental" or "unstable" would work for me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I simplified things as suggested (as I understand it). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this becomes API we'll need an entry also in xterm.d.ts as well as a test in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in commit 6b65ebd. |
||
return (this._core as TerminalCore).inputHandler; | ||
} | ||
public get element(): HTMLElement { return this._core.element; } | ||
public get textarea(): HTMLTextAreaElement { return this._core.textarea; } | ||
public get rows(): number { return this._core.rows; } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yupp this will work without penalty if no other handler is attached 👍
One thing though: can we make this management code more general usable, like in a private method called for CSI, OSC etc. when needed. This way the code is cleaner without duplication and feels less monkey patching, and can also be used for ESC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See commit 8a5a032 "New method _linkHandler used by both addCsiHandler and addOscHandler." I had some problems with the type-checking, and the resulting code is a bit ugly, but not too bad, I think.