xterm
[VSoRC/.git] / node_modules / xterm / src / common / parser / DcsParser.ts
1 /**
2  * Copyright (c) 2019 The xterm.js authors. All rights reserved.
3  * @license MIT
4  */
5
6 import { IDisposable } from 'common/Types';
7 import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType } from 'common/parser/Types';
8 import { utf32ToString } from 'common/input/TextDecoder';
9 import { Params } from 'common/parser/Params';
10 import { PAYLOAD_LIMIT } from 'common/parser/Constants';
11
12 const EMPTY_HANDLERS: IDcsHandler[] = [];
13
14 export class DcsParser implements IDcsParser {
15   private _handlers: IHandlerCollection<IDcsHandler> = Object.create(null);
16   private _active: IDcsHandler[] = EMPTY_HANDLERS;
17   private _ident: number = 0;
18   private _handlerFb: DcsFallbackHandlerType = () => {};
19
20   public dispose(): void {
21     this._handlers = Object.create(null);
22     this._handlerFb = () => {};
23   }
24
25   public addHandler(ident: number, handler: IDcsHandler): IDisposable {
26     if (this._handlers[ident] === undefined) {
27       this._handlers[ident] = [];
28     }
29     const handlerList = this._handlers[ident];
30     handlerList.push(handler);
31     return {
32       dispose: () => {
33         const handlerIndex = handlerList.indexOf(handler);
34         if (handlerIndex !== -1) {
35           handlerList.splice(handlerIndex, 1);
36         }
37       }
38     };
39   }
40
41   public setHandler(ident: number, handler: IDcsHandler): void {
42     this._handlers[ident] = [handler];
43   }
44
45   public clearHandler(ident: number): void {
46     if (this._handlers[ident]) delete this._handlers[ident];
47   }
48
49   public setHandlerFallback(handler: DcsFallbackHandlerType): void {
50     this._handlerFb = handler;
51   }
52
53   public reset(): void {
54     if (this._active.length) {
55       this.unhook(false);
56     }
57     this._active = EMPTY_HANDLERS;
58     this._ident = 0;
59   }
60
61   public hook(ident: number, params: IParams): void {
62     // always reset leftover handlers
63     this.reset();
64     this._ident = ident;
65     this._active = this._handlers[ident] || EMPTY_HANDLERS;
66     if (!this._active.length) {
67       this._handlerFb(this._ident, 'HOOK', params);
68     } else {
69       for (let j = this._active.length - 1; j >= 0; j--) {
70         this._active[j].hook(params);
71       }
72     }
73   }
74
75   public put(data: Uint32Array, start: number, end: number): void {
76     if (!this._active.length) {
77       this._handlerFb(this._ident, 'PUT', utf32ToString(data, start, end));
78     } else {
79       for (let j = this._active.length - 1; j >= 0; j--) {
80         this._active[j].put(data, start, end);
81       }
82     }
83   }
84
85   public unhook(success: boolean): void {
86     if (!this._active.length) {
87       this._handlerFb(this._ident, 'UNHOOK', success);
88     } else {
89       let j = this._active.length - 1;
90       for (; j >= 0; j--) {
91         if (this._active[j].unhook(success) !== false) {
92           break;
93         }
94       }
95       j--;
96       // cleanup left over handlers
97       for (; j >= 0; j--) {
98         this._active[j].unhook(false);
99       }
100     }
101     this._active = EMPTY_HANDLERS;
102     this._ident = 0;
103   }
104 }
105
106 /**
107  * Convenient class to create a DCS handler from a single callback function.
108  * Note: The payload is currently limited to 50 MB (hardcoded).
109  */
110 export class DcsHandler implements IDcsHandler {
111   private _data = '';
112   private _params: IParams | undefined;
113   private _hitLimit: boolean = false;
114
115   constructor(private _handler: (data: string, params: IParams) => any) {}
116
117   public hook(params: IParams): void {
118     this._params = params.clone();
119     this._data = '';
120     this._hitLimit = false;
121   }
122
123   public put(data: Uint32Array, start: number, end: number): void {
124     if (this._hitLimit) {
125       return;
126     }
127     this._data += utf32ToString(data, start, end);
128     if (this._data.length > PAYLOAD_LIMIT) {
129       this._data = '';
130       this._hitLimit = true;
131     }
132   }
133
134   public unhook(success: boolean): any {
135     let ret;
136     if (this._hitLimit) {
137       ret = false;
138     } else if (success) {
139       ret = this._handler(this._data, this._params ? this._params : new Params());
140     }
141     this._params = undefined;
142     this._data = '';
143     this._hitLimit = false;
144     return ret;
145   }
146 }