]> git.proxmox.com Git - mirror_xterm.js.git/blobdiff - src/Parser.ts
Merge pull request #746 from Tyriar/745_colon_word_sep
[mirror_xterm.js.git] / src / Parser.ts
index 5dab9cef3f6e847bda51e2285b76b4940cf9f6e5..00d574eec80871e9063feb26d0ebcec6ee7b19f2 100644 (file)
@@ -1,6 +1,10 @@
+/**
+ * @license MIT
+ */
+
 import { C0 } from './EscapeSequences';
 import { IInputHandler } from './Interfaces';
-import { CHARSETS } from './Charsets';
+import { CHARSETS, DEFAULT_CHARSET } from './Charsets';
 
 const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {};
 normalStateHandler[C0.BEL] = (parser, handler) => handler.bell();
@@ -37,21 +41,21 @@ escapedStateHandler['P'] = (parser, terminal) => {
 escapedStateHandler['_'] = (parser, terminal) => {
   // ESC _ Application Program Command ( APC is 0x9f).
   parser.setState(ParserState.IGNORE);
-}
+};
 escapedStateHandler['^'] = (parser, terminal) => {
   // ESC ^ Privacy Message ( PM is 0x9e).
   parser.setState(ParserState.IGNORE);
-}
+};
 escapedStateHandler['c'] = (parser, terminal) => {
   // ESC c Full Reset (RIS).
   terminal.reset();
-}
+};
 escapedStateHandler['E'] = (parser, terminal) => {
   // ESC E Next Line ( NEL is 0x85).
   terminal.x = 0;
   terminal.index();
   parser.setState(ParserState.NORMAL);
-}
+};
 escapedStateHandler['D'] = (parser, terminal) => {
   // ESC D Index ( IND is 0x84).
   terminal.index();
@@ -66,10 +70,11 @@ escapedStateHandler['%'] = (parser, terminal) => {
   // ESC % Select default/utf-8 character set.
   // @ = default, G = utf-8
   terminal.setgLevel(0);
-  terminal.setgCharset(0, CHARSETS.US);
+  terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
   parser.setState(ParserState.NORMAL);
   parser.skipNextChar();
 };
+escapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
 
 const csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {};
 csiParamStateHandler['?'] = (parser) => parser.setPrefix('?');
@@ -90,8 +95,9 @@ csiParamStateHandler['"'] = (parser) => parser.setPostfix('"');
 csiParamStateHandler[' '] = (parser) => parser.setPostfix(' ');
 csiParamStateHandler['\''] = (parser) => parser.setPostfix('\'');
 csiParamStateHandler[';'] = (parser) => parser.finalizeParam();
+csiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
 
-const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string) => void} = {};
+const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {};
 csiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params);
 csiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params);
 csiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params);
@@ -132,9 +138,15 @@ csiStateHandler['p'] = (handler, params, prefix) => {
     case '!': handler.softReset(params); break;
   }
 };
+csiStateHandler['q'] = (handler, params, prefix, postfix) => {
+  if (postfix === ' ') {
+    handler.setCursorStyle(params);
+  }
+};
 csiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params);
 csiStateHandler['s'] = (handler, params) => handler.saveCursor(params);
 csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params);
+csiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL);
 
 enum ParserState {
   NORMAL = 0,
@@ -147,6 +159,11 @@ enum ParserState {
   IGNORE = 7
 }
 
+/**
+ * The terminal's parser, all input into the terminal goes through the parser
+ * which parses and defers the actual input handling the the IInputHandler
+ * specified in the constructor.
+ */
 export class Parser {
   private _state: ParserState;
   private _position: number;
@@ -159,7 +176,12 @@ export class Parser {
     this._state = ParserState.NORMAL;
   }
 
-  public parse(data: string) {
+  /**
+   * Parse and handle data.
+   *
+   * @param data The data to parse.
+   */
+  public parse(data: string): ParserState {
     let l = data.length, j, cs, ch, code, low;
 
     this._position = 0;
@@ -330,56 +352,13 @@ export class Parser {
           break;
 
         case ParserState.CHARSET:
-          switch (ch) {
-            case '0': // DEC Special Character and Line Drawing Set.
-              cs = CHARSETS.SCLD;
-              break;
-            case 'A': // UK
-              cs = CHARSETS.UK;
-              break;
-            case 'B': // United States (USASCII).
-              cs = CHARSETS.US;
-              break;
-            case '4': // Dutch
-              cs = CHARSETS.Dutch;
-              break;
-            case 'C': // Finnish
-            case '5':
-              cs = CHARSETS.Finnish;
-              break;
-            case 'R': // French
-              cs = CHARSETS.French;
-              break;
-            case 'Q': // FrenchCanadian
-              cs = CHARSETS.FrenchCanadian;
-              break;
-            case 'K': // German
-              cs = CHARSETS.German;
-              break;
-            case 'Y': // Italian
-              cs = CHARSETS.Italian;
-              break;
-            case 'E': // NorwegianDanish
-            case '6':
-              cs = CHARSETS.NorwegianDanish;
-              break;
-            case 'Z': // Spanish
-              cs = CHARSETS.Spanish;
-              break;
-            case 'H': // Swedish
-            case '7':
-              cs = CHARSETS.Swedish;
-              break;
-            case '=': // Swiss
-              cs = CHARSETS.Swiss;
-              break;
-            case '/': // ISOLatin (actually /A)
-              cs = CHARSETS.ISOLatin;
-              this._position++;
-              break;
-            default: // Default
-              cs = CHARSETS.US;
-              break;
+          if (ch in CHARSETS) {
+            cs = CHARSETS[ch];
+            if (ch === '/') { // ISOLatin is actually /A
+              this.skipNextChar();
+            }
+          } else {
+            cs = DEFAULT_CHARSET;
           }
           this._terminal.setgCharset(this._terminal.gcharset, cs);
           this._terminal.gcharset = null;
@@ -479,7 +458,7 @@ export class Parser {
 
         case ParserState.CSI:
           if (ch in csiStateHandler) {
-            csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix);
+            csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this);
           } else {
             this._terminal.error('Unknown CSI code: %s.', ch);
           }
@@ -492,6 +471,8 @@ export class Parser {
         case ParserState.DCS:
           if (ch === C0.ESC || ch === C0.BEL) {
             if (ch === C0.ESC) this._position++;
+            let pt;
+            let valid: boolean;
 
             switch (this._terminal.prefix) {
               // User-Defined Keys (DECUDK).
@@ -501,8 +482,8 @@ export class Parser {
               // Request Status String (DECRQSS).
               // test: echo -e '\eP$q"p\e\\'
               case '$q':
-                let pt = this._terminal.currentParam
-                valid = false;
+                pt = this._terminal.currentParam;
+                valid = false;
 
                 switch (pt) {
                   // DECSCA
@@ -547,9 +528,8 @@ export class Parser {
               // This can cause a small glitch in vim.
               // test: echo -ne '\eP+q6b64\e\\'
               case '+q':
-                // TODO: Don't declare pt twice
-                /*let*/ pt = this._terminal.currentParam
-                , valid = false;
+                pt = this._terminal.currentParam;
+                valid = false;
 
                 this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\');
                 break;
@@ -584,37 +564,74 @@ export class Parser {
           break;
       }
     }
+    return this._state;
   }
 
+  /**
+   * Set the parser's current parsing state.
+   *
+   * @param state The new state.
+   */
   public setState(state: ParserState): void {
     this._state = state;
   }
 
+  /**
+   * Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>'
+   * or '!'.
+   *
+   * @param prefix The prefix.
+   */
   public setPrefix(prefix: string): void {
     this._terminal.prefix = prefix;
   }
 
+  /**
+   * Sets the parsier's current prefix. CSI codes can have postfixes of '$',
+   * '"', ' ', '\''.
+   *
+   * @param postfix The postfix.
+   */
+  public setPostfix(postfix: string): void {
+    this._terminal.postfix = postfix;
+  }
+
+  /**
+   * Sets the parser's current parameter.
+   *
+   * @param param the parameter.
+   */
   public setParam(param: number) {
     this._terminal.currentParam = param;
   }
 
+  /**
+   * Gets the parser's current parameter.
+   */
   public getParam(): number {
     return this._terminal.currentParam;
   }
 
+  /**
+   * Finalizes the parser's current parameter, adding it to the list of
+   * parameters and setting the new current parameter to 0.
+   */
   public finalizeParam(): void {
     this._terminal.params.push(this._terminal.currentParam);
     this._terminal.currentParam = 0;
   }
 
-  public setPostfix(postfix: string): void {
-    this._terminal.postfix = postfix;
-  }
-
+  /**
+   * Tell the parser to skip the next character.
+   */
   public skipNextChar(): void {
     this._position++;
   }
 
+  /**
+   * Tell the parser to repeat parsing the current character (for example if it
+   * needs parsing using a different state.
+   */
   // public repeatChar(): void {
   //   this._position--;
   // }