[
  {
    "path": ".dockerignore",
    "content": "node_modules/\ndist/\ncompiled/\ntest/\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules/\n/compiled/src/*\n/tmp/*\n/npm-debug.log\n/dist/*\n\n# Created by https://www.gitignore.io/api/osx\n\n### OSX ###\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n# Directory created by Visual Studio Code\n.vscode\n\n# Directory created by IDEA\n.idea\n"
  },
  {
    "path": ".node-version",
    "content": "8.11.0\n"
  },
  {
    "path": ".npmrc",
    "content": "save-exact = true\nregistry = https://registry.npmjs.org\n"
  },
  {
    "path": ".nvmrc",
    "content": "v8.11.0\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\n\nbefore_script:\n  - if [[ \"$TRAVIS_OS_NAME\" == \"linux\" ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; fi\n\nmatrix:\n  include:\n    - os: linux\n      sudo: required\n      dist: trusty\n#    - os: osx\n#      osx_image: xcode7.3\n\ncache:\n  directories:\n    - node_modules\n\ndeploy:\n  skip_cleanup: true\n  provider: script\n  script: npm run pack\n  on:\n    tags: true\n\nenv:\n  global:\n    # GH_TOKEN\n    secure: LkI4RAc08x0XsiMlK0cIKFy381qJ8v8WKepjhAGdwbNgPqDcMqLXSznTTaGTcN1hKonHBPJVL9l0G02oRjj/ulh/yeGV1Q/JRPBm2XLoG0EX6hcuDj/skaLO24fuYf/miYejZbJcRBj2F/OP7sR1SfQPl6dfzEnLj96IHCUxzEBbB6PPJdGRL35117HqD0Y/LGLPkHgdh53n/FEtsEbl6DwvkPr8+yDZxUCqTJW53ZoBJzf9znZE5gMZg6btkStWTUuM5n5doe446ipRouGtomOXkgsCtQbsd66cRzdlIlEGKUEDaEL/c4KDMIUUDw2MpxoUm2fDJ98krxRLhvANgN/rqQVBoYY45OT7SzK9TYcvqS36E6a9pdmFpt0M3w532f5E6simgJp1a6gBoSBBYoZL8hRscF2VgAvjJV0QVQzos6Ec01nGjAbpC/i2B6IR6tnI1L5C3YHR4xDvSqW3iDo3hpc+Y4INOMysMt3cK+oWx3bEsbH8G3JRbU6Edz5vUVQ5aeoyVgfr/vxhIohWq8NpGd8zqdyyfKq59DYugkDNpvKr07w+FQZLtexILKw6FzNEjm3gI9prz5a7+WzFLQcdgy6xFM7z3GN5e1kbX39BSMcmkueWk96kCYSpKOflAX5h+WOs3VjwVxoIaB5uYeQD10L+6703eSyXy3Ni3Sg=\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "I have found a bug!\n-------------------\n\nAwesome, but before you [report it to us](issues/new), make sure to [check whether this has already been reported](issues?q=is%3Aissue).\nIf not, before reporting the issue you'll need to gather some information by following these instructions:\n\n1. Make sure you are using the latest version of the application:\n\n  ```bash\n  git pull\n  rm -r \"/Applications/Upterm\"*\n  npm run pack\n  ```\n2. If the bug is still present, [open an issue](issues/new).\n3. Write steps to reproduce the bug.\n4. Take some screenshots.\n5. Gather debug logs.\n\n 5.1. Open developer tools (View -> Toggle Developer Tools).\n\n 5.2. Find Console.\n\n 5.3. Copy the output and paste it into the issue.\n\nDeveloping\n----------\n\nClone the repo, then `npm start`. This will install dependencies, build,\nand run Electron. You may need additional packages, such as `libgconf2`\n([reference](https://github.com/railsware/upterm/issues/1320)).\n\nI have some important changes!\n------------------------------\n\n1. [Clone the repo](https://help.github.com/articles/importing-a-git-repository-using-the-command-line/).\n2. [Create a separate branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches) (to prevent unrelated updates).\n3. Apply your changes.\n4. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/).\n5. Describe what has been done.\n\nTest\n----\n\n* Install [selenium-standalone](https://github.com/vvo/selenium-standalone)\n* `selenium-standalone start`\n* `npm run test`\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:8.11.0\n\nRUN mkdir /upterm\nWORKDIR /upterm\n\nCOPY package.json .\nCOPY .npmrc .\n\nRUN npm install\nCOPY . /upterm\nRUN npm run pack\n\nVOLUME /dist\nCMD cp /upterm/dist/*.AppImage /dist\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Volodymyr Shatsky\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "[![Join the chat at https://gitter.im/railsware/upterm](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/upterm/Lobby)\n[![Build Status](https://travis-ci.org/railsware/upterm.svg?branch=master)](https://travis-ci.org/railsware/upterm)\n\n# Deprecated\n\nUpterm is deprecated. Project had [lost maintainer](https://github.com/railsware/upterm/issues/1301#issue-327003344) and have zero activity to support it from community (only issues was created in repo).\n\nI am no longer accepting pull requests and issues.\n\nI recomended to check [Hyper](https://hyper.is/) instead.\n\nWhat Is It?\n-----------\n\n**[Upterm is looking for maintainers](https://github.com/railsware/upterm/issues/1301)**\n\nUpterm (formerly Black Screen) is an IDE in the world of terminals. Strictly speaking, it's both a\nterminal emulator and an *interactive* shell based on [Electron](http://electron.atom.io/).\n\n![](README/main.png)\n\n###### Autocompletion\n\nUpterm shows the autocompletion box as you type and tries to be smart about what to suggest.\nOften you can find useful additional information on the right side of the autocompletion, e.g. expanded alias value,\ncommand descriptions, value of the previous directory (`cd -`), etc.\n\n###### Compatibility\n\nAll command-line programs (including emacs, ssh and vim) should work as expected. If you experience any glitches, please [create an issue](https://github.com/railsware/upterm/issues/new).\n\nInstall\n------------\n\n###### MacOS\n\n```bash\nbrew cask install upterm\n```\n\nBeware that the version in Homebrew might be outdated. Visit the [releases](https://github.com/railsware/upterm/releases) page to download the latest version.\n\n###### Linux *(Arch Linux)*\n```bash\nyaourt -S upterm\n```\n\nAs with macOS's `brew` install, the AUR may also be outdated. To install the latest version, refer to the [install guide for Linux (Others)](#linux-others).\n\n###### Linux *(Others)*\n\n* Download and open the AppImage file from the [releases](https://github.com/railsware/upterm/releases) page.\n\n###### Windows\n\nWindows is not officially supported at the moment. The [Windows Support](https://github.com/railsware/upterm/issues/800) Issue explains potential experimental support.\n\nTechnologies\n------------\n\n* [Electron](http://electron.atom.io/)\n* [TypeScript](http://www.typescriptlang.org/)\n* [ReactJS](https://facebook.github.io/react/)\n\n\nMore Screenshots\n----------------\n\n![](README/npm_autocompletion.png)\n![](README/error.png)\n![](README/top_autocompletion.png)\n![](README/json_prettyfier.png)\n![](README/vim.png)\n![](README/emacs.png)\n![](README/htop.png)\n![](README/cd.png)\n\nDevelopment Setup\n------------\n\n```bash\ngit clone https://github.com/railsware/upterm.git && cd upterm\nnpm start\n```\nInstructions are available for [debugging the application in Visual Studio Code](docs/vscodedebugging.md).\n\nTo create a standalone application, execute `npm run pack` in the project directory.\n\nContributing\n------------\n\nSee [Contributing Guide](CONTRIBUTING.md).\n\nLicense\n-------\n\n[The MIT License](LICENSE).\n"
  },
  {
    "path": "docs/vscodedebugging.md",
    "content": "# Debugging Upterm in Visual Studio Code\n\nMicrosoft's open source Visual Studio Code (vscode) provides debugging for Typescript based applications.\n\nThe recommended steps for debugging Upterm in vscode are described in the steps below.\n\n## Step 1. Install the Debugger for Chrome Extension\n\nTo debug Electron based applications the vscode \"Debugger for Chrome\" extension is required.\n\nTo install this extension choose the <b>Extensions</b> icon on the left hand side. Then search for and select the <b>Debugger for Chrome</b>. The extension should take about a minute to install after which you will be prompted to reload vscode.\n\n![chrome debugger](images/install_chrome_debug_ext.png \"chrome debugger\")\n\n## Step 2. Setup vscode project build and debug configuration\n\nUnder the `.vscode` folder create the `launch.json` and `tasks.json` files.\n\n![.vscode folder](images/dot-vscode-folder.png \".vscode folder\")\n\nThe contents of these files should be as follows.\n\n<b>a. launch.json</b>\n```\n{\n    // Use IntelliSense to learn about possible Node.js debug attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n\n        {\n            \"type\": \"node\",\n            \"request\": \"launch\",\n            \"name\": \"Electron Main\",\n            // Mac OS & Linux process runtime executable\n            \"runtimeExecutable\": \"${workspaceRoot}/node_modules/.bin/electron\",\n            \"windows\": {\n                // Windows process runtime executable\n                \"runtimeExecutable\": \"${workspaceRoot}\\\\node_modules\\\\.bin\\\\electron.cmd\"\n            },\n            \"program\": \"${workspaceRoot}/compiled/src/main/Main.js\",\n            \"protocol\": \"inspector\",\n            \"stopOnEntry\": false,\n            \"args\": [],\n            \"cwd\": \"${workspaceRoot}/\",\n            \"runtimeArgs\": [\n                \"--enable-logging\"\n            ],\n            \"env\": {},\n            \"sourceMaps\": true,\n            \"outFiles\": [\n                \"${workspaceRoot}/compiled/src\"\n            ],\n            \"internalConsoleOptions\": \"openOnSessionStart\",\n            \"console\": \"integratedTerminal\"\n        },\n        {\n            \"name\": \"Debug renderer process\",\n            \"type\": \"chrome\",\n            \"request\": \"launch\",\n            // Mac OS & Linux process runtime executable\n            \"runtimeExecutable\": \"${workspaceRoot}/node_modules/.bin/electron\",\n            \"windows\": {\n                // Windows process runtime executable\n                \"runtimeExecutable\": \"${workspaceRoot}\\\\node_modules\\\\.bin\\\\electron.cmd\"\n            },\n            \"runtimeArgs\": [\n                \"${workspaceRoot}/\",\n                \"--enable-logging\",\n                \"--remote-debugging-port=9222\"\n            ],\n            \"webRoot\": \"${workspaceRoot}/\",\n            \"sourceMaps\": true,\n            \"internalConsoleOptions\": \"openOnSessionStart\"\n        }\n    ]\n}\n```\n\n<b>b. tasks.json</b>\n```\n{\n    \"version\": \"2.0.0\",\n    \"presentation\": {\n        \"echo\": true,\n        \"reveal\": \"always\",\n        \"focus\": false,\n        \"panel\": \"new\"\n    },\n    \"tasks\": [\n        {\n            // Custom prestart task i.e. npm run prestart.\n            \"taskName\": \"prestart\",\n            \"command\": \"npm\",\n            \"args\": [\n                \"run\",\n                \"prestart\"\n            ],\n            \"type\": \"shell\",\n            \"group\": \"build\",\n            // Specify tsc problem matcher.\n            \"problemMatcher\": [\"$tsc-watch\"]\n        },\n        {\n            // Default compile task from package.json i.e. npm run compile.\n            \"type\": \"npm\",\n            \"script\": \"compile\",\n            \"group\": \"build\",\n            // Specify tsc problem matcher.\n            \"problemMatcher\": [\"$tsc-watch\"]\n        }\n    ]\n}\n```\n\n## Step 3. Build the project\n\nAfter first install of Upterm and after each time Upterm is modified the project needs to be re-built before launching a debug session.\n\nTo build the project in vscode open the activity search box by selecting the `⌘ + p` keys on Mac OS (or `Ctrl + p` on Linux and Windows), and then enter `task prestart`. Or alternatively from the command line run `npn run prestart`.\n\n![prestart task launch](images/launch_task_prestart.png \"prestart task launch\")\n\nIf the code has been modified and no updates to dependent node modules were made you can compile the project by entering `task compile` into the search box. Or from the command line run `npn run compile`.\n\nNote. The compile task is a npm script defined in `package.json` which vscode detects and displays as `npm: compile` in the search box.\n\n## Step 4. Debug the project\n\nSource maps are enabled for the Upterm project which allows the Typescript code to be debugged. Source maps map the Typescript code to the generated (transpiled) Javascript code, with the Javascript code being what's executed by node.js.\n\nTo enter debugging mode select the <b>Debug</b> icon on the left hand side menu. Or alternatively, select the `Shift + ⌘ + d` (or `Shift + Ctrl + d`) keys.\n\n![debug process](images/debug_renderer_process.png \"debug process\")\n\nTo launch a debug session, from the top left hand side of the <b>Debug</b> window select `Debug rendered process` and press the Play button.\nNote. A breakpoint needs to be placed in the code prior to launching a debug session, to allow stepping through code.\n\nThese instructions were tested on vscode version 1.16.0.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"upterm\",\n  \"productName\": \"Upterm\",\n  \"description\": \"A terminal emulator for the 21st century.\",\n  \"version\": \"0.4.4\",\n  \"main\": \"compiled/src/main/Main.js\",\n  \"author\": \"Volodymyr Shatskyi <shockone89@gmail.com>\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/railsware/upterm.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/railsware/upterm/issues\"\n  },\n  \"engines\": {\n    \"node\": \">=8.0.0 || >=10.0.0\"\n  },\n  \"keywords\": [\n    \"terminal\",\n    \"emulator\",\n    \"shell\",\n    \"console\"\n  ],\n  \"dependencies\": {\n    \"child-process-promise\": \"2.2.1\",\n    \"chokidar\": \"2.0.3\",\n    \"classnames\": \"2.2.5\",\n    \"csv-parse\": \"2.2.0\",\n    \"csv-stringify\": \"3.0.0\",\n    \"dirStat\": \"0.0.2\",\n    \"font-awesome\": \"4.7.0\",\n    \"fs-extra\": \"5.0.0\",\n    \"immutable\": \"3.8.2\",\n    \"klaw\": \"2.1.1\",\n    \"lodash\": \"4.17.11\",\n    \"mode-to-permissions\": \"0.0.2\",\n    \"monaco-editor\": \"vlad-shatskyi/monaco-editor\",\n    \"node-ansiparser\": \"2.2.0\",\n    \"node-pty\": \"0.7.4\",\n    \"react\": \"16.2.0\",\n    \"react-dom\": \"16.2.0\",\n    \"rxjs\": \"5.5.8\",\n    \"tinycolor2\": \"1.4.1\"\n  },\n  \"devDependencies\": {\n    \"@types/chai\": \"4.1.3\",\n    \"@types/chokidar\": \"1.7.5\",\n    \"@types/classnames\": \"2.2.3\",\n    \"@types/csv-parse\": \"1.1.11\",\n    \"@types/csv-stringify\": \"1.4.2\",\n    \"@types/enzyme\": \"3.1.10\",\n    \"@types/fs-extra\": \"5.0.2\",\n    \"@types/klaw\": \"2.1.1\",\n    \"@types/lodash\": \"4.14.116\",\n    \"@types/mocha\": \"5.2.0\",\n    \"@types/node\": \"9.6.1\",\n    \"@types/react\": \"16.1.0\",\n    \"@types/webdriverio\": \"4.10.1\",\n    \"chai\": \"4.1.2\",\n    \"concurrently\": \"3.5.1\",\n    \"cpx\": \"1.5.0\",\n    \"cross-env\": \"5.1.4\",\n    \"devtron\": \"1.4.0\",\n    \"electron\": \"2.0.10\",\n    \"electron-builder\": \"20.28.4\",\n    \"electron-mocha\": \"6.0.4\",\n    \"enzyme\": \"3.3.0\",\n    \"mkdirp\": \"0.5.1\",\n    \"mocha\": \"5.2.0\",\n    \"npm-check-updates\": \"2.14.2\",\n    \"react-addons-test-utils\": \"15.6.2\",\n    \"rimraf\": \"2.6.2\",\n    \"spectron\": \"3.8.0\",\n    \"ts-node\": \"5.0.1\",\n    \"tslint\": \"5.9.1\",\n    \"typescript\": \"2.8.1\"\n  },\n  \"scripts\": {\n    \"preinstall\": \"npm prune\",\n    \"postinstall\": \"electron-builder install-app-deps\",\n    \"pack\": \"build\",\n    \"electron\": \"electron .\",\n    \"prestart\": \"npm install && npm run compile\",\n    \"start\": \"concurrently --kill-others -s first \\\"tsc --watch\\\" \\\"cross-env NODE_ENV=development npm run electron\\\"\",\n    \"test\": \"npm run lint && npm run compile && npm run unit-tests && npm run ui-tests && npm run integration-tests\",\n    \"unit-tests\": \"NODE_ENV=test electron-mocha --require ts-node/register $(find test -name '*_spec.ts')\",\n    \"ui-tests\": \"NODE_ENV=test electron-mocha --require ts-node/register $(find test -name '*_spec.tsx')\",\n    \"integration-tests\": \"NODE_ENV=test electron-mocha --require ts-node/register test/e2e.ts\",\n    \"update-dependencies\": \"ncu -u\",\n    \"lint\": \"tslint `find src -name '*.ts*'` `find test -name '*.ts*'`\",\n    \"cleanup\": \"rimraf compiled/src\",\n    \"copy-html\": \"mkdirp compiled/src/views && cpx src/views/index.html compiled/src/views\",\n    \"compile\": \"npm run cleanup && npm run tsc && npm run copy-html\",\n    \"tsc\": \"tsc\"\n  },\n  \"license\": \"MIT\",\n  \"build\": {\n    \"appId\": \"com.github.railsware.upterm\",\n    \"linux\": {\n      \"icon\": \"icons\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/Autocompletion.ts",
    "content": "import {leafNodeAt, ASTNode} from \"./shell/Parser\";\nimport * as _ from \"lodash\";\nimport {Environment} from \"./shell/Environment\";\nimport {OrderedSet} from \"./utils/OrderedSet\";\nimport {Aliases} from \"./shell/Aliases\";\n\ntype GetSuggestionsOptions = {\n    currentText: string;\n    currentCaretPosition: number;\n    ast: ASTNode;\n    environment: Environment;\n    historicalPresentDirectoriesStack: OrderedSet<string>;\n    aliases: Aliases;\n};\n\nexport async function getSuggestions({\n    currentCaretPosition,\n    ast,\n    environment,\n    historicalPresentDirectoriesStack,\n    aliases,\n}: GetSuggestionsOptions): Promise<monaco.languages.CompletionList> {\n    const node = leafNodeAt(currentCaretPosition, ast);\n    const suggestions = await node.suggestions({\n        environment: environment,\n        historicalPresentDirectoriesStack: historicalPresentDirectoriesStack,\n        aliases: aliases,\n    });\n\n    const uniqueSuggestions = _.uniqBy(suggestions, suggestion => suggestion.label);\n\n    return {\n        isIncomplete: false,\n        items: uniqueSuggestions.map(suggestion => ({\n            ...suggestion,\n            kind: monaco.languages.CompletionItemKind.Interface,\n        })),\n    };\n}\n"
  },
  {
    "path": "src/Char.ts",
    "content": "import {Attributes} from \"./Interfaces\";\nimport {Brightness, Weight, Color} from \"./Enums\";\n\nexport const defaultAttributes = Object.freeze({\n    inverse: false,\n    color: Color.White,\n    backgroundColor: Color.Black,\n    brightness: Brightness.Normal,\n    weight: Weight.Normal,\n    underline: false,\n    crossedOut: false,\n    blinking: false,\n    cursor: false,\n});\n\nexport interface Char {\n    value: string;\n    attributes: Attributes;\n}\n\nexport function createChar(char: string, attributes: Attributes): Char {\n    if (char.length !== 1) {\n        throw(`Char can be created only from a single character; passed ${char.length}: ${char}`);\n    }\n\n    return {\n        value: char,\n        attributes: attributes,\n    };\n}\n"
  },
  {
    "path": "src/Decorators.ts",
    "content": "import * as _ from \"lodash\";\n\nexport function memoize(resolver: ((...args: any[]) => any) | undefined = undefined) {\n    if (typeof resolver !== \"function\") {\n        resolver = (...args: any[]) => JSON.stringify(args);\n    }\n\n    return (_target: any, _name: string, descriptor: TypedPropertyDescriptor<any>) => {\n        descriptor.value = _.memoize(descriptor.value, resolver);\n\n        return descriptor;\n    };\n}\n\nexport const memoizeAccessor = <T>(_target: Object, name: string | symbol, descriptor: TypedPropertyDescriptor<T>) => {\n    const memoizedPropertyName = `__memoized_${name}`;\n    const originalGetter = descriptor.get;\n\n    descriptor.get = function (this: any) {\n        if (!this[memoizedPropertyName]) {\n            this[memoizedPropertyName] = originalGetter!.call(this);\n        }\n\n        return this[memoizedPropertyName];\n    };\n\n    return descriptor;\n};\n\nexport function debounce(wait: number = 0) {\n    return (_target: any, _name: string, descriptor: PropertyDescriptor) => {\n        descriptor.value = _.debounce(descriptor.value, wait);\n\n        return descriptor;\n    };\n}\n"
  },
  {
    "path": "src/EmitterWithUniqueID.ts",
    "content": "import * as events from \"events\";\n\nexport class EmitterWithUniqueID extends events.EventEmitter {\n    public id: number;\n\n    constructor() {\n        super();\n        this.id = Date.now();\n    }\n}\n"
  },
  {
    "path": "src/Enums.ts",
    "content": "/**\n * @link https://css-tricks.com/snippets/javascript/javascript-keycodes/\n * @link https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html\n */\nexport enum KeyCode {\n    Bell = 7,\n    Backspace = 8,\n    Tab = 9,\n    NewLine = 10,\n    VerticalTab = 11,\n    CarriageReturn = 13,\n    ShiftOut = 14,\n    ShiftIn = 15,\n    Shift = 16,\n    Ctrl = 17,\n    Alt = 18,\n    CapsLock = 20,\n    Escape = 27,\n    Space = 32,\n    Left = 37,\n    Up = 38,\n    Right = 39,\n    Down = 40,\n    One = 49,\n    Nine = 57,\n    A = 65,\n    B = 66,\n    C = 67,\n    D = 68,\n    E = 69,\n    F = 70,\n    G = 71,\n    H = 72,\n    I = 73,\n    J = 74,\n    K = 75,\n    L = 76,\n    M = 77,\n    N = 78,\n    O = 79,\n    P = 80,\n    Q = 81,\n    R = 82,\n    S = 83,\n    T = 84,\n    U = 85,\n    V = 86,\n    W = 87,\n    X = 88,\n    Y = 89,\n    Z = 90,\n    Meta = 91,\n    Delete = 127,\n    Underscore = 189,\n    Period = 190,\n    VerticalBar = 220,\n    AltGraph = 225,\n}\n\nexport enum Color {\n    Black,\n    Red,\n    Green,\n    Yellow,\n    Blue,\n    Magenta,\n    Cyan,\n    White,\n}\n\nexport enum Status {\n    InProgress = \"in-progress\",\n    Failed = \"failed\",\n    Success = \"success\",\n}\nexport enum ScreenMode {\n    Light = \"light\",\n    Dark = \"dark\",\n}\n\nexport enum BufferType {\n    Normal = \"normal\",\n    Alternate = \"alternate\",\n}\n\nexport enum Weight {\n    Normal = \"normal\",\n    Bold = \"bold\",\n    Faint = \"faint\",\n}\n\nexport enum Brightness {\n    Normal = \"normal\",\n    Bright = \"bright\",\n}\n\nexport enum LogLevel {\n    Info = \"info\",\n    Log = \"log\",\n    Error = \"error\",\n}\n\nexport enum KeyboardAction {\n    // CLI commands\n    cliClearJobs,\n    cliClearText,\n    cliAppendLastArgumentOfPreviousCommand,\n    cliHistoryPrevious,\n    cliHistoryNext,\n    // autocomplete commands\n    autocompleteInsertCompletion,\n    autocompletePreviousSuggestion,\n    autocompleteNextSuggestion,\n    // tab commands\n    tabNew,\n    tabFocus,\n    tabPrevious,\n    tabNext,\n    // session commands\n    otherSession,\n    sessionClose,\n    // edit/clipboard commands\n    clipboardCopy,\n    clipboardPaste,\n    editFind,\n    editFindClose,\n    increaseFontSize,\n    decreaseFontSize,\n    resetFontSize,\n    // view commands\n    viewToggleFullScreen,\n    // Upterm commands\n    uptermQuit,\n    // developer\n    toggleDeveloperTools,\n}\n"
  },
  {
    "path": "src/Interfaces.ts",
    "content": "import {Weight, Brightness} from \"./Enums\";\nimport {Stats} from \"fs\";\nimport {ReactElement} from \"react\";\nimport {Job} from \"./shell/Job\";\nimport {Session} from \"./shell/Session\";\nimport {Suggestion} from \"./plugins/completion_utils/Common\";\nimport {Output} from \"./Output\";\nimport {Environment} from \"./shell/Environment\";\nimport {OrderedSet} from \"./utils/OrderedSet\";\nimport {Argument} from \"./shell/Parser\";\nimport {Aliases} from \"./shell/Aliases\";\n\nexport type ColorCode = number | number[];\n\nexport interface Attributes {\n    readonly inverse: boolean;\n    readonly color: ColorCode;\n    readonly backgroundColor: ColorCode;\n    readonly brightness: Brightness;\n    readonly weight: Weight;\n    readonly underline: boolean;\n    readonly crossedOut: boolean;\n    readonly blinking: boolean;\n    readonly cursor: boolean;\n}\n\nexport interface PreliminaryAutocompletionContext {\n    readonly environment: Environment;\n    readonly historicalPresentDirectoriesStack: OrderedSet<string>;\n    readonly aliases: Aliases;\n}\n\nexport interface AutocompletionContext extends PreliminaryAutocompletionContext {\n    readonly argument: Argument;\n}\n\nexport type AutocompletionProvider = (context: AutocompletionContext) => Promise<Suggestion[]>;\n\nexport interface FileInfo {\n    name: string;\n    stat: Stats;\n}\n\nexport interface Prettyfier {\n    isApplicable: (job: Job) => boolean;\n    prettify: (job: Job) => ReactElement<any>;\n}\n\nexport interface EnvironmentObserverPlugin {\n    presentWorkingDirectoryWillChange: (session: Session, directory: string) => void;\n    presentWorkingDirectoryDidChange: (session: Session, directory: string) => void;\n}\n\nexport interface TerminalLikeDevice {\n    output: Output;\n    write: (input: string | KeyboardEvent) => void;\n}\n\nexport type UserEvent = KeyboardEvent | ClipboardEvent;\n\nexport type MouseEvent = DragEvent;\n"
  },
  {
    "path": "src/Output.ts",
    "content": "import * as events from \"events\";\nimport {defaultAttributes, createChar, Char} from \"./Char\";\nimport * as i from \"./Interfaces\";\nimport * as e from \"./Enums\";\nimport {List} from \"immutable\";\nimport {Color, Weight, Brightness, KeyCode, LogLevel, BufferType, ScreenMode} from \"./Enums\";\nimport {Attributes, TerminalLikeDevice, ColorCode} from \"./Interfaces\";\nimport {print, error, info, csi, times} from \"./utils/Common\";\nimport * as _ from \"lodash\";\n\nconst ansiParserConstructor: typeof AnsiParser = require(\"node-ansiparser\");\n\ninterface HandlerResult {\n    status: string;\n    description: string;\n    longDescription?: string;\n    url: string;\n}\n\ninterface SavedState {\n    cursorRowIndex: number;\n    cursorColumnIndex: number;\n    attributes: i.Attributes;\n    designatedCharacterSets: DesignatedCharacterSets;\n    selectedCharacterSet: SelectedCharacterSet;\n}\n\n/**\n * @link http://vt100.net/docs/vt220-rm/chapter4.html\n */\nenum CharacterSets {\n    ASCIIGraphics,\n    SupplementalGraphics,\n}\n\ninterface DesignatedCharacterSets {\n    G0: CharacterSets;\n    G1: CharacterSets;\n    G2: CharacterSets;\n    G3: CharacterSets;\n}\n\ntype SelectedCharacterSet = keyof DesignatedCharacterSets;\n\nfunction or1(value: number | undefined) {\n    if (value === undefined) {\n        return 1;\n    } else {\n        return value;\n    }\n}\n\n\nfunction logPosition(buffer: Buffer) {\n    const position = {rowIndex: buffer.cursorRowIndex, columnIndex: buffer.cursorColumnIndex};\n    const char = buffer.at(position);\n    const value = char ? char.value : \"NULL\";\n    info(`%crow: ${position.rowIndex + 1}\\tcolumn: ${buffer.cursorColumnIndex + 1}\\t value: ${value}, rows: ${buffer.size}`, \"color: grey\");\n}\n\n/**\n * Copied from xterm.js\n * @link https://github.com/sourcelair/xterm.js/blob/master/src/Charsets.ts\n */\nconst graphicCharset: Dictionary<string> = {\n    \"`\": \"\\u25c6\", // \"◆\"\n    \"a\": \"\\u2592\", // \"▒\"\n    \"b\": \"\\u0009\", // \"\\t\"\n    \"c\": \"\\u000c\", // \"\\f\"\n    \"d\": \"\\u000d\", // \"\\r\"\n    \"e\": \"\\u000a\", // \"\\n\"\n    \"f\": \"\\u00b0\", // \"°\"\n    \"g\": \"\\u00b1\", // \"±\"\n    \"h\": \"\\u2424\", // \"\\u2424\" (NL)\n    \"i\": \"\\u000b\", // \"\\v\"\n    \"j\": \"\\u2518\", // \"┘\"\n    \"k\": \"\\u2510\", // \"┐\"\n    \"l\": \"\\u250c\", // \"┌\"\n    \"m\": \"\\u2514\", // \"└\"\n    \"n\": \"\\u253c\", // \"┼\"\n    \"o\": \"\\u23ba\", // \"⎺\"\n    \"p\": \"\\u23bb\", // \"⎻\"\n    \"q\": \"\\u2500\", // \"─\"\n    \"r\": \"\\u23bc\", // \"⎼\"\n    \"s\": \"\\u23bd\", // \"⎽\"\n    \"t\": \"\\u251c\", // \"├\"\n    \"u\": \"\\u2524\", // \"┤\"\n    \"v\": \"\\u2534\", // \"┴\"\n    \"w\": \"\\u252c\", // \"┬\"\n    \"x\": \"\\u2502\", // \"│\"\n    \"y\": \"\\u2264\", // \"≤\"\n    \"z\": \"\\u2265\", // \"≥\"\n    \"{\": \"\\u03c0\", // \"π\"\n    \"|\": \"\\u2260\", // \"≠\"\n    \"}\": \"\\u00a3\", // \"£\"\n    \"~\": \"\\u00b7\", // \"·\"\n};\n\nconst SGR: { [indexer: string]: (attributes: Attributes) => Attributes } = {\n    0: (_attributes: Attributes) => defaultAttributes,\n    1: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright}),\n    2: (attributes: Attributes) => ({...attributes, weight: Weight.Faint}),\n    4: (attributes: Attributes) => ({...attributes, underline: true}),\n    7: (attributes: Attributes) => ({...attributes, inverse: true}),\n    22: (attributes: Attributes) => ({...attributes, weight: Weight.Normal}),\n    24: (attributes: Attributes) => ({...attributes, underline: false}),\n    27: (attributes: Attributes) => ({...attributes, inverse: false}),\n    30: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Black}),\n    31: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Red}),\n    32: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Green}),\n    33: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Yellow}),\n    34: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Blue}),\n    35: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Magenta}),\n    36: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.Cyan}),\n    37: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.White}),\n    39: (attributes: Attributes) => ({...attributes, color: <ColorCode>Color.White}),\n    40: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Black}),\n    41: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Red}),\n    42: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Green}),\n    43: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Yellow}),\n    44: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Blue}),\n    45: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Magenta}),\n    46: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Cyan}),\n    47: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.White}),\n    49: (attributes: Attributes) => ({...attributes, backgroundColor: <ColorCode>Color.Black}),\n    90: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Black}),\n    91: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Red}),\n    92: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Green}),\n    93: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Yellow}),\n    94: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Blue}),\n    95: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Magenta}),\n    96: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.Cyan}),\n    97: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, color: <ColorCode>Color.White}),\n    100: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Black}),\n    101: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Red}),\n    102: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Green}),\n    103: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Yellow}),\n    104: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Blue}),\n    105: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Magenta}),\n    106: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.Cyan}),\n    107: (attributes: Attributes) => ({...attributes, brightness: Brightness.Bright, backgroundColor: <ColorCode>Color.White}),\n};\n\nconst CSI = {\n    erase: {\n        toEnd: 0,\n        toBeginning: 1,\n        entire: 2,\n        entireSsh: 3,\n    },\n};\n\nconst colorFormatCodes = {\n    format8bit: 5,\n    formatTrueColor: 2,\n};\n\nexport class Output extends events.EventEmitter {\n    public activeBufferType = e.BufferType.Normal;\n    public isCursorKeysModeSet = false;\n    public screenMode = ScreenMode.Dark;\n    private normalBuffer: Buffer;\n    private alternateBuffer: Buffer;\n    private parser: AnsiParser;\n\n    constructor(private terminalDevice: TerminalLikeDevice, public dimensions: Dimensions) {\n        super();\n\n        this.normalBuffer = new Buffer(this, 200);\n        this.alternateBuffer = new Buffer(this, 0);\n\n        this.parser = new ansiParserConstructor({\n            inst_p: (text: string) => {\n                info(\"text\", text, text.split(\"\").map(letter => letter.charCodeAt(0)));\n\n                for (let i = 0; i !== text.length; ++i) {\n                    this.activeBuffer.writeOne(text.charAt(i));\n                }\n\n                logPosition(this.activeBuffer);\n            },\n            inst_o: function (s: any) {\n                error(\"osc\", s);\n            },\n            inst_x: (flag: string) => {\n                this.activeBuffer.writeOne(flag);\n\n                print((KeyCode[flag.charCodeAt(0)] ? LogLevel.Log : LogLevel.Error), [\"char\", flag.split(\"\").map((_, index) => flag.charCodeAt(index))]);\n                logPosition(this.activeBuffer);\n            },\n            /**\n             * CSI handler.\n             */\n            inst_c: (collected: any, params: Array<number>, flag: string) => {\n                let handlerResult: HandlerResult;\n                if (collected === \"?\") {\n                    if (params.length !== 1) {\n                        return error(`CSI private mode has ${params.length} parameters: ${params}`);\n                    }\n                    if (flag !== \"h\" && flag !== \"l\") {\n                        return error(`CSI private mode has an incorrect flag: ${flag}`);\n                    }\n                    const mode = params[0];\n                    handlerResult = this.decPrivateModeHandler(mode, flag);\n\n                    if (handlerResult.status === \"handled\") {\n                        info(`%cCSI ? ${mode} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                    } else {\n                        error(`%cCSI ? ${mode} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                    }\n                } else {\n                    handlerResult = this.csiHandler(collected, params, flag);\n\n                    if (handlerResult.status === \"handled\") {\n                        info(`%cCSI ${params} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                    } else {\n                        error(`%cCSI ${params} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                    }\n                }\n\n                logPosition(this.activeBuffer);\n            },\n            /**\n             * ESC handler.\n             */\n            inst_e: (collected: any, flag: string) => {\n                const handlerResult = this.escapeHandler(collected, flag);\n\n                if (handlerResult.status === \"handled\") {\n                    info(`%cESC ${collected} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                } else {\n                    error(`%cESC ${collected} ${flag}`, \"color: blue\", handlerResult.description, handlerResult.url);\n                }\n\n                logPosition(this.activeBuffer);\n            },\n        });\n    }\n\n    write(ansiString: string) {\n        this.parser.parse(ansiString);\n        this.emit(\"data\");\n    }\n\n    toLines() {\n        return this.activeBuffer.toLines();\n    }\n\n    toString(): string {\n        return this.toLines().join(\"\\n\");\n    }\n\n    isEmpty(): boolean {\n        return this.activeBuffer.size === 0;\n    }\n\n    get activeBuffer() {\n        if (this.activeBufferType === e.BufferType.Normal) {\n            return this.normalBuffer;\n        } else {\n            return this.alternateBuffer;\n        }\n    }\n\n    private escapeHandler(collected: any, flag: string) {\n        let short = \"\";\n        let long = \"\";\n        let url = \"\";\n        let status = \"handled\";\n\n        if (collected) {\n            if (collected === \"#\" && flag === \"8\") {\n                short = \"DEC Screen Alignment Test (DECALN).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/DECALN\";\n\n                const dimensions = this.activeBuffer.dimensions;\n\n                for (let i = 0; i !== dimensions.rows; ++i) {\n                    this.activeBuffer.moveCursorAbsolute({rowIndex: i, columnIndex: 0});\n                    this.write(Array(dimensions.columns).join(\"E\"));\n                }\n\n                this.activeBuffer.moveCursorAbsolute({rowIndex: 0, columnIndex: 0});\n            } else if (collected === \"(\" && flag === \"0\") {\n                short = \"Designate Graphic Charset to G0\";\n                this.activeBuffer.designatedCharacterSets.G0 = CharacterSets.SupplementalGraphics;\n            } else if (collected === \"(\" && flag === \"B\") {\n                short = \"Designate ASCII Charset to G0\";\n                this.activeBuffer.designatedCharacterSets.G0 = CharacterSets.ASCIIGraphics;\n            } else if (collected === \")\" && flag === \"0\") {\n                short = \"Designate Graphic Charset to G1\";\n                this.activeBuffer.designatedCharacterSets.G1 = CharacterSets.SupplementalGraphics;\n            } else if (collected === \")\" && flag === \"B\") {\n                short = \"Designate ASCII Charset to G1\";\n                this.activeBuffer.designatedCharacterSets.G1 = CharacterSets.ASCIIGraphics;\n            } else {\n                status = \"unhandled\";\n            }\n        } else {\n            switch (flag) {\n                case \"A\":\n                    short = \"Cursor up.\";\n\n                    this.activeBuffer.moveCursorRelative({vertical: -1});\n                    break;\n                case \"B\":\n                    short = \"Cursor down.\";\n\n                    this.activeBuffer.moveCursorRelative({vertical: 1});\n                    break;\n                case \"C\":\n                    short = \"Cursor right.\";\n\n                    this.activeBuffer.moveCursorRelative({horizontal: 1});\n                    break;\n                case \"D\":\n                    short = \"Index (IND).\";\n                    url = \"http://www.vt100.net/docs/vt510-rm/IND\";\n\n                    this.activeBuffer.moveCursorRelative({vertical: 1});\n                    break;\n                case \"H\":\n                    short = \"Horizontal Tab Set (HTS).\";\n                    url = \"http://www.vt100.net/docs/vt510-rm/HTS\";\n\n                    this.activeBuffer.setTabStop();\n                    break;\n                case \"M\":\n                    short = \"Reverse Index (RI).\";\n                    /* tslint:disable:max-line-length */\n                    long = \"Move the active position to the same horizontal position on the preceding line if the active position is at the top margin, a scroll down is performed.\";\n\n                    if (this.activeBuffer.cursorRowIndex === this.activeBuffer.marginTop) {\n                        this.activeBuffer.scrollDown(1);\n                    } else {\n                        this.activeBuffer.moveCursorRelative({vertical: -1});\n                    }\n                    break;\n                case \"E\":\n                    short = \"Next Line (NEL).\";\n                    /* tslint:disable:max-line-length */\n                    long = \"This sequence causes the active position to move to the first position on the next line downward. If the active position is at the bottom margin, a scroll up is performed.\";\n\n                    this.activeBuffer.moveCursorRelative({vertical: 1});\n                    this.activeBuffer.moveCursorAbsolute({columnIndex: 0});\n                    break;\n                case \"7\":\n                    long = \"Save current state (cursor coordinates, attributes, character sets pointed at by G0, G1).\";\n                    this.activeBuffer.saveCurrentState();\n                    break;\n                case \"8\":\n                    long = \"Restore state most recently saved by ESC 7.\";\n                    this.activeBuffer.restoreCurrentState();\n                    break;\n                default:\n                    status = \"unhandled\";\n            }\n        }\n\n        return {\n            status: status,\n            description: short,\n            longDescription: long,\n            url: url,\n        };\n    }\n\n    private decPrivateModeHandler(ps: number, flag: \"h\" | \"l\"): HandlerResult {\n        let description = \"\";\n        let url = \"\";\n        let status: \"handled\" | \"unhandled\" = \"handled\";\n        let shouldSet = flag === \"h\";\n\n        // noinspection FallThroughInSwitchStatementJS\n        switch (ps) {\n            case 1:\n                description = \"Cursor Keys Mode.\";\n                url = \"http://www.vt100.net/docs/vt510-rm/DECCKM\";\n\n                this.isCursorKeysModeSet = shouldSet;\n                break;\n            case 3:\n                url = \"http://www.vt100.net/docs/vt510-rm/DECCOLM\";\n\n                if (shouldSet) {\n                    description = \"132 Column Mode (DECCOLM).\";\n\n                    this.dimensions = {columns: 132, rows: this.activeBuffer.dimensions.rows};\n                } else {\n                    description = \"80 Column Mode (DECCOLM).\";\n\n                    this.dimensions = {columns: 80, rows: this.activeBuffer.dimensions.rows};\n                }\n                this.activeBuffer.clear();\n                // TODO\n                // If you change the DECCOLM setting, the terminal:\n                //      Sets the left, right, top and bottom scrolling margins to their default positions.\n                //      Erases all data in page memory.\n                // DECCOLM resets vertical split screen mode (DECLRMM) to unavailabl\n                // DECCOLM clears data from the status line if the status line is set to host-writabl\n                break;\n            case 5:\n                description = \"Reverse Video (DECSCNM).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/DECSCNM\";\n\n                this.screenMode = (shouldSet ? ScreenMode.Light : ScreenMode.Dark);\n                break;\n            case 6:\n                description = \"Origin Mode (DECOM).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/DECOM\";\n\n                this.activeBuffer.isOriginModeSet = shouldSet;\n                break;\n            case 7:\n                description = \"Wraparound Mode (DECAWM).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/DECAWM\";\n\n                this.activeBuffer.isAutowrapModeSet = shouldSet;\n                break;\n            case 12:\n                if (shouldSet) {\n                    description = \"Start Blinking Cursor (att610).\";\n\n                    this.activeBuffer.blinkCursor(true);\n                } else {\n                    description = \"Stop Blinking Cursor (att610).\";\n\n                    this.activeBuffer.blinkCursor(false);\n                }\n\n                break;\n            case 25:\n                url = \"http://www.vt100.net/docs/vt510-rm/DECTCEM\";\n\n                if (shouldSet) {\n                    description = \"Show Cursor (DECTCEM).\";\n\n                    this.activeBuffer.showCursor(true);\n                } else {\n                    description = \"Hide Cursor (DECTCEM).\";\n\n                    this.activeBuffer.showCursor(false);\n                }\n                break;\n            case 1049:\n                if (shouldSet) {\n                    /* tslint:disable:max-line-length */\n                    description = \"Save cursor as in DECSC and use Alternate Screen Buffer, clearing it first.  (This may be disabled by the titeInhibit resource).  This combines the effects of the 1047  and 1048  modes.  Use this with terminfo-based applications rather than the 47  mod\";\n\n                    this.activeBufferType = BufferType.Alternate;\n                } else {\n                    // TODO: Add Implementation\n                    status = \"unhandled\";\n                }\n                break;\n            case 2004:\n                if (shouldSet) {\n                    description = \"Set bracketed paste mod\";\n                } else {\n                    // TODO: Add Implementation\n                    status = \"unhandled\";\n                }\n                break;\n            default:\n                status = \"unhandled\";\n        }\n\n        return {\n            status: status,\n            description: description,\n            url: url,\n        };\n    }\n\n    private csiHandler(_collected: any, rawParams: number[] | number, flag: string): HandlerResult {\n        let short = \"\";\n        let long = \"\";\n        let url = \"\";\n        let status = \"handled\";\n\n        let params: number[] = Array.isArray(rawParams) ? rawParams : [];\n        const param: number = params[0] || 0;\n\n        switch (flag) {\n            case \"A\":\n                short = \"Cursor Up Ps Times (default = 1) (CUU).\";\n\n                this.activeBuffer.moveCursorRelative({vertical: -(param || 1)});\n                break;\n            case \"B\":\n                short = \"Cursor Down Ps Times (default = 1) (CUD).\";\n                this.activeBuffer.moveCursorRelative({vertical: (param || 1)});\n                break;\n            case \"C\":\n                short = \"Cursor Forward Ps Times (default = 1) (CUF).\";\n\n                this.activeBuffer.moveCursorRelative({horizontal: (param || 1)});\n                break;\n            case \"D\":\n                short = \"Cursor Backward Ps Times (default = 1) (CUB).\";\n\n                this.activeBuffer.moveCursorRelative({horizontal: -(param || 1)});\n                break;\n            // CSI Ps E  Cursor Next Line Ps Times (default = 1) (CNL).\n            // CSI Ps F  Cursor Preceding Line Ps Times (default = 1) (CPL).\n            case \"G\":\n                short = \"Cursor Character Absolute [column] (default = [row,1]) (CHA)\";\n                url = \"http://www.vt100.net/docs/vt510-rm/CHA\";\n\n                this.activeBuffer.moveCursorAbsolute({columnIndex: or1(param || 1) - 1});\n                break;\n            case \"H\":\n                short = \"Cursor Position [row;column] (default = [1,1]) (CUP).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/CUP\";\n\n                this.activeBuffer.moveCursorAbsolute({rowIndex: or1(params[0]) - 1, columnIndex: or1(params[1]) - 1});\n                break;\n            case \"J\":\n                url = \"http://www.vt100.net/docs/vt510-rm/ED\";\n                switch (param) {\n                    case CSI.erase.entire:\n                    case CSI.erase.entireSsh:\n                        short = \"Erase Entire Display (ED).\";\n\n                        this.activeBuffer.clear();\n                        break;\n                    case CSI.erase.toEnd:\n                        short = \"Erase Display Below (ED).\";\n\n                        this.activeBuffer.clearToEnd();\n                        break;\n                    case CSI.erase.toBeginning:\n                        short = \"Erase Display Above (ED).\";\n\n                        this.activeBuffer.clearToBeginning();\n                        break;\n                    default:\n                        throw `Unknown CSI erase: \"${param}\".`;\n                }\n                break;\n            case \"K\":\n                url = \"http://www.vt100.net/docs/vt510-rm/DECSEL\";\n                switch (param) {\n                    case CSI.erase.entire:\n                        short = \"Erase the Line (DECSEL).\";\n\n                        this.activeBuffer.clearRow();\n                        break;\n                    case CSI.erase.toEnd:\n                        short = \"Erase Line to Right (DECSEL).\";\n                        this.activeBuffer.clearRowToEnd();\n                        break;\n                    case CSI.erase.toBeginning:\n                        short = \"Erase Line to Left (DECSEL).\";\n                        this.activeBuffer.clearRowToBeginning();\n                        break;\n                    default:\n                        throw `Unknown CSI erase: \"${param}\".`;\n                }\n                break;\n            case \"L\":\n                url = \"http://www.vt100.net/docs/vt510-rm/IL\";\n                short = \"Inserts one or more blank lines, starting at the cursor. (DL)\";\n\n                this.activeBuffer.scrollDown(param || 1);\n                break;\n            case \"M\":\n                url = \"http://www.vt100.net/docs/vt510-rm/DL\";\n                short = \"Deletes one or more lines in the scrolling region, starting with the line that has the cursor. (DL)\";\n\n                this.activeBuffer.scrollUp(param || 1, this.activeBuffer.cursorRowIndex);\n                break;\n            case \"P\":\n                url = \"http://www.vt100.net/docs/vt510-rm/DCH.html\";\n                short = \"Deletes one or more characters from the cursor position to the right.\";\n\n                this.activeBuffer.deleteRight(param);\n                break;\n            case \"X\":\n                short = \"Erase P s Character(s) (default = 1) (ECH)\";\n                url = \"http://www.vt100.net/docs/vt510-rm/ECH\";\n\n                this.activeBuffer.eraseRight(param || 1);\n                break;\n            case \"c\":\n                short = \"Send Device Attributes (Primary DA)\";\n                this.terminalDevice.write(\"\\x1b>1;2;\");\n                break;\n            case \"d\":\n                short = \"Line Position Absolute [row] (default = [1,column]) (VPA).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/VPA\";\n\n                this.activeBuffer.moveCursorAbsolute({rowIndex: or1(param || 1) - 1});\n                break;\n            case \"f\":\n                short = \"Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP).\";\n                url = \"http://www.vt100.net/docs/vt510-rm/HVP\";\n\n                this.activeBuffer.moveCursorAbsolute({rowIndex: or1(params[0]) - 1, columnIndex: or1(params[1]) - 1});\n                break;\n            case \"g\":\n                url = \"http://www.vt100.net/docs/vt510-rm/TBC\";\n\n                switch (param) {\n                    case 0:\n                        short = \"Clear Tab Stop At Current Column (TBC).\";\n\n                        this.activeBuffer.clearTabStop();\n                        break;\n                    case 3:\n                        short = \"Clear All Tab Stops (TBC).\";\n\n                        this.activeBuffer.clearAllTabStops();\n                        break;\n                    default:\n                        error(`Unknown tab clear parameter \"${param}\", ignoring.`);\n                }\n                break;\n            case \"m\":\n                short = `SGR: ${params}`;\n\n                if (params.length === 0) {\n                    short = \"Reset SGR\";\n                    this.activeBuffer.resetAttributes();\n                    break;\n                }\n\n                while (params.length !== 0) {\n                    const sgr = params.shift()!;\n\n                    if (sgr === 38 || sgr === 48) {\n                        const colorFormat = params.shift();\n\n                        if (colorFormat === colorFormatCodes.format8bit) {\n                            const color = params.shift();\n\n                            if (color) {\n                                this.setColor(sgr, color);\n                            } else {\n                                error(\"sgr\", sgr, colorFormat, params);\n                            }\n                        } else if (colorFormat === colorFormatCodes.formatTrueColor) {\n                            this.setColor(sgr, params);\n                            params = [];\n                        } else {\n                            error(\"sgr\", sgr, colorFormat, params);\n                        }\n                    } else {\n                        const attributesUpdater = SGR[sgr];\n\n                        if (attributesUpdater) {\n                            this.activeBuffer.setAttributes(attributesUpdater(this.activeBuffer.attributes));\n                        } else {\n                            error(\"sgr\", sgr, params);\n                        }\n                    }\n                }\n\n                break;\n            case \"n\":\n                if (param === 6) {\n                    url = \"http://www.vt100.net/docs/vt510-rm/CPR\";\n                    short = \"Report Cursor Position (CPR) [row;column] as CSI r ; c R\";\n                    this.terminalDevice.write(csi(`${this.activeBuffer.cursorRowIndex + 1};${this.activeBuffer.cursorColumnIndex + 1}R`));\n                } else {\n                    status = \"unhandled\";\n                }\n\n                break;\n            case \"r\":\n                url = \"http://www.vt100.net/docs/vt510-rm/DECSTBM\";\n                short = \"Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM).\";\n\n                const top = <number>(params[0] ? params[0] - 1 : undefined);\n                const bottom = <number>(params[1] ? params[1] - 1 : undefined);\n\n                this.activeBuffer.margins = {top: top, bottom: bottom};\n                this.activeBuffer.moveCursorAbsolute({rowIndex: 0, columnIndex: 0});\n                break;\n            case \"@\":\n                url = \"http://www.vt100.net/docs/vt510-rm/ICH.html\";\n                short = \"Inserts one or more space (SP) characters starting at the cursor position.\";\n\n                this.activeBuffer.insertSpaceRight(param);\n                break;\n            default:\n                status = \"unhandled\";\n        }\n\n        return {\n            status: status,\n            description: short,\n            longDescription: long,\n            url: url,\n        };\n    }\n\n    private setColor(sgr: number, color: ColorCode): void {\n        if (sgr === 38) {\n            this.activeBuffer.setAttributes({...this.activeBuffer.attributes, color: color});\n        } else {\n            this.activeBuffer.setAttributes({...this.activeBuffer.attributes, backgroundColor: color});\n        }\n    }\n}\n\n\nclass Buffer {\n    public cursorRowIndex = 0;\n    public cursorColumnIndex = 0;\n    public _showCursor = true;\n    public _blinkCursor = true;\n    public designatedCharacterSets: DesignatedCharacterSets = {\n        G0: CharacterSets.ASCIIGraphics,\n        G1: CharacterSets.ASCIIGraphics,\n        G2: CharacterSets.ASCIIGraphics,\n        G3: CharacterSets.ASCIIGraphics,\n    };\n    public selectedCharacterSet: SelectedCharacterSet = \"G0\";\n    public isOriginModeSet = false;\n    public isAutowrapModeSet = true;\n    private scrollback = List<List<Char>>();\n    private page = List<List<Char>>();\n    private _attributes: i.Attributes = {...defaultAttributes, color: e.Color.White, weight: e.Weight.Normal};\n    private _margins: Margins = {top: 0, left: 0};\n    private savedState: SavedState | undefined;\n    private tabStopIndices = _.range(8, 300, 8);\n\n    constructor(private output: Output, private maxScrollbackSize: number) {\n        this.ensureCursorRowExists();\n    }\n\n    map<T>(callback: (row: List<Char>, index: number) => T): T[] {\n        const result: T[] = [];\n        let index = 0;\n\n        this.scrollback.forEach(row => {\n            result.push(callback(row!, index));\n            ++index;\n        });\n        this.page.forEach(row => {\n            result.push(callback(row!, index));\n            ++index;\n        });\n\n        return result;\n    }\n\n    writeOne(char: string): void {\n        const charCode = char.charCodeAt(0);\n\n        /**\n         * Is a special symbol.\n         * TODO: take into account C1 and DELETE.\n         * @link http://www.asciitable.com/index/asciifull.gif\n         */\n        if (charCode < 32) {\n            switch (charCode) {\n                case e.KeyCode.Bell:\n                    break;\n                case e.KeyCode.Backspace:\n                    this.moveCursorRelative({horizontal: -1});\n                    break;\n                case e.KeyCode.Tab:\n                    this.moveCursorAbsolute({columnIndex: this.nextTabStopIndex});\n                    break;\n                case e.KeyCode.NewLine:\n                case e.KeyCode.VerticalTab:\n                    if (this.cursorRowIndex === this._margins.bottom) {\n                        this.scrollUp(1);\n                    } else {\n                        this.moveCursorRelative({vertical: 1});\n                    }\n\n                    break;\n                case e.KeyCode.CarriageReturn:\n                    this.moveCursorAbsolute({columnIndex: 0});\n                    break;\n                case e.KeyCode.ShiftIn:\n                    this.selectedCharacterSet = \"G0\";\n                    break;\n                case e.KeyCode.ShiftOut:\n                    this.selectedCharacterSet = \"G1\";\n                    break;\n                default:\n                    error(`Couldn't write a special char with code ${charCode}.`);\n            }\n        } else {\n\n            const charFromCharset = this.charFromCharset(char);\n            const charObject = createChar(charFromCharset, this.attributes);\n\n            if (this.cursorColumnIndex === this.dimensions.columns) {\n                if (this.isAutowrapModeSet) {\n                    this.moveCursorAbsolute({columnIndex: 0});\n                    this.moveCursorRelative({vertical: 1});\n                } else {\n                    this.moveCursorRelative({horizontal: -1});\n                }\n            }\n\n            this.set(charObject);\n            this.moveCursorRelative({horizontal: 1});\n        }\n    }\n\n    scrollDown(count: number) {\n        times(count, () => this.page = this.page.delete(this.marginBottom));\n        times(count, () => this.page = this.page.insert(this.cursorRowIndex, this.emptyLine));\n    }\n\n    scrollUp(count: number, deletedLine = this._margins.top) {\n        times(count, () => this.page = this.page.splice((this._margins.bottom || 0) + 1, 0, this.emptyLine).toList());\n        this.page = this.page.splice(deletedLine, count).toList();\n    }\n\n    get attributes(): i.Attributes {\n        return this._attributes;\n    }\n\n    resetAttributes(): void {\n        this._attributes = defaultAttributes;\n    }\n\n    setAttributes(attributes: i.Attributes): void {\n        this._attributes = {...this._attributes, ...attributes};\n    }\n\n    toLines(): string[] {\n        return this.map(row => row.map(char => char!.value).join(\"\"));\n    }\n\n    showCursor(state: boolean): void {\n        this._showCursor = state;\n    }\n\n    blinkCursor(state: boolean): void {\n        this._blinkCursor = state;\n    }\n\n    moveCursorRelative(advancement: Advancement): this {\n        const unboundRowIndex = this.cursorRowIndex + (advancement.vertical || 0);\n        const boundRowIndex = this._margins.bottom ? Math.min(this.marginBottom, unboundRowIndex) : unboundRowIndex;\n\n        // Cursor might be hanging after the last column.\n        const boundColumnIndex = Math.min(this.lastColumnIndex, this.cursorColumnIndex);\n\n\n        this.cursorRowIndex = Math.max(0, boundRowIndex);\n        this.cursorColumnIndex = Math.min(this.dimensions.columns, Math.max(0, boundColumnIndex + (advancement.horizontal || 0)));\n\n        this.ensureCursorRowExists();\n        return this;\n    }\n\n    moveCursorAbsolute(position: Partial<RowColumn>): this {\n        if (typeof position.columnIndex === \"number\") {\n            this.cursorColumnIndex = Math.max(position.columnIndex, 0) + this.homePosition.columnIndex;\n        }\n\n        if (typeof position.rowIndex === \"number\") {\n\n            this.cursorRowIndex = Math.max(position.rowIndex, 0) + this.homePosition.rowIndex;\n        }\n\n        this.ensureCursorRowExists();\n        return this;\n    }\n\n    deleteRight(n: number) {\n        this.page = this.page.update(\n            this.cursorRowIndex,\n            row => row.splice(this.cursorColumnIndex, n).concat(this.spaces(n)).toList(),\n        );\n    }\n\n    insertSpaceRight(n: number) {\n        this.page = this.page.update(\n            this.cursorRowIndex,\n            row => row.splice(this.cursorColumnIndex, 0, this.spaces(n)).toList(),\n        );\n    }\n\n    eraseRight(n: number) {\n        this.page = this.page.update(\n            this.cursorRowIndex,\n            row => row.take(this.cursorColumnIndex)\n                .concat(this.spaces(n), row.skip(this.cursorColumnIndex + n))\n                .toList(),\n        );\n    }\n\n    clearRow() {\n        this.page = this.page.set(this.cursorRowIndex, this.emptyLine);\n    }\n\n    clearRowToEnd() {\n        const oldRow = this.page.get(this.cursorRowIndex);\n        const charsToDeleteCount = this.dimensions.columns - this.cursorColumnIndex;\n        const newHead = oldRow.splice(this.cursorColumnIndex, charsToDeleteCount);\n        const newTail = this.spaces(charsToDeleteCount);\n        const newRow = newHead.concat(newTail).toList();\n\n        this.page = this.page.set(this.cursorRowIndex, newRow);\n    }\n\n    clearRowToBeginning() {\n        const count = this.cursorColumnIndex + 1;\n        this.page = this.page.update(\n            this.cursorRowIndex,\n            row => this.spaces(count).concat(row.skip(count)).toList());\n    }\n\n    clear() {\n        this.page = List<List<Char>>();\n        this.moveCursorAbsolute({rowIndex: 0, columnIndex: 0});\n    }\n\n    clearToBeginning() {\n        this.clearRowToBeginning();\n        const replacement = Array(this.cursorRowIndex).fill(this.emptyLine);\n\n        this.page = this.page.splice(0, this.cursorRowIndex, ...replacement).toList();\n    }\n\n    clearToEnd() {\n        this.clearRowToEnd();\n        this.page = this.page.splice(this.cursorRowIndex + 1, this.size - this.cursorRowIndex).toList();\n    }\n\n    get scrollbackSize(): number {\n        return this.scrollback.size;\n    }\n\n    get size(): number {\n        return this.page.size;\n    }\n\n    set margins(margins: Partial<Margins>) {\n        this._margins = {...this._margins, ...margins};\n    }\n\n    get marginTop(): number {\n        return this._margins.top;\n    }\n\n    get marginBottom(): number {\n        if (this._margins.bottom) {\n            return this._margins.bottom;\n        } else {\n            return this.dimensions.rows - 1;\n        }\n    }\n\n    at(position: RowColumn): Char {\n        return this.page.getIn([position.rowIndex, position.columnIndex]);\n    }\n\n    saveCurrentState() {\n        this.savedState = {\n            cursorRowIndex: this.cursorRowIndex,\n            cursorColumnIndex: this.cursorColumnIndex,\n            attributes: {...this.attributes},\n            designatedCharacterSets: {...this.designatedCharacterSets},\n            selectedCharacterSet: this.selectedCharacterSet,\n        };\n    }\n\n    restoreCurrentState() {\n        if (this.savedState) {\n            this.moveCursorAbsolute({rowIndex: this.savedState.cursorRowIndex, columnIndex: this.savedState.cursorColumnIndex});\n            this.setAttributes(this.savedState.attributes);\n            this.selectedCharacterSet = this.savedState.selectedCharacterSet;\n            this.designatedCharacterSets = this.savedState.designatedCharacterSets;\n        } else {\n            console.error(\"No state to restore.\");\n        }\n    }\n\n    setTabStop() {\n        this.tabStopIndices = _.sortBy(_.union(this.tabStopIndices, [this.cursorColumnIndex]));\n    }\n\n    clearTabStop() {\n        this.tabStopIndices = _.without(this.tabStopIndices, this.cursorColumnIndex);\n    }\n\n    clearAllTabStops() {\n        this.tabStopIndices = [];\n    }\n\n    get nextTabStopIndex() {\n        const unboundTabStopIndex = this.tabStopIndices.find(index => index > this.cursorColumnIndex) || this.cursorColumnIndex;\n        return Math.min(unboundTabStopIndex, this.lastColumnIndex);\n    }\n\n    private get homePosition(): RowColumn {\n        if (this.isOriginModeSet) {\n            return {rowIndex: this._margins.top || 0, columnIndex: this._margins.left || 0};\n        } else {\n            return {rowIndex: 0, columnIndex: 0};\n        }\n    }\n\n    private set(char: Char): void {\n        this.ensureCursorRowExists();\n        this.page = this.page.setIn([this.cursorRowIndex, this.cursorColumnIndex], char);\n    }\n\n    private ensureCursorRowExists(): void {\n        for (let index = this.cursorRowIndex; index >= 0; --index) {\n            if (!this.page.get(index)) {\n                this.page = this.page.set(index, this.spaces(this.dimensions.columns, defaultAttributes));\n            } else {\n                break;\n            }\n        }\n\n        if (this.size > this.dimensions.rows) {\n            const newStorage = this.page.takeLast(this.dimensions.rows).toList();\n            const rowsToMoveToScrollback = this.page.skipLast(this.dimensions.rows).toList();\n            this.scrollback = this.scrollback.concat(rowsToMoveToScrollback).takeLast(this.maxScrollbackSize).toList();\n\n            this.page = newStorage;\n            this.cursorRowIndex = this.size - 1;\n        }\n    }\n\n    private charFromCharset(char: string) {\n        if (this.designatedCharacterSets[this.selectedCharacterSet] === CharacterSets.ASCIIGraphics) {\n            return char;\n        } else {\n            return graphicCharset[char] || char;\n        }\n    }\n\n    private get lastColumnIndex() {\n        return this.dimensions.columns - 1;\n    }\n\n    private get emptyLine() {\n        return this.spaces(this.dimensions.columns);\n    }\n\n    private spaces(n: number, attributes = this.attributes) {\n        return List.of(...Array(n).fill(createChar(\" \", attributes)));\n    }\n\n    get dimensions() {\n        return this.output.dimensions;\n    }\n}\n"
  },
  {
    "path": "src/PTY.ts",
    "content": "import * as ChildProcess from \"child_process\";\nimport * as OS from \"os\";\nimport * as _ from \"lodash\";\nimport * as pty from \"node-pty\";\nimport {loginShell} from \"./utils/Shell\";\nimport {homeDirectory, info} from \"./utils/Common\";\n\ninterface ITerminal {\n    write(data: string): void;\n    resize(cols: number, rows: number): void;\n    kill(signal?: string): void;\n    on(type: string, listener: (...args: any[]) => any): void;\n}\n\nexport class PTY {\n    private terminal: ITerminal;\n\n    // TODO: write proper signatures.\n    // TODO: use generators.\n    // TODO: terminate. https://github.com/atom/atom/blob/v1.0.15/src/task.coffee#L151\n    constructor(words: EscapedShellWord[], env: ProcessEnvironment, dimensions: Dimensions, dataHandler: (d: string) => void, exitHandler: (c: number) => void) {\n        const shellArguments = [...loginShell.noConfigSwitches, ...loginShell.interactiveCommandSwitches, words.join(\" \")];\n\n        info(`PTY: ${loginShell.executableName} ${JSON.stringify(shellArguments)}`);\n        info(`Dimensions: ${JSON.stringify(dimensions)}}`);\n\n        this.terminal = <any> pty.fork(loginShell.executableName, shellArguments, {\n            cols: dimensions.columns,\n            rows: dimensions.rows,\n            cwd: env.PWD,\n            env: env,\n        });\n\n        this.terminal.on(\"data\", (data: string) => dataHandler(data));\n        this.terminal.on(\"exit\", (code: number) => exitHandler(code));\n    }\n\n    write(data: string): void {\n        this.terminal.write(data);\n    }\n\n    resize(dimensions: Dimensions) {\n        this.terminal.resize(dimensions.columns, dimensions.rows);\n    }\n\n    kill(signal: string): void {\n        /**\n         *  The if branch is necessary because pty.js doesn't handle SIGINT correctly.\n         *  You can test whether it works by executing\n         *     ruby -e \"loop { puts 'yes'; sleep 1 }\"\n         *  and trying to kill it with SIGINT.\n         *\n         *  {@link https://github.com/chjj/pty.js/issues/58}\n         */\n        if (signal === \"SIGINT\") {\n            this.terminal.kill(\"SIGTERM\");\n        } else {\n            this.terminal.kill(signal);\n        }\n    }\n}\n\nexport function executeCommand(\n    command: string,\n    args: string[] = [],\n    directory: string,\n    execOptions?: any,\n): Promise<string> {\n    return new Promise((resolve, reject) => {\n        const options = {\n            ...execOptions,\n            env: _.extend({PWD: directory, SHLVL: 1}, process.env),\n            cwd: directory,\n            shell: loginShell.commandExecutorPath,\n        };\n\n        ChildProcess.exec(`${command} ${args.join(\" \")}`, options, (error, output) => {\n            if (error) {\n                reject(error);\n            } else {\n                resolve(output.toString());\n            }\n        });\n    });\n}\n\nexport async function linedOutputOf(command: string, args: string[], directory: string): Promise<string[]> {\n    let output = await executeCommand(command, args, directory);\n    return output.split(\"\\\\\" + OS.EOL).join(\" \").split(OS.EOL).filter(path => path.length > 0);\n}\n\nexport async function executeCommandWithShellConfig(command: string): Promise<string[]> {\n    const sourceCommands = (await loginShell.existingConfigFiles()).map(fileName => `source ${fileName} &> /dev/null`);\n\n    return await linedOutputOf(loginShell.executableName, [...loginShell.executeCommandSwitches, loginShell.combineCommands([...sourceCommands, command])], homeDirectory);\n}\n"
  },
  {
    "path": "src/PluginManager.ts",
    "content": "import {Prettyfier, EnvironmentObserverPlugin, AutocompletionProvider} from \"./Interfaces\";\nimport * as Path from \"path\";\nimport {io} from \"./utils/Common\";\nimport { defaultAutocompletionProvider } from \"./plugins/completion_utils/Common\";\n\n// FIXME: Technical debt: register all the plugin types via single method.\nexport class PluginManager {\n    private static _prettyfiers: Prettyfier[] = [];\n    private static _environmentObservers: EnvironmentObserverPlugin[] = [];\n    private static _autocompletionProviders: Dictionary<AutocompletionProvider> = {};\n\n    static registerPrettyfier(prettyfier: Prettyfier): void {\n        this._prettyfiers.push(prettyfier);\n    }\n\n    static get prettyfiers(): Prettyfier[] {\n        return this._prettyfiers;\n    }\n\n    static registerEnvironmentObserver(plugin: EnvironmentObserverPlugin): void {\n        this._environmentObservers.push(plugin);\n    }\n\n    static get environmentObservers(): EnvironmentObserverPlugin[] {\n        return this._environmentObservers;\n    }\n\n    static registerAutocompletionProvider(commandName: string, provider: AutocompletionProvider): void {\n        this._autocompletionProviders[commandName] = provider;\n    }\n\n    static autocompletionProviderFor(commandName: string): AutocompletionProvider {\n        return this._autocompletionProviders[commandName] || defaultAutocompletionProvider;\n    }\n}\n\n\nexport async function loadAllPlugins(): Promise<void> {\n    const pluginsDirectory = Path.join(__dirname, \"plugins\");\n    const filePaths = await io.recursiveFilesIn(pluginsDirectory);\n\n    filePaths.map(require).map((module: any) => module.default);\n}\n"
  },
  {
    "path": "src/main/Main.ts",
    "content": "import {app, ipcMain, nativeImage, BrowserWindow, screen} from \"electron\";\nimport {readFileSync} from \"fs\";\nimport {windowBoundsFilePath} from \"../utils/Common\";\n\napp.on(\"ready\", () => {\n    const bounds = windowBounds();\n\n    let options: Electron.BrowserWindowConstructorOptions = {\n        webPreferences: {\n            experimentalFeatures: true,\n            experimentalCanvasFeatures: true,\n        },\n        titleBarStyle: \"hidden\",\n        resizable: true,\n        minWidth: 500,\n        minHeight: 300,\n        width: bounds.width,\n        height: bounds.height,\n        x: bounds.x,\n        y: bounds.y,\n        show: false,\n    };\n    const browserWindow = new BrowserWindow(options);\n\n    if (app.dock) {\n        app.dock.setIcon(nativeImage.createFromPath(\"build/icon.png\"));\n    } else {\n        browserWindow.setIcon(nativeImage.createFromPath(\"build/icon.png\"));\n    }\n\n    browserWindow.loadURL(\"file://\" + __dirname + \"/../views/index.html\");\n\n    browserWindow.webContents.on(\"did-finish-load\", () => {\n        browserWindow.show();\n        browserWindow.focus();\n    });\n\n    app.on(\"open-file\", (_event, file) => browserWindow.webContents.send(\"change-working-directory\", file));\n});\n\napp.on(\"window-all-closed\", () => app.quit());\n\nipcMain.on(\"quit\", app.quit);\n\nfunction windowBounds(): Electron.Rectangle {\n    try {\n        return JSON.parse(readFileSync(windowBoundsFilePath).toString());\n    } catch (error) {\n        const workAreaSize = screen.getPrimaryDisplay().workAreaSize;\n\n        return {\n            width: workAreaSize.width,\n            height: workAreaSize.height,\n            x: 0,\n            y: 0,\n        };\n    }\n}\n"
  },
  {
    "path": "src/monaco/Loader.ts",
    "content": "export function requireMonaco(callback: () => void) {\n    const g = (global as any);\n    const nodeRequire = g.require;\n\n    const loaderScript = document.createElement(\"script\");\n    loaderScript.type = \"text/javascript\";\n    loaderScript.src = \"../../../node_modules/monaco-editor/min/vs/loader.js\";\n\n    loaderScript.addEventListener(\"load\", () => {\n        const amdRequire = g.require;\n        g.require = nodeRequire;\n        // require node modules before loader.js comes in\n        const path = require(\"path\");\n\n        function uriFromPath(_path: string) {\n            let pathName = path.resolve(_path).replace(/\\\\/g, \"/\");\n            if (pathName.length > 0 && pathName.charAt(0) !== \"/\") {\n                pathName = \"/\" + pathName;\n            }\n            return encodeURI(\"file://\" + pathName);\n        }\n\n        amdRequire.config({\n            baseUrl: uriFromPath(path.join(__dirname, \"../../../node_modules/monaco-editor/dev\")),\n        });\n        // workaround monaco-css not understanding the environment\n        (self as any).module = undefined;\n        // workaround monaco-typescript not understanding the environment\n        (self as any).process.browser = true;\n        amdRequire([\"vs/editor/editor.main\"], callback);\n\n        // window.require([\"vs/editor/editor.main\"], () => require(\"./Main\"));\n    });\n    document.body.appendChild(loaderScript);\n}\n"
  },
  {
    "path": "src/monaco/PromptTheme.ts",
    "content": "import {backgroundColor, colors, textColor} from \"../views/css/colors\";\n\nmonaco.editor.defineTheme(\"upterm-prompt-theme\", {\n    base: \"vs-dark\",\n    inherit: true,\n    rules: [\n        {token: \"string\", foreground: colors.green.slice(1)},\n        {token: \"string.invalid\", foreground: colors.red.slice(1)},\n        {token: \"variable-name\", foreground: colors.yellow.slice(1)},\n        {token: \"variable-value\", foreground: textColor.slice(1)},\n        {token: \"command-name\", foreground: colors.blue.slice(1), fontStyle: \"bold\"},\n        {token: \"argument\", foreground: textColor.slice(1)},\n        {token: \"redirect-path\", foreground: colors.yellow.slice(1)},\n        {token: \"pipe\", foreground: colors.yellow.slice(1)},\n        {token: \"semicolon\", foreground: colors.yellow.slice(1)},\n        {token: \"and\", foreground: colors.yellow.slice(1)},\n        {token: \"or\", foreground: colors.yellow.slice(1)},\n        {token: \"appending-output-redirection-symbol\", foreground: colors.yellow.slice(1)},\n        {token: \"input-redirection-symbol\", foreground: colors.yellow.slice(1)},\n        {token: \"output-redirection-symbol\", foreground: colors.yellow.slice(1)},\n    ],\n    colors: {\n        \"editor.foreground\": textColor,\n        \"editor.background\": backgroundColor,\n        \"editor.lineHighlightBackground\": backgroundColor,\n        \"editorSuggestWidget.background\": backgroundColor,\n        \"editorSuggestWidget.highlightForeground\": colors.blue,\n    },\n});\n"
  },
  {
    "path": "src/monaco/ShellHistoryLanguage.ts",
    "content": "import {services} from \"../services/index\";\nimport * as _ from \"lodash\";\n\nmonaco.languages.setMonarchTokensProvider(\"shell-history\", {\n    tokenizer: {\n        root: [\n            {\n                regex: /.+/,\n                action: {token: \"history-item\"},\n            },\n        ],\n    },\n    defaultToken: \"invalid\",\n    tokenPostfix: \".shell-history\",\n});\n\nmonaco.languages.register({\n    id: \"shell-history\",\n});\n\nmonaco.languages.registerCompletionItemProvider(\"shell-history\", {\n    triggerCharacters: [\" \", \"/\"],\n    provideCompletionItems: () => {\n        return {\n            isIncomplete: false,\n            items: _.uniqBy(services.history.all, record => record.command).map(record => ({\n                label: record.command,\n                kind: monaco.languages.CompletionItemKind.Text,\n            })),\n        };\n    },\n});\n\nmonaco.languages.setLanguageConfiguration(\"shell-history\", {\n    wordPattern: /.*/g,\n});\n"
  },
  {
    "path": "src/monaco/ShellLanguage.ts",
    "content": "import {SessionID} from \"../shell/Session\";\nimport {getSuggestions} from \"../Autocompletion\";\nimport {services} from \"../services/index\";\nimport {scan} from \"../shell/Scanner\";\nimport {CompleteCommand} from \"../shell/Parser\";\nimport {io} from \"../utils/Common\";\n\nmonaco.languages.setMonarchTokensProvider(\"shell\", {\n    variableName: /[a-zA-Z][a-zA-Z0-9_]*/,\n    word: /[a-zA-Z0-9\\u0080-\\uFFFF+~!@#%^*_,.:/?\\\\-]+/,\n    escapes: /\\\\(?:[btnfr\\\\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,\n    defaultToken: \"invalid\",\n    tokenizer: {\n        root: [\n            {\n                regex: /\\s+/,\n                action: {token: \"spaces\"},\n            },\n            {\n                regex: /@variableName=/,\n                action: {token: \"variable-name\", next: \"@variableValue\"},\n            },\n            {\n                regex: /@word/,\n                action: {token: \"command-name\", next: \"@arguments\"},\n            },\n        ],\n        arguments: [\n            [/\\s+/, \"spaces\"],\n            [/\\$@variableName/, \"variable-name\"],\n            [/@word/, \"argument\"],\n            [/[=[\\]]/, \"argument\"],\n            {\n                regex: /\\|\\|/,\n                action: {token: \"or\", next: \"@pop\"},\n            },\n            {\n                regex: /\\|/,\n                action: {token: \"pipe\", next: \"@pop\"},\n            },\n            {\n                regex: /;/,\n                action: {token: \"semicolon\", next: \"@pop\"},\n            },\n            {\n                regex: /&&/,\n                action: {token: \"and\", next: \"@pop\"},\n            },\n            {\n                regex: />>/,\n                action: {token: \"appending-output-redirection-symbol\", next: \"@redirect\"},\n            },\n            {\n                regex: /</,\n                action: {token: \"input-redirection-symbol\", next: \"@redirect\"},\n            },\n            {\n                regex: />/,\n                action: {token: \"output-redirection-symbol\", next: \"@redirect\"},\n            },\n            { include: \"@allowStringLiterals\" },\n        ],\n        redirect: [\n            {\n                regex: /\\s+/,\n                action: {token: \"spaces\"},\n            },\n            {\n                regex: /@word/,\n                action: {token: \"redirect-path\", next: \"@pop\"},\n            },\n        ],\n        variableValue: [\n            {\n                regex: /@word/,\n                action: {token: \"variable-value\", next: \"@pop\"},\n            },\n            {include: \"@checkInvalidStringLiteral\"},\n            {\n                regex: /\"/,\n                action: {token: \"string\", switchTo: \"@doubleQuotedString\"},\n            },\n            {\n                regex: /'/,\n                action: {token: \"string\", switchTo: \"@singleQuotedString\"},\n            },\n            {\n                regex: /.*/,\n                action: {token: \"invalid\"},\n            },\n        ],\n        checkInvalidStringLiteral: [\n            // strings: recover on non-terminated strings\n            [/\"([^\"\\\\]|\\\\.)*$/, \"string.invalid\"],  // non-teminated string\n            [/'([^'\\\\]|\\\\.)*$/, \"string.invalid\"],  // non-teminated string\n        ],\n        allowStringLiterals: [\n            {include: \"@checkInvalidStringLiteral\"},\n            [/\"/, \"string\", \"@doubleQuotedString\"],\n            [/'/, \"string\", \"@singleQuotedString\"],\n        ],\n        singleQuotedString: [\n            [/[^\\\\']+/, \"string\"],\n            [/@escapes/, \"string.escape\"],\n            [/\\\\./,      \"string.escape.invalid\"],\n            [/'/, \"string\", \"@pop\"],\n        ],\n        doubleQuotedString: [\n            [/\\$@variableName/, \"variable-name\"],\n            [/\\${@variableName}/, \"variable-name\"],\n            [/[^\\\\\"$]+/, \"string\"],\n            [/@escapes/, \"string.escape\"],\n            [/\\\\./,      \"string.escape.invalid\"],\n            [/\"/, \"string\", \"@pop\"],\n        ],\n\n    },\n    tokenPostfix: \".shell\",\n} as any);\n\nmonaco.languages.register({\n    id: \"shell\",\n});\n\nmonaco.languages.setLanguageConfiguration(\"shell\", {\n    brackets: [\n        [\"'\", \"'\"],\n        ['\"', '\"'],\n        [\"`\", \"`\"],\n        [\"(\", \")\"],\n        [\"[\", \"]\"],\n        [\"{\", \"}\"],\n    ],\n    wordPattern: /(\\d*\\.\\d\\w*\\$)|([^`~!#%^&*()=+\\[{\\]}\\\\|;:'\",<>\\/?\\s]+)/g,\n});\n\nmonaco.editor.onDidCreateModel(model => {\n    if (model.uri.scheme !== \"shell\") {\n        return;\n    }\n\n    model.onDidChangeContent(async () => {\n        const value = model.getValue();\n\n        const sessionID: SessionID = <SessionID>Number.parseInt(model.uri.authority, 10);\n        const session = services.sessions.get(sessionID);\n        const executables = await io.executablesInPaths(session.environment.path);\n        const markers: monaco.editor.IMarkerData[] = [];\n\n        monaco.editor.tokenize(value, \"shell\").forEach((lineTokens, lineIndex) => {\n            lineTokens.forEach((token, tokenIndex) => {\n                if (token.type !== \"command-name.shell\") {\n                    return;\n                }\n\n                const nextToken = lineTokens[tokenIndex + 1];\n\n                // Possibly still writing command name.\n                if (!nextToken) {\n                    return;\n                }\n\n                const tokenRange = {\n                    startLineNumber: lineIndex + 1,\n                    endLineNumber: lineIndex + 1,\n                    startColumn: token.offset + 1,\n                    endColumn: nextToken ? (nextToken.offset + 1) : Infinity,\n                };\n                const commandName = model.getValueInRange(tokenRange);\n\n                if (!executables.includes(commandName) && !session.aliases.has(commandName)) {\n                    markers.push({\n                        severity: monaco.Severity.Error,\n                        message: `Executable ${commandName} doesn't exist in $PATH.`,\n                        ...tokenRange,\n                    });\n                }\n            });\n        });\n\n        monaco.editor.setModelMarkers(model, \"upterm\", markers);\n    });\n});\n\nmonaco.languages.registerCompletionItemProvider(\"shell\", {\n    triggerCharacters: [\" \", \"/\", \"$\", \"-\", \".\"],\n    provideCompletionItems: async function (model, position): Promise<monaco.languages.CompletionList> {\n        model.getValue();\n        const sessionID: SessionID = <SessionID>Number.parseInt(model.uri.authority, 10);\n        const session = services.sessions.get(sessionID);\n        const text = model.getValue();\n\n        const ast = new CompleteCommand(scan(text));\n\n        return getSuggestions({\n            currentText: text,\n            currentCaretPosition: position.column - 1,\n            ast: ast,\n            environment: session.environment,\n            historicalPresentDirectoriesStack: session.historicalPresentDirectoriesStack,\n            aliases: session.aliases,\n        });\n    },\n});\n\n// https://github.com/Microsoft/monaco-editor/issues/346#issuecomment-277215371\nexport function getTokensAtLine(model: any, lineNumber: number) {\n    // Force line's state to be accurate\n    model.getLineTokens(lineNumber, /*inaccurateTokensAcceptable*/false);\n    // Get the tokenization state at the beginning of this line\n    const freshState = model._lines[lineNumber - 1].getState().clone();\n    // Get the human readable tokens on this line\n    return model._tokenizationSupport.tokenize(model.getLineContent(lineNumber), freshState, 0).tokens;\n}\n"
  },
  {
    "path": "src/plugins/AliasSuggestions.ts",
    "content": "import {services} from \"../services/index\";\n\nservices.jobs.onStart.subscribe(job => {\n    const input = job.prompt.value;\n    const alias = job.session.aliases.getNameByValue(input);\n\n    if (alias && alias.length < input.length) {\n        /* tslint:disable:no-unused-expression */\n        new Notification(\"Alias Reminder\", { body: `You have an alias \"${alias}\" for \"${input}\".` });\n    }\n});\n"
  },
  {
    "path": "src/plugins/DotEnvLoader.ts",
    "content": "import {Session} from \"../shell/Session\";\nimport {PluginManager} from \"../PluginManager\";\nimport * as Path from \"path\";\nimport {io} from \"../utils/Common\";\nimport {sourceFile} from \"../shell/BuiltInCommands\";\n\nPluginManager.registerEnvironmentObserver({\n    presentWorkingDirectoryWillChange: () => void 0,\n    presentWorkingDirectoryDidChange: async(session: Session, directory: string) => {\n        if (await io.fileExists(Path.join(directory, \".env\"))) {\n            sourceFile(session, \".env\");\n        }\n    },\n});\n"
  },
  {
    "path": "src/plugins/GitGrep.tsx",
    "content": "import * as React from \"react\";\nimport {PluginManager} from \"../PluginManager\";\nimport {Job} from \"../shell/Job\";\nimport {Link} from \"../utils/Link\";\nimport {join} from \"path\";\nimport {colors} from \"../views/css/colors\";\n\nPluginManager.registerPrettyfier({\n  prettify: (job: Job): React.ReactElement<any> => {\n    return <div style={{\n      padding: \"10px\",\n      lineHeight: \"18px\",\n    }}>{job.output.toLines().map((line, index) => {\n      const match = line.match(/^(.*?):(\\d+):(.*)$/);\n      if (match) {\n        const [, path, lineNum, rest] = match;\n        if (path && lineNum && rest) {\n          const absolutePath = join(job.environment.pwd, path);\n          return <div key={index.toString()}>\n            <Link absolutePath={absolutePath}>{path}</Link>\n            <span style={{color: colors.cyan}}>:</span>\n            {lineNum}\n            <span style={{color: colors.cyan}}>:</span>\n            <span style={{whiteSpace: \"pre\"}}>{rest}</span>\n          </div>;\n        }\n      }\n      return <div key={index.toString()}>{line}</div>;\n    })}</div>;\n  },\n\n  isApplicable: (job: Job): boolean => {\n    try {\n      const promptWords = job.prompt.expandedTokens.map(t => t.escapedValue);\n      return promptWords.length === 3 && promptWords[0] === \"git\" && promptWords[1] === \"grep\";\n    } catch (e) {\n      return false;\n    }\n  },\n});\n"
  },
  {
    "path": "src/plugins/JSON.tsx",
    "content": "import * as React from \"react\";\nimport {Job} from \"../shell/Job\";\nimport {PluginManager} from \"../PluginManager\";\nimport {JSONTree} from \"../utils/JSONTree\";\n\nPluginManager.registerPrettyfier({\n    prettify: (job: Job): React.ReactElement<any> => {\n        return <JSONTree data={JSON.parse(job.output.toString())}/>;\n    },\n\n    isApplicable: (job: Job): boolean => {\n        try {\n            const parseResult = JSON.parse(job.output.toString());\n            return parseResult && typeof parseResult === \"object\";\n        } catch (exception) {\n            return false;\n        }\n    },\n});\n"
  },
  {
    "path": "src/plugins/JobFinishedNotifications.ts",
    "content": "import {services} from \"../services/index\";\nimport {remote} from \"electron\";\nimport {Status} from \"../Enums\";\n\nservices.jobs.onFinish.subscribe(job => {\n    const electronWindow = remote.BrowserWindow.getAllWindows()[0];\n\n    if (remote.app.dock && !electronWindow.isFocused()) {\n        remote.app.dock.bounce(\"informational\");\n        remote.app.dock.setBadge(job.status === Status.Success ? \"1\" : \"✕\");\n\n        const title = job.status === Status.Success ? \"Completed\" : \"Failed\";\n\n        /* tslint:disable:no-unused-expression */\n        new Notification(title, {body: job.prompt.value});\n    }\n});\n\nconst electronWindow = remote.BrowserWindow.getAllWindows()[0];\nelectronWindow.on(\"focus\", () => remote.app.dock && remote.app.dock.setBadge(\"\"));\n"
  },
  {
    "path": "src/plugins/NVM.ts",
    "content": "import {Session} from \"../shell/Session\";\nimport {PluginManager} from \"../PluginManager\";\nimport * as Path from \"path\";\nimport {homeDirectory, io} from \"../utils/Common\";\n\nasync function withNvmPath(directory: string, callback: (path: string) => void) {\n    const rcPath = Path.join(directory, \".nvmrc\");\n\n    if (await io.fileExists(rcPath)) {\n        const version = (await io.readFile(rcPath)).trim();\n        callback(Path.join(homeDirectory, \".nvm\", \"versions\", \"node\", version, \"bin\"));\n    }\n}\n\nPluginManager.registerEnvironmentObserver({\n    presentWorkingDirectoryWillChange: async(session: Session) => {\n        withNvmPath(session.directory, path => session.environment.path.remove(path));\n    },\n    presentWorkingDirectoryDidChange: async(session: Session, directory: string) => {\n        withNvmPath(directory, path => session.environment.path.prepend(path));\n    },\n});\n"
  },
  {
    "path": "src/plugins/PWDOperatingSystemIntegrator.ts",
    "content": "import {Session} from \"../shell/Session\";\nimport {PluginManager} from \"../PluginManager\";\nimport {remote} from \"electron\";\n\nPluginManager.registerEnvironmentObserver({\n    presentWorkingDirectoryWillChange: () => { /* do nothing */ },\n\n    presentWorkingDirectoryDidChange: (_session: Session, directory: string) => {\n        remote.app.addRecentDocument(directory);\n    },\n});\n"
  },
  {
    "path": "src/plugins/RVM.ts",
    "content": "import {Session} from \"../shell/Session\";\nimport {PluginManager} from \"../PluginManager\";\nimport * as Path from \"path\";\nimport {homeDirectory, io} from \"../utils/Common\";\n\nconst rvmDirectory = Path.join(homeDirectory, \".rvm\");\nconst rubyVersionFileName = \".ruby-version\";\nconst gemSetNameFileName = \".ruby-gemset\";\n\nasync function getRubyVersion(directory: string): Promise<string> {\n    if (await io.fileExists(Path.join(directory, rubyVersionFileName))) {\n        return (await io.readFile(Path.join(directory, rubyVersionFileName))).trim();\n    } else {\n        const resolvedPath = await io.realPath(Path.join(rvmDirectory, \"rubies\", \"default\"));\n        return resolvedPath.split(\"-\")[1];\n    }\n}\n\nasync function getGemSetName(directory: string): Promise<string> {\n    const gemSetNameFilePath = Path.join(directory, gemSetNameFileName);\n\n    if (await io.fileExists(gemSetNameFilePath)) {\n        return (await io.readFile(gemSetNameFilePath)).trim();\n    } else {\n        return \"global\";\n    }\n}\n\n/**\n * Contract: the non-global path should be first.\n */\nfunction getGemSetPaths(rubyVersion: string, gemSetName: string): string[] {\n    const suffixes = gemSetName === \"global\" ? [\"\", \"@global\"] : [`@${gemSetName}`, \"@global\"];\n    return suffixes.map(suffix => Path.join(rvmDirectory, \"gems\", `ruby-${rubyVersion}${suffix}`));\n}\n\nfunction binPaths(rubyVersion: string, gemSetName: string): string[] {\n    return [\n        Path.join(rvmDirectory, \"bin\"),\n        Path.join(rvmDirectory, \"rubies\", `ruby-${rubyVersion}`, \"bin\"),\n        ...getGemSetPaths(rubyVersion, gemSetName).map(path => Path.join(path, \"bin\")),\n    ];\n}\n\nasync function withRvmData(directory: string, callback: (binPaths: string[], gemPaths: string[]) => void) {\n    try {\n        const rubyVersion = await getRubyVersion(directory);\n        const gemSetName = await getGemSetName(directory);\n        const gemPaths = getGemSetPaths(rubyVersion, gemSetName);\n\n        callback(binPaths(rubyVersion, gemSetName), gemPaths);\n    } catch (e) {\n        if (e.code === \"ENOENT\") {\n            // No RVM installed. Ignore exception.\n        } else {\n            throw e;\n        }\n    }\n}\n\nPluginManager.registerEnvironmentObserver({\n    presentWorkingDirectoryWillChange: () => async(session: Session, directory: string) => {\n        withRvmData(directory, binPaths => {\n            binPaths.forEach(path => session.environment.path.remove(path));\n\n            session.environment.setMany({\n                GEM_PATH: \"\",\n                GEM_HOME: \"\",\n            });\n        });\n    },\n    presentWorkingDirectoryDidChange: async(session: Session, directory: string) => {\n        withRvmData(directory, (binPaths, gemPaths) => {\n            binPaths.forEach(path => session.environment.path.prepend(path));\n\n            session.environment.setMany({\n                GEM_PATH: gemPaths.join(Path.delimiter),\n                GEM_HOME: gemPaths[0],\n            });\n        });\n    },\n});\n"
  },
  {
    "path": "src/plugins/SaveHistory.ts",
    "content": "import {appendFileSync} from \"fs\";\nimport {historyFilePath} from \"../utils/Common\";\nimport * as csvStringify from \"csv-stringify\";\nimport {services} from \"../services/index\";\n\nservices.jobs.onFinish.subscribe(job => services.history.add({\n    command: job.prompt.value,\n    expandedCommand: job.prompt.expandedTokens.map(t => t.escapedValue).join(\" \"),\n    timestamp: job.startTime,\n    directory: job.environment.pwd,\n    sessionID: job.session.id,\n}));\n\nservices.history.onNewRecord.subscribe(record => csvStringify(\n    [Object.values(record)],\n    (_error, output) => appendFileSync(historyFilePath, output),\n));\n"
  },
  {
    "path": "src/plugins/SaveWindowBounds.ts",
    "content": "import {services} from \"../services/index\";\nimport {windowBoundsFilePath} from \"../utils/Common\";\nimport {outputJSON} from \"fs-extra\";\n\nservices.window.onBoundsChange.subscribe(bounds => outputJSON(windowBoundsFilePath, bounds));\n"
  },
  {
    "path": "src/plugins/Show.tsx",
    "content": "import * as React from \"react\";\nimport {PluginManager} from \"../PluginManager\";\nimport {Job} from \"../shell/Job\";\n\nPluginManager.registerPrettyfier({\n    prettify: (job: Job): React.ReactElement<any> => {\n        const rows = job.output.toLines().map(path => <img style={{maxHeight: \"90vh\", maxWidth: \"100vh\"}} src={path}/>);\n\n        return <div>{rows}</div>;\n    },\n\n    isApplicable: (job: Job): boolean => {\n        return job.hasOutput() && (job.prompt.commandName === \"show\");\n    },\n});\n"
  },
  {
    "path": "src/plugins/UpdateLastPresentWorkingDirectory.ts",
    "content": "import {services} from \"../services/index\";\nimport {outputJSON} from \"fs-extra\";\nimport {presentWorkingDirectoryFilePath} from \"../utils/Common\";\n\nservices.jobs.onFinish.subscribe(job =>\n    outputJSON(presentWorkingDirectoryFilePath, job.session.directory),\n);\n"
  },
  {
    "path": "src/plugins/completion/Brew.ts",
    "content": "import {\n    emptyProvider,\n    longFlag,\n    provide,\n    shortFlag,\n    staticSuggestionsProvider,\n    Suggestion,\n} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {PluginManager} from \"../../PluginManager\";\nimport {AutocompletionContext, AutocompletionProvider} from \"../../Interfaces\";\nimport {executeCommand} from \"../../PTY\";\nimport {concat, find, memoize, sortBy} from \"lodash\";\nimport {homeDirectory} from \"../../utils/Common\";\n\ninterface FormulaAttributes {\n    name: string;\n    path: string;\n}\n\nconst getFormulae = memoize(\n    async(brewArgs: string[]): Promise<FormulaAttributes[]> => {\n        const text = await executeCommand(\"brew\", brewArgs, homeDirectory);\n\n        const matches = text.match(/^([\\-a-zA-Z0-9]+\\/)*([\\-a-zA-Z0-9]+)$/gm);\n        if (matches) {\n            return matches.map((match: string) => {\n                const matchParts = match.split(\"/\");\n                return {\n                    name: matchParts[matchParts.length - 1],\n                    path: matchParts.length > 1 ? match.trim() : \"\",\n                };\n            });\n        }\n        return [];\n    },\n    (brewArgs: string[]) => brewArgs.join(\" \"),\n);\n\nconst getAllFormulae = (cask: boolean) => getFormulae(cask ? [\"cask\", \"search\"] : [\"search\"]);\nconst getInstalledFormulae = (cask: boolean) => getFormulae(cask ? [\"cask\", \"list\"] : [\"list\"]);\n\nconst formulaSuggestions = async(formulae: FormulaAttributes[],\n                                 query: string): Promise<Suggestion[]> => {\n    if (!formulae) {\n        return [];\n    }\n    return formulae\n        .filter(formula =>  !query ||\n        formula.name.startsWith(query) ||\n        formula.path.startsWith(query))\n        .map(formula => ({\n            label: formula.name,\n        }));\n};\n\nconst availableFormulae = provide(async context => {\n    const argument = context.argument.command.nthArgument(2);\n    const query = argument ? argument.value : \"\";\n    return formulaSuggestions(await getAllFormulae(false), query);\n});\n\nconst installedFormulae = provide(async context => {\n    const argument = context.argument.command.nthArgument(2);\n    const query = argument ? argument.value : \"\";\n    return formulaSuggestions(await getInstalledFormulae(false), query);\n});\n\nconst caskAvailableFormulae = provide(async context => {\n    const argument = context.argument.command.nthArgument(3);\n    const query = argument ? argument.value : \"\";\n    return formulaSuggestions(await getAllFormulae(true), query);\n});\n\nconst caskInstalledFormulae = provide(async context => {\n    const argument = context.argument.command.nthArgument(3);\n    const query = argument ? argument.value : \"\";\n    return formulaSuggestions(await getInstalledFormulae(true), query);\n});\n\ninterface BrewCommandData {\n    name: string;\n    description: string;\n    provider?: AutocompletionProvider;\n    commands?: BrewCommandData[];\n}\n\nconst commonCommands: BrewCommandData[] = [\n    {\n        name: \"search\",\n        description: \"Perform a substring search of formula names\",\n        provider: combine([\n            longFlag(\"desc\"),\n        ]),\n    },\n    {\n        name: \"update\",\n        description: \"Fetch the newest version of Homebrew\",\n        provider: combine([\n            longFlag(\"merge\"),\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"list\",\n        description: \"List all installed formulae\",\n        provider: combine([\n            longFlag(\"versions\"),\n            longFlag(\"pinned\"),\n        ]),\n    },\n    {\n        name: \"doctor\",\n        description: \"Check your system for potential problems\",\n        provider: emptyProvider,\n    },\n    {\n        name: \"create\",\n        description: \"Generate a formula for a downloadable file\",\n        provider: combine([\n            longFlag(\"autotools\"),\n            longFlag(\"cmake\"),\n            longFlag(\"no-fetch\"),\n            longFlag(\"set-name\"),\n            longFlag(\"tap\"),\n        ]),\n    },\n];\n\nconst caskCommands: BrewCommandData[] = [\n    {\n        name: \"install\",\n        description: \"Install a Cask formula\",\n        provider: combine([\n            caskAvailableFormulae,\n            longFlag(\"force\"),\n            longFlag(\"skip-cask-deps\"),\n            longFlag(\"require-sha\"),\n        ]),\n    },\n    {\n        name: \"fetch\",\n        description: \"Download a Cask formula\",\n        provider: combine([\n            caskAvailableFormulae,\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"remove\",\n        description: \"Uninstall a Cask formula\",\n        provider: combine([\n            caskInstalledFormulae,\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"uninstall\",\n        description: \"Uninstall a Cask formula\",\n        provider: combine([\n            caskInstalledFormulae,\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"cat\",\n        description: \"Display the source to a Cask formula\",\n        provider: caskInstalledFormulae,\n    },\n    {\n        name: \"cleanup\",\n        description: \"Remove old version and download files for formula\",\n        provider: combine([\n            caskInstalledFormulae,\n            longFlag(\"outdated\"),\n        ]),\n    },\n    {\n        name: \"info\",\n        description: \"Display information about a Cask formula\",\n        provider: caskAvailableFormulae,\n    },\n    {\n        name: \"home\",\n        description: \"Open a Cask formula homepage in the browser\",\n        provider: caskAvailableFormulae,\n    },\n    {\n        name: \"edit\",\n        description: \"Edit a Cask formula\",\n        provider: caskInstalledFormulae,\n    },\n];\n\nconst brewCommands: BrewCommandData[] = [\n    {\n        name: \"install\",\n        description: \"Install a formula\",\n        provider: combine([\n            availableFormulae,\n            longFlag(\"debug\"),\n            longFlag(\"env\"),\n            longFlag(\"ignore-dependencies\"),\n            longFlag(\"only-dependencies\"),\n            longFlag(\"build-from-source\"),\n            longFlag(\"devel\"),\n            longFlag(\"keep-temp\"),\n            longFlag(\"cc\"),\n        ]),\n    },\n    {\n        name: \"cask\",\n        description: \"Install a cask formula\",\n        commands: concat(caskCommands, commonCommands),\n    },\n    {\n        name: \"fetch\",\n        description: \"Install a formula\",\n        provider: combine([\n            availableFormulae,\n            longFlag(\"force\"),\n            longFlag(\"retry\"),\n            longFlag(\"deps\"),\n            longFlag(\"build-from-source\"),\n            longFlag(\"force-bottle\"),\n        ]),\n    },\n    {\n        name: \"remove\",\n        description: \"Uninstall a formula\",\n        provider: combine([\n            installedFormulae,\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"uninstall\",\n        description: \"Uninstall a formula\",\n        provider: combine([\n            installedFormulae,\n            longFlag(\"force\"),\n        ]),\n    },\n    {\n        name: \"upgrade\",\n        description: \"Upgrade a formula\",\n        provider: combine([\n            installedFormulae,\n            longFlag(\"upgrade\"),\n            longFlag(\"fetch-HEAD\"),\n        ]),\n    },\n    {\n        name: \"cat\",\n        description: \"Display the source to a formula\",\n        provider: installedFormulae,\n    },\n    {\n        name: \"cleanup\",\n        description: \"Remove old version and download files for formula\",\n        provider: combine([\n            installedFormulae,\n            longFlag(\"prune\"),\n            longFlag(\"dry-run\"),\n            shortFlag(\"s\"),\n        ]),\n    },\n    {\n        name: \"deps\",\n        description: \"Show dependencies for a formula\",\n        provider: combine([\n            availableFormulae,\n            longFlag(\"1\"),\n            shortFlag(\"n\"),\n            longFlag(\"union\"),\n            longFlag(\"installed\"),\n            longFlag(\"include-build\"),\n            longFlag(\"include-optional\"),\n            longFlag(\"skip-recommended\"),\n            longFlag(\"tree\"),\n        ]),\n    },\n    {\n        name: \"info\",\n        description: \"Display information about a formula\",\n        provider: combine([\n            availableFormulae,\n            longFlag(\"github\"),\n            longFlag(\"json\"),\n            longFlag(\"installed\"),\n            longFlag(\"all\"),\n        ]),\n    },\n    {\n        name: \"home\",\n        description: \"Open a formula homepage in the browser\",\n        provider: availableFormulae,\n    },\n    {\n        name: \"options\",\n        description: \"Display install options for a formula\",\n        provider: availableFormulae,\n    },\n    {\n        name: \"edit\",\n        description: \"Edit a formula\",\n        provider: installedFormulae,\n    },\n];\n\nconst fromData = (commandsData: BrewCommandData[]) => {\n    const suggestions = sortBy(\n        commandsData.map(command => ({label: command.name, detail: command.description || \"\"})),\n        suggestion => !suggestion.detail,\n    );\n    return staticSuggestionsProvider(suggestions);\n};\n\nlet getProvider = (context: AutocompletionContext,\n                   commandData: BrewCommandData[],\n                   argIndex: number): AutocompletionProvider => {\n    if (context.argument.position === argIndex) {\n        return fromData(commandData);\n    }\n\n    const argument = context.argument.command.nthArgument(argIndex);\n    if (!argument) {\n        return emptyProvider;\n    }\n\n    const name = argument.value;\n    const data = find(commandData, {name});\n\n    if (data && data.commands) {\n        return getProvider(context, data.commands, argIndex + 1);\n    } else if (data && data.provider) {\n        return data.provider;\n    }\n\n    return emptyProvider;\n};\n\nPluginManager.registerAutocompletionProvider(\"brew\", async context => {\n    const provider = getProvider(context, concat(brewCommands, commonCommands), 1);\n    return provider(context);\n});\n"
  },
  {
    "path": "src/plugins/completion/Cat.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {anyFilesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"cat\", combine([anyFilesSuggestionsProvider, manPageOptions(\"cat\")]));\n"
  },
  {
    "path": "src/plugins/completion/Cd.ts",
    "content": "import {directoriesSuggestionsProvider, Suggestion} from \"../completion_utils/Common\";\nimport * as _ from \"lodash\";\nimport {PluginManager} from \"../../PluginManager\";\nimport {join} from \"path\";\nimport {userFriendlyPath} from \"../../utils/Common\";\n\nPluginManager.registerAutocompletionProvider(\"cd\", async(context) => {\n    let suggestions: Suggestion[] = await directoriesSuggestionsProvider(context);\n\n    if (context.argument.value.length === 0) {\n        const cdpathDirectories = _.flatten(await Promise.all(context.environment.cdpath.filter(path => path !== \".\" && path !== context.environment.pwd)\n            .map(async(directory) => (await directoriesSuggestionsProvider(context, directory)).map(suggestion => ({...suggestion, label: userFriendlyPath(join(directory, suggestion.label))})))));\n\n        suggestions.push(...cdpathDirectories);\n    }\n\n    return suggestions;\n});\n"
  },
  {
    "path": "src/plugins/completion/Cp.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {anyFilesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"cp\", combine([anyFilesSuggestionsProvider, manPageOptions(\"cp\")]));\n"
  },
  {
    "path": "src/plugins/completion/Df.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"df\", manPageOptions(\"df\"));\n"
  },
  {
    "path": "src/plugins/completion/Executable.ts",
    "content": "export const commandDescriptions: Dictionary<string> = {\n    admin: \"Create and administer SCCS files\",\n    alias: \"Define or display aliases\",\n    ar: \"Create and maintain library archives\",\n    asa: \"Interpret carriage-control characters\",\n    at: \"Execute commands at a later time\",\n    awk: \"Pattern scanning and processing language\",\n    basename: \"Return non-directory portion of a pathname; see also dirname\",\n    batch: \"Schedule commands to be executed in a batch queue\",\n    bc: \"Arbitrary-precision arithmetic language\",\n    bg: \"Run jobs in the background\",\n    cc: \"Compile standard C programs\",\n    cal: \"Print a calendar\",\n    cat: \"Concatenate and print files\",\n    cflow: \"Generate a C-language flowgraph\",\n    chgrp: \"Change the file group ownership\",\n    chmod: \"Change the file modes/attributes/permissions\",\n    chown: \"Change the file ownership\",\n    cksum: \"Write file checksums and sizes\",\n    cmp: \"Compare two files; see also diff\",\n    comm: \"Select or reject lines common to two files\",\n    command: \"Execute a simple command\",\n    compress: \"Compress data\",\n    cp: \"Copy files\",\n    crontab: \"Schedule periodic background work\",\n    csplit: \"Split files based on context\",\n    ctags: \"Create a tags file\",\n    cut: \"Cut out selected fields of each line of a file\",\n    cxref: \"Generate a C-language program cross-reference table\",\n    date: \"Display the date and time\",\n    dd: \"Convert and copy a file\",\n    delta: \"Make a delta (change) to an SCCS file\",\n    df: \"Report free disk space\",\n    diff: \"Compare two files; see also cmp\",\n    dirname: \"Return the directory portion of a pathname; see also basename\",\n    du: \"Estimate file space usage\",\n    echo: \"Write arguments to standard output\",\n    ed: \"The standard text editor\",\n    env: \"Set the environment for command job\",\n    ex: \"Text editor\",\n    expand: \"Convert tabs to spaces\",\n    expr: \"Evaluate arguments as an expression\",\n    FALSE: \"Return false value\",\n    fc: \"Process the command history list\",\n    fg: \"Run jobs in the foreground\",\n    file: \"Determine file type\",\n    find: \"Find files\",\n    fold: \"Filter for folding lines\",\n    fort77: \"FORTRAN compiler\",\n    fuser: \"List process IDs of all processes that have one or more files open\",\n    gencat: \"Generate a formatted message catalog\",\n    get: \"Get a version of an SCCS file\",\n    getconf: \"Get configuration values\",\n    getopts: \"Parse utility options\",\n    grep: \"Search text for a pattern\",\n    hash: \"hash database access method\",\n    head: \"Copy the first part of files\",\n    iconv: \"Codeset conversion\",\n    id: \"Return user identity\",\n    ipcrm: \"Remove a message queue, semaphore set, or shared memory segment identifier\",\n    ipcs: \"Report interprocess communication facilities status\",\n    jobs: \"Display status of jobs in the current session\",\n    join: \"Merges two sorted text files based on the presence of a common field\",\n    kill: \"Terminate or signal processes\",\n    lex: \"Generate programs for lexical tasks\",\n    link: \"Create a hard link to a file\",\n    ln: \"Link files\",\n    locale: \"Get locale-specific information\",\n    localedef: \"Define locale environment\",\n    logger: \"Log messages\",\n    logname: \"Return the user\\\"s login name\",\n    lp: \"Send files to a printer\",\n    ls: \"List directory contents\",\n    m4: \"Macro processor\",\n    mailx: \"Process messages\",\n    make: \"Maintain, update, and regenerate groups of programs\",\n    man: \"Display system documentation\",\n    mesg: \"Permit or deny messages\",\n    mkdir: \"Make directories\",\n    mkfifo: \"Make FIFO special files\",\n    more: \"Display files on a page-by-page basis\",\n    mv: \"Move files\",\n    newgrp: \"Change to a new group (functionaliy similar to sg[1])\",\n    nice: \"Invoke a utility with an altered nice value\",\n    nl: \"Line numbering filter\",\n    nm: \"Write the name list of an object file\",\n    nohup: \"Invoke a utility immune to hangups\",\n    od: \"Dump files in various formats\",\n    paste: \"Merge corresponding or subsequent lines of files\",\n    patch: \"Apply changes to files\",\n    pathchk: \"Check pathnames\",\n    pax: \"Portable archive interchange\",\n    pr: \"Print files\",\n    printf: \"Write formatted output\",\n    prs: \"Print an SCCS file\",\n    ps: \"Report process status\",\n    pwd: \"print working directory - Return working directory name\",\n    qalter: \"Alter batch job\",\n    qdel: \"Delete batch jobs\",\n    qhold: \"Hold batch jobs\",\n    qmove: \"Move batch jobs\",\n    qmsg: \"Send message to batch jobs\",\n    qrerun: \"Rerun batch jobs\",\n    qrls: \"Release batch jobs\",\n    qselect: \"Select batch jobs\",\n    qsig: \"Signal batch jobs\",\n    qstat: \"Show status of batch jobs\",\n    qsub: \"Submit a script\",\n    read: \"Read a line from standard input\",\n    renice: \"Set nice values of running processes\",\n    rm: \"Remove directory entries\",\n    rmdel: \"Remove a delta from an SCCS file\",\n    rmdir: \"Remove directories\",\n    sact: \"Print current SCCS file-editing activity\",\n    sccs: \"Front end for the SCCS subsystem\",\n    sed: \"Stream editor\",\n    sh: \"Shell, the standard command language interpreter\",\n    sleep: \"Suspend execution for an interval\",\n    sort: \"Sort, merge, or sequence check text files\",\n    split: \"Split files into pieces\",\n    strings: \"Find printable strings in files\",\n    strip: \"Remove unnecessary information from executable files\",\n    stty: \"Set the options for a terminal\",\n    tabs: \"Set terminal tabs\",\n    tail: \"Copy the last part of a file\",\n    talk: \"Talk to another user\",\n    tee: \"Duplicate the standard output\",\n    test: \"Evaluate expression\",\n    time: \"Time a simple command\",\n    touch: \"Change file access and modification times\",\n    tput: \"Change terminal characteristics\",\n    tr: \"Translate characters\",\n    TRUE: \"Return true value\",\n    tsort: \"Topological sort\",\n    tty: \"Return user\\\"s terminal name     \",\n    type: \"Displays how a name would be interpreted if used as a command\",\n    ulimit: \"Set or report file size limit\",\n    umask: \"Get or set the file mode creation mask\",\n    unalias: \"Remove alias definitions\",\n    uname: \"Return system name\",\n    uncompress: \"Expand compressed data\",\n    unexpand: \"Convert spaces to tabs\",\n    unget: \"Undo a previous get of an SCCS file\",\n    uniq: \"Report or filter out repeated lines in a file\",\n    unlink: \"Call the unlink function\",\n    uucp: \"System-to-system copy\",\n    uudecode: \"Decode a binary file\",\n    uuencode: \"Encode a binary file\",\n    uustat: \"uucp status inquiry and job control\",\n    uux: \"Remote command execution\",\n    val: \"Validate SCCS files\",\n    vi: \"Screen-oriented (visual) display editor\",\n    wait: \"Await process completion\",\n    wc: \"Line, word and byte or character count\",\n    what: \"Identify SCCS files\",\n    who: \"Display who is on the system\",\n    write: \"Write to another user\\\"s terminal\",\n    xargs: \"Construct argument lists and invoke utility\",\n    yacc: \"Yet another compiler compiler\",\n    zcat: \"Expand and concatenate data\",\n};\n"
  },
  {
    "path": "src/plugins/completion/Find.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst findOptions = manPageOptions(\"find\");\n\nPluginManager.registerAutocompletionProvider(\"find\", combine([directoriesSuggestionsProvider, findOptions]));\n"
  },
  {
    "path": "src/plugins/completion/Git.ts",
    "content": "import * as Git from \"../../utils/Git\";\nimport {\n    commandWithSubcommands,\n    emptyProvider,\n    longFlag,\n    provide,\n    staticSuggestionsProvider,\n    SubcommandConfig,\n    Suggestion,\n    unique,\n} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {PluginManager} from \"../../PluginManager\";\nimport {executeCommand, linedOutputOf} from \"../../PTY\";\nimport {find, once, sortBy} from \"lodash\";\nimport {homeDirectory} from \"../../utils/Common\";\nimport {descriptions} from \"../completion_utils/Descriptions\";\n\nconst addOptions: Suggestion[] = [\n    {\n        label: \"-p\",\n        detail: descriptions.git.add.patch,\n    },\n    {\n        label: \"--patch\",\n        detail: descriptions.git.add.patch,\n    },\n    {\n        label: \"-i\",\n        detail: descriptions.git.add.interactive,\n    },\n    {\n        label: \"--interactive\",\n        detail: descriptions.git.add.interactive,\n    },\n    {\n        label: \"-n\",\n        detail: descriptions.git.add.dryRun,\n    },\n    {\n        label: \"--dry-run\",\n        detail: descriptions.git.add.dryRun,\n    },\n    {\n        label: \"-v\",\n        detail: descriptions.git.add.verbose,\n    },\n    {\n        label: \"--verbose\",\n    },\n    {\n        label: \"-f\",\n        detail: descriptions.git.add.force,\n    },\n    {\n        label: \"--force\",\n        detail: descriptions.git.add.force,\n    },\n    {\n        label: \"-e\",\n        detail: descriptions.git.add.edit,\n    },\n    {\n        label: \"--edit\",\n        detail: descriptions.git.add.edit,\n    },\n    {\n        label: \"-u\",\n        detail: descriptions.git.add.update,\n    },\n    {\n        label: \"--update\",\n        detail: descriptions.git.add.update,\n    },\n    {\n        label: \"-A\",\n        detail: descriptions.git.add.noIgnoreRemoval,\n    },\n    {\n        label: \"--all\",\n        detail: descriptions.git.add.noIgnoreRemoval,\n    },\n    {\n        label: \"--no-ignore-removal\",\n        detail: descriptions.git.add.noIgnoreRemoval,\n    },\n    {\n        label: \"--no-all\",\n        detail: descriptions.git.add.ignoreRemoval,\n    },\n    {\n        label: \"--ignore-removal\",\n        detail: descriptions.git.add.ignoreRemoval,\n    },\n    {\n        label: \"-N\",\n        detail: descriptions.git.add.intentToAdd,\n    },\n    {\n        label: \"--intent-to-add\",\n        detail: descriptions.git.add.intentToAdd,\n    },\n    {\n        label: \"--refresh\",\n        detail: descriptions.git.add.refresh,\n    },\n    {\n        label: \"--ignore-errors\",\n        detail: descriptions.git.add.ignoreErrors,\n    },\n    {\n        label: \"--ignore-missing\",\n        detail: descriptions.git.add.ignoreMissing,\n    },\n    {\n        label: \"--chmod=\",\n        detail: descriptions.git.add.chmod,\n    },\n    {\n        label: \"--\",\n        detail: descriptions.git.add.separator,\n    },\n];\n\nconst commitOptions: Suggestion[] = [\n    {\n        label: \"--message\",\n        detail: descriptions.git.commit.message,\n        kind: monaco.languages.CompletionItemKind.Snippet,\n        insertText: {value: \"--message \\\"${0:Commit message}\\\"\"},\n    },\n    {\n        label: \"-m\",\n        detail: descriptions.git.commit.message,\n        kind: monaco.languages.CompletionItemKind.Snippet,\n        insertText: {value: \"-m \\\"${0:Commit message}\\\"\"},\n    },\n    {\n        label: \"--all\",\n        detail: descriptions.git.commit.all,\n    },\n    {\n        label: \"-a\",\n        detail: descriptions.git.commit.all,\n    },\n    {\n        label: \"--patch\",\n        detail: descriptions.git.commit.patch,\n    },\n    {\n        label: \"-p\",\n        detail: descriptions.git.commit.patch,\n    },\n    {\n        label: \"--null\",\n        detail: descriptions.git.commit.NULL,\n    },\n    {\n        label: \"-z\",\n        detail: descriptions.git.commit.NULL,\n    },\n    {\n        label: \"--template\",\n        detail: descriptions.git.commit.template,\n    },\n    {\n        label: \"-t\",\n        detail: descriptions.git.commit.template,\n    },\n    {\n        label: \"--signoff\",\n        detail: descriptions.git.commit.signoff,\n    },\n    {\n        label: \"-s\",\n        detail: descriptions.git.commit.signoff,\n    },\n    {\n        label: \"--no-verify\",\n        detail: descriptions.git.commit.noVerify,\n    },\n    {\n        label: \"-n\",\n        detail: descriptions.git.commit.noVerify,\n    },\n    {\n        label: \"--edit\",\n        detail: descriptions.git.commit.edit,\n    },\n    {\n        label: \"-e\",\n        detail: descriptions.git.commit.edit,\n    },\n    {\n        label: \"--include\",\n        detail: descriptions.git.commit.include,\n    },\n    {\n        label: \"-i\",\n        detail: descriptions.git.commit.include,\n    },\n    {\n        label: \"--only\",\n        detail: descriptions.git.commit.only,\n    },\n    {\n        label: \"-o\",\n        detail: descriptions.git.commit.only,\n    },\n    {\n        label: \"--verbose\",\n        detail: descriptions.git.commit.verbose,\n    },\n    {\n        label: \"-v\",\n        detail: descriptions.git.commit.verbose,\n    },\n    {\n        label: \"--quiet\",\n        detail: descriptions.git.commit.quiet,\n    },\n    {\n        label: \"-q\",\n        detail: descriptions.git.commit.quiet,\n    },\n    {\n        label: \"--reset-author\",\n        detail: descriptions.git.commit.resetAuthor,\n    },\n    {\n        label: \"--short\",\n        detail: descriptions.git.commit.short,\n    },\n    {\n        label: \"--branch\",\n        detail: descriptions.git.commit.branch,\n    },\n    {\n        label: \"--porcelain\",\n        detail: descriptions.git.commit.porcelain,\n    },\n    {\n        label: \"--long\",\n        detail: descriptions.git.commit.long,\n    },\n    {\n        label: \"--allow-empty\",\n        detail: descriptions.git.commit.allowEmpty,\n    },\n    {\n        label: \"--allow-empty-message\",\n        detail: descriptions.git.commit.allowEmptyMessage,\n    },\n    {\n        label: \"--no-edit\",\n        detail: descriptions.git.commit.noEdit,\n    },\n    {\n        label: \"--no-post-rewrite\",\n        detail: descriptions.git.commit.noPostRewrite,\n    },\n    {\n        label: \"--dry-run\",\n        detail: descriptions.git.commit.dryRun,\n    },\n    {\n        label: \"--status\",\n        detail: descriptions.git.commit.status,\n    },\n    {\n        label: \"--no-status\",\n        detail: descriptions.git.commit.noStatus,\n    },\n    {\n        label: \"--no-gpg-sign\",\n        detail: descriptions.git.commit.noGpgSign,\n    },\n];\n\nconst pushOptions: Suggestion[] = [\n    {\n        label: \"--all\",\n        detail: descriptions.git.push.all,\n    },\n    {\n        label: \"--prune\",\n        detail: descriptions.git.push.prune,\n    },\n    {\n        label: \"--force\",\n    },\n    {\n        label: \"--force-with-lease\",\n    },\n];\n\nconst resetOptions: Suggestion[] = [\n    {\n        label: \"--soft\",\n    },\n    {\n        label: \"--mixed\",\n    },\n    {\n        label: \"--hard\",\n    },\n    {\n        label: \"--merge\",\n    },\n    {\n        label: \"--keep\",\n    },\n];\n\nconst stashOptions: Suggestion[] = [\n    {\n        label: \"list\",\n    },\n    {\n        label: \"show\",\n    },\n    {\n        label: \"drop\",\n    },\n    {\n        label: \"pop\",\n    },\n    {\n        label: \"apply\",\n    },\n    {\n        label: \"save\",\n    },\n    {\n        label: \"push\",\n    },\n    {\n        label: \"clear\",\n    },\n    {\n        label: \"create\",\n    },\n    {\n        label: \"store\",\n    },\n];\n\nconst statusOptions: Suggestion[] = [\n    {\n        label: \"-s\",\n        detail: descriptions.git.status.short,\n    },\n    {\n        label: \"--short\",\n        detail: descriptions.git.status.short,\n    },\n    {\n        label: \"-b\",\n        detail: descriptions.git.status.branch,\n    },\n    {\n        label: \"--branch\",\n        detail: descriptions.git.status.branch,\n    },\n    {\n        label: \"--porcelain\",\n        detail: descriptions.git.status.porcelain,\n    },\n    {\n        label: \"--long\",\n        detail: descriptions.git.status.long,\n    },\n    {\n        label: \"-v\",\n        detail: descriptions.git.status.verbose,\n    },\n    {\n        label: \"--verbose\",\n        detail: descriptions.git.status.verbose,\n    },\n    {\n        label: \"-u\",\n        detail: descriptions.git.status.untrackedFiles,\n    },\n    {\n        label: \"--untracked-files\",\n        detail: descriptions.git.status.untrackedFiles,\n    },\n    {\n        label: \"--ignore-submodules\",\n        detail: descriptions.git.status.ignoreSubmodules,\n    },\n    {\n        label: \"--ignored\",\n        detail: descriptions.git.status.ignored,\n    },\n    {\n        label: \"-z\",\n        detail: descriptions.git.status.terminateWithNull,\n    },\n    {\n        label: \"--column\",\n        detail: descriptions.git.status.column,\n    },\n    {\n        label: \"--no-column\",\n        detail: descriptions.git.status.column,\n    },\n];\n\nconst configOptions: Suggestion[] = [\n    {\n        label: \"--global\",\n    },\n    {\n        label: \"--system\",\n    },\n    {\n        label: \"--list\",\n    },\n    {\n        label: \"-l\",\n    },\n    {\n        label: \"--edit\",\n    },\n    {\n        label: \"-e\",\n    },\n];\n\nconst fetchOptions: Suggestion[] = [\n    {\n        label: \"--quiet\",\n    },\n    {\n        label: \"--verbose\",\n    },\n    {\n        label: \"--append\",\n    },\n    {\n        label: \"--upload-pack\",\n    },\n    {\n        label: \"--force\",\n    },\n    {\n        label: \"--keep\",\n    },\n    {\n        label: \"--depth=\",\n    },\n    {\n        label: \"--tags\",\n    },\n    {\n        label: \"--no-tags\",\n    },\n    {\n        label: \"--all\",\n    },\n    {\n        label: \"--prune\",\n    },\n    {\n        label: \"--dry-run\",\n    },\n    {\n        label: \"--recurse-submodules=\",\n    },\n];\n\nconst checkoutOptions: Suggestion[] = [\n    {\n        label: \"-b\",\n        detail: descriptions.git.checkout.branch,\n    },\n];\n\nconst commonMergeOptions: Suggestion[] = [\n    {\n        label: \"--no-commit\",\n    },\n    {\n        label: \"--no-stat\",\n    },\n    {\n        label: \"--log\",\n    },\n    {\n        label: \"--no-log\",\n    },\n    {\n        label: \"--squash\",\n    },\n    {\n        label: \"--strategy\",\n    },\n    {\n        label: \"--commit\",\n    },\n    {\n        label: \"--stat\",\n    },\n    {\n        label: \"--no-squash\",\n    },\n    {\n        label: \"--ff\",\n    },\n    {\n        label: \"--no-ff\",\n    },\n    {\n        label: \"--ff-only\",\n    },\n    {\n        label: \"--edit\",\n    },\n    {\n        label: \"--no-edit\",\n    },\n    {\n        label: \"--verify-signatures\",\n    },\n    {\n        label: \"--no-verify-signatures\",\n    },\n    {\n        label: \"--gpg-sign\",\n    },\n    {\n        label: \"--quiet\",\n    },\n    {\n        label: \"--verbose\",\n    },\n    {\n        label: \"--progress\",\n    },\n    {\n        label: \"--no-progress\",\n    },\n];\n\nfunction doesLookLikeBranchAlias(word: string) {\n    if (!word) return false;\n    return word.startsWith(\"-\") || word.includes(\"@\") || word.includes(\"HEAD\") || /\\d/.test(word);\n}\n\nfunction canonizeBranchAlias(alias: string) {\n    if (alias[0] === \"-\") {\n        const steps = parseInt(alias.slice(1), 10) || 1;\n        alias = `@{-${steps}}`;\n    }\n\n    return alias;\n}\n\nconst remotes = provide(async context => {\n    if (Git.isGitDirectory(context.environment.pwd)) {\n        const names = await Git.remotes(context.environment.pwd);\n        return names.map(name => ({label: name}));\n    }\n\n    return [];\n});\n\nconst configVariables = unique(provide(async context => {\n    const variables = await Git.configVariables(context.environment.pwd);\n\n    return variables.map(variable => ({label: variable.name, detail: variable.value}));\n}));\n\nconst branchesExceptCurrent = provide(async context => {\n    if (Git.isGitDirectory(context.environment.pwd)) {\n        const allBranches = (await Git.branches({\n            directory: context.environment.pwd,\n            remotes: true,\n            tags: false,\n        }));\n        const nonCurrentBranches = allBranches.filter(branch => !branch.isCurrent());\n        return nonCurrentBranches.map(branch => ({label: branch.toString()}));\n    } else {\n        return [];\n    }\n});\n\nconst branchAlias = provide(async context => {\n    if (doesLookLikeBranchAlias(context.argument.value)) {\n        let nameOfAlias = (await linedOutputOf(\"git\", [\"name-rev\", \"--name-only\", canonizeBranchAlias(context.argument.value)], context.environment.pwd))[0];\n        if (nameOfAlias && !nameOfAlias.startsWith(\"Could not get\")) {\n            return [{label: context.argument.value, detail: nameOfAlias}];\n        }\n    }\n\n    return [];\n});\n\nconst notStagedFiles = unique(provide(async context => {\n    if (Git.isGitDirectory(context.environment.pwd)) {\n        const fileStatuses = await Git.status(context.environment.pwd);\n        return fileStatuses.map(fileStatus => ({label: fileStatus.value}));\n    } else {\n        return [];\n    }\n}));\n\nconst commandsData: SubcommandConfig[] = [\n    {\n        name: \"add\",\n        detail: descriptions.git.subcommands.add,\n        provider: combine([notStagedFiles, staticSuggestionsProvider(addOptions)]),\n    },\n    {\n        name: \"am\",\n        detail: descriptions.git.subcommands.am,\n    },\n    {\n        name: \"archive\",\n        detail: descriptions.git.subcommands.archive,\n    },\n    {\n        name: \"bisect\",\n        detail: descriptions.git.subcommands.bisect,\n    },\n    {\n        name: \"branch\",\n        detail: descriptions.git.subcommands.branch,\n        provider: branchesExceptCurrent,\n    },\n    {\n        name: \"bundle\",\n        detail: descriptions.git.subcommands.bundle,\n    },\n    {\n        name: \"checkout\",\n        detail: descriptions.git.subcommands.checkout,\n        provider: combine([branchesExceptCurrent, branchAlias, notStagedFiles, staticSuggestionsProvider(checkoutOptions)]),\n    },\n    {\n        name: \"cherry-pick\",\n        detail: descriptions.git.subcommands.cherryPick,\n    },\n    {\n        name: \"citool\",\n        detail: descriptions.git.subcommands.citool,\n    },\n    {\n        name: \"clean\",\n        detail: descriptions.git.subcommands.clean,\n    },\n    {\n        name: \"clone\",\n        detail: descriptions.git.subcommands.clone,\n    },\n    {\n        name: \"commit\",\n        detail: descriptions.git.subcommands.commit,\n        provider: staticSuggestionsProvider(commitOptions),\n    },\n    {\n        name: \"config\",\n        detail: descriptions.git.subcommands.config,\n        provider: combine([configVariables, staticSuggestionsProvider(configOptions)]),\n    },\n    {\n        name: \"describe\",\n        detail: descriptions.git.subcommands.describe,\n    },\n    {\n        name: \"diff\",\n        detail: descriptions.git.subcommands.diff,\n    },\n    {\n        name: \"fetch\",\n        detail: descriptions.git.subcommands.fetch,\n        provider: combine([remotes, staticSuggestionsProvider(fetchOptions)]),\n    },\n    {\n        name: \"format-patch\",\n        detail: descriptions.git.subcommands.formatPatch,\n    },\n    {\n        name: \"gc\",\n        detail: descriptions.git.subcommands.gc,\n    },\n    {\n        name: \"grep\",\n        detail: descriptions.git.subcommands.grep,\n    },\n    {\n        name: \"gui\",\n        detail: descriptions.git.subcommands.gui,\n    },\n    {\n        name: \"init\",\n        detail: descriptions.git.subcommands.init,\n    },\n    {\n        name: \"log\",\n        detail: descriptions.git.subcommands.log,\n    },\n    {\n        name: \"merge\",\n        detail: descriptions.git.subcommands.merge,\n        provider: combine([\n            branchesExceptCurrent,\n            branchAlias,\n            staticSuggestionsProvider(commonMergeOptions),\n            longFlag(\"rerere-autoupdate\"),\n            longFlag(\"no-rerere-autoupdate\"),\n            longFlag(\"abort\"),\n        ]),\n    },\n    {\n        name: \"mv\",\n        detail: descriptions.git.subcommands.mv,\n    },\n    {\n        name: \"notes\",\n        detail: descriptions.git.subcommands.notes,\n    },\n    {\n        name: \"pull\",\n        detail: descriptions.git.subcommands.pull,\n        provider: combine([\n            longFlag(\"rebase\"),\n            longFlag(\"no-rebase\"),\n            staticSuggestionsProvider(commonMergeOptions),\n            staticSuggestionsProvider(fetchOptions),\n        ]),\n    },\n    {\n        name: \"push\",\n        detail: descriptions.git.subcommands.push,\n        provider: staticSuggestionsProvider(pushOptions),\n    },\n    {\n        name: \"rebase\",\n        detail: descriptions.git.subcommands.rebase,\n    },\n    {\n        name: \"reset\",\n        detail: descriptions.git.subcommands.reset,\n        provider: staticSuggestionsProvider(resetOptions),\n    },\n    {\n        name: \"revert\",\n        detail: descriptions.git.subcommands.revert,\n    },\n    {\n        name: \"rm\",\n        detail: descriptions.git.subcommands.rm,\n    },\n    {\n        name: \"shortlog\",\n        detail: descriptions.git.subcommands.shortlog,\n    },\n    {\n        name: \"show\",\n        detail: descriptions.git.subcommands.show,\n    },\n    {\n        name: \"stash\",\n        detail: descriptions.git.subcommands.stash,\n        provider: staticSuggestionsProvider(stashOptions),\n    },\n    {\n        name: \"status\",\n        detail: descriptions.git.subcommands.status,\n        provider: staticSuggestionsProvider(statusOptions),\n    },\n    {\n        name: \"submodule\",\n        detail: descriptions.git.subcommands.submodule,\n    },\n    {\n        name: \"tag\",\n        detail: descriptions.git.subcommands.tag,\n    },\n    {\n        name: \"worktree\",\n        detail: descriptions.git.subcommands.worktree,\n    },\n];\n\nconst commands = once(async(): Promise<SubcommandConfig[]> => {\n    const text = await executeCommand(\"git\", [\"help\", \"-a\"], homeDirectory);\n    const matches: string[] | null = text.match(/  ([\\-a-zA-Z0-9]+)/gm);\n\n    if (matches) {\n        const suggestions = matches\n            .filter(match => match.indexOf(\"--\") === -1)\n            .map(match => {\n                const name = match.trim();\n                const data = find(commandsData, {name});\n\n                return {\n                    name,\n                    detail: data ? data.detail : \"\",\n                    provider: data ? data.provider : emptyProvider,\n                };\n            });\n\n        return sortBy(suggestions, suggestion => !suggestion.detail);\n    }\n\n    return [];\n});\n\nconst aliases = once(async(): Promise<SubcommandConfig[]> => {\n    const aliasList = await Git.aliases(homeDirectory);\n    return aliasList.map(({ name, value }) => {\n        let result: SubcommandConfig = {\n            name: name,\n        };\n\n        const expandedAliasConfig = find(commandsData, data => data.name === value);\n        if (expandedAliasConfig && expandedAliasConfig.provider) {\n            result.provider = expandedAliasConfig.provider;\n        }\n\n        return result;\n    });\n});\n\nPluginManager.registerAutocompletionProvider(\"git\", async context => {\n    const allCommands = [...(await aliases()), ...(await commands())];\n    return commandWithSubcommands(allCommands)(context);\n});\n"
  },
  {
    "path": "src/plugins/completion/Grep.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {\n    longFlag, longAndShortFlag, mapSuggestions, anyFilesSuggestionsProvider,\n    anyFilesSuggestions, directoriesSuggestions, provide,\n} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {mapObject} from \"../../utils/Common\";\n\n// Grep option suggestions based on linux  man file:\n// http://linux.die.net/man/1/grep\nconst baseOptions = combine(mapObject(\n    {\n        // Generic Program Information\n        \"version\": {\n            short: \"V\",\n            description: `Print the version number of grep to standard error.`,\n        },\n        // Matcher Selection\n        \"basic-regexp\": {\n            short: \"G\",\n            description: `Interpret PATTERN as a basic regular expression. This is the default.`,\n        },\n        \"perl-regexp\": {\n            short: \"P\",\n            description: `Interpret PATTERN as a Perl regular expression.`,\n        },\n        // Matching Control\n        \"regexp\": {\n            short: \"e\",\n            description: `Print the byte offset within the input file before each line of output`,\n        },\n        \"file=\": {\n            short: \"\",\n            description: `Obtain  patterns  from  FILE, one per line`,\n        },\n        \"ignore-case\": {\n            short: \"i\",\n            description: `Ignore case distinctions in both the PATTERN and the input files`,\n        },\n        \"invert-match\": {\n            short: \"v\",\n            description: `Invert the sense of matching, to select non-matching lines`,\n        },\n        \"word-regexp\": {\n            short: \"w\",\n            description: `Select only those  lines  containing  matches  that  form  whole words`,\n        },\n        \"line-regexp\": {\n            short: \"x\",\n            description: `Select only those matches that exactly match the whole line`,\n        },\n        // General Output Control\n        \"count\": {\n            short: \"c\",\n            description: `Suppress normal output; instead print a count of matching lines for each input\n                file. With the -v, --invert-match option (see below), count non-matching lines.`,\n        },\n        \"color=\": {\n            short: \"\",\n            description: `Surround the matching string with the marker find in GREP_COLOR environment variable`,\n        },\n        \"files-without-match\": {\n            short: \"L\",\n            description: `Suppress normal output; instead print the name of each input file from which no\n            output would normally have been printed. The scanning will stop on the first match`,\n        },\n        \"files-with-match\": {\n            short: \"l\",\n            description: `Suppress normal output; instead print the name of each input file from which output\n                would normally have been printed. The scanning will stop on the first match.`,\n        },\n        \"max-count\": {\n            short: \"m\",\n            description: `Stop reading a file after NUM matching lines. If the input is standard input from a\n                regular file, and NUM matching lines are output, grep ensures that the standard input is positioned\n                to just after the last matching line before exiting, regardless of the presence of trailing context\n                lines.`,\n        },\n        \"only-matching\": {\n            short: \"o\",\n            description: `Print only the matched (non-empty) parts of a matching line, with each such part on a\n                separate output line`,\n        },\n        \"quiet\": {\n            short: \"q\",\n            description: `Quiet; do not write anything to standard output. Exit immediately with zero status if\n                any match is found, even if an error was detected.`,\n        },\n        \"silent\": {\n            short: \"\",\n            description: `Quiet; do not write anything to standard output. Exit immediately with zero status if\n                any match is found, even if an error was detected.`,\n        },\n        \"no-messages\": {\n            short: \"s\",\n            description: `Suppress error messages about nonexistent or unreadable files`,\n        },\n        // Output Line Prefix Control\n        \"byte-offset\": {\n            short: \"b\",\n            description: `Print the byte offset within the input file before each line of output.`,\n        },\n        \"with-filename\": {\n            short: \"H\",\n            description: `Quiet; do not write anything to standard output. Exit immediately with zero status\n                if any match is found, even if an error was detected.`,\n        },\n        \"no-filename\": {\n            short: \"h\",\n            description: `Suppress the prefixing of file names on output. This is the default when there is\n                only one file (or only standard input) to search.`,\n        },\n        \"label=\": {\n            short: \"\",\n            description: `Display input actually coming from standard input as input coming from file LABEL.`,\n        },\n        \"line-number\": {\n            short: \"n\",\n            description: `Prefix each line of output with the 1-based line number within its input file.`,\n        },\n        \"initial-tab\": {\n            short: \"T\",\n            description: `Make sure that the first character of actual line content lies on a tab stop, so\n                that the alignment of tabs looks normal.`,\n        },\n        \"unix-byte-offsets\": {\n            short: \"u\",\n            description: `Report Unix-style byte offsets. This  switch causes grep to report byte offsets\n                as if the file were Unix-style text file, i.e. with CR characters stripped off`,\n        },\n        \"null\": {\n            short: \"Z\",\n            description: `Output a zero byte (the ASCII NUL character) instead of the character that normally\n                follows a file name`,\n        },\n        // Context Line Control\n        \"after-context\": {\n            short: \"A\",\n            description: `Print NUM lines of trailing context after matching lines. Places a line containing a\n                group separator (--) between contiguous groups of matches.`,\n        },\n        \"before-context\": {\n            short: \"B\",\n            description: `Print NUM lines of leading context before matching lines. Places a line containing\n                a group separator (--) between contiguous groups of matches.`,\n        },\n        \"context\": {\n            short: \"C\",\n            description: `Print NUM lines of output context. Places a line containing a group separator (--)\n                between contiguous groups of matches.`,\n        },\n        // File and Directory Selection\n        \"text\": {\n            short: \"a\",\n            description: `Process a binary file as if it were text, equivalent to --binary-files=text`,\n        },\n        \"binary-files=\": {\n            short: \"r\",\n            description: `Read all files under each directory, recursively; this is equivalent to the\n                -d recurse option`,\n        },\n        \"devices=\": {\n            short: \"D\",\n            description: `If an input file is a device, FIFO or socket, use ACTION to process  it`,\n        },\n        \"directories=\": {\n            short: \"d\",\n            description: `If an input file is a directory, use ACTION to process it. By default, ACTION is\n                read, which means that directories are read just as if they were ordinary files.`,\n        },\n        \"exclude=\": {\n            short: \"\",\n            description: `Recurse in directories skip file matching PATTERN.`,\n        },\n        \"exclude-from=\": {\n            short: \"\",\n            description: `Skip files whose base name matches any of the file-name globs read from FILE (using\n                wildcard matching as described under --exclude).`,\n        },\n        \"exclude-dir=\": {\n            short: \"\",\n            description: `Exclude directories matching the pattern DIR from recursive searches.`,\n        },\n        \"include=\": {\n            short: \"\",\n            description: `Search only files whose base name matches GLOB (using wildcard matching as described\n                under --exclude).`,\n        },\n        \"recursive\": {\n            short: \"r\",\n            description: `Read all files under each directory, recursively; this is equivalent to the -d\n                recurse option.`,\n        },\n        \"line-buffered\": {\n            short: \"\",\n            description: `Use line buffering on output. This can cause a performance penalty.`,\n        },\n        \"binary\": {\n            short: \"U\",\n            description: `Treat the file(s) as binary`,\n        },\n        \"null-data\": {\n            short: \"z\",\n            description: `Treat the input as a set of lines, each terminated by a zero byte (the ASCII NUL\n                character) instead of a newline.`,\n        },\n    },\n    (option, info) => {\n        if (info.short) {\n            return mapSuggestions(\n                longAndShortFlag(option, info.short),\n                suggestion => ({...suggestion, description: info.description}),\n            );\n        } else {\n            return mapSuggestions(\n                longFlag(option),\n                suggestion => ({...suggestion, description: info.description}),\n            );\n        }\n    },\n));\n\nconst extendedRegexOption = combine([\n    mapSuggestions(\n        longAndShortFlag(\"extended-regexp\", \"E\"),\n        suggestion => ({\n            ...suggestion,\n            description: `Interpret <pattern> (defined using --regexp=<pattern> as an extended regular expression`,\n        })),\n]);\n\nconst fixedStringsOption = combine([\n    mapSuggestions(\n        longAndShortFlag(\"fixed-strings\", \"F\"),\n        suggestion => ({\n            ...suggestion,\n            description: `Interpret <pattern> (defined using --regexp=<pattern>) as a list of fixed strings, separated by new-lines lines, any of which is to be matched`,\n        })),\n]);\n\nconst binaryFilesValues = [\n    {\n        flag: \"binary-files\",\n        displayValue: \"binary\",\n        description: `(default) Outputs either a one-line message saying that a binary file matches or\n            no message if there is no match`,\n    },\n    {\n        flag: \"binary-files\",\n        displayValue: \"without-match\",\n        description: `Assumes that a binary file does not match.`,\n    },\n    {\n        flag: \"binary-files\",\n        displayValue: \"text\",\n        description: `Processes a binary file as if it were text.`,\n    }];\n\nconst devicesValues = [\n    {\n        flag: \"devices\",\n        displayValue: \"read\",\n        description: `(default) Devices are read just as if they were ordinary files`,\n    },\n    {\n        flag: \"devices\",\n        displayValue: \"skip\",\n        description: `Devices are silently skipped`,\n    }];\n\nconst colorValues = [\n    {\n        flag: \"color\",\n        displayValue: \"never\",\n        description: `Never highlight the matching pattern`,\n    },\n    {\n        flag: \"color\",\n        displayValue: \"always\",\n        description: `Always highlight the matching pattern`,\n    },\n    {\n        flag: \"color\",\n        displayValue: \"auto\",\n        description: `Auto highlight the matching pattern`,\n    }];\n\nconst directoriesValues = [\n    {\n        flag: \"directories\",\n        displayValue: \"read\",\n        description: `(Default) Directories are read just as if they were ordinary files.`,\n    },\n    {\n        flag: \"directories\",\n        displayValue: \"skip\",\n        description: `Directories are silently skipped.`,\n    },\n    {\n        flag: \"directories\",\n        displayValue: \"recurse\",\n        description: `grep reads all files under each directory, recursively;\n            this is equivalent to the -r option.`,\n    }];\n\nconst fixedValueSuggestions = provide(async context => {\n    const token = context.argument.value;\n    let optionValues: any[] = [];\n\n    if (token.startsWith(\"--binary-files=\")) {\n        optionValues = binaryFilesValues;\n    } else if (token.startsWith(\"--devices=\")) {\n        optionValues = devicesValues;\n    } else if (token.startsWith(\"--color=\")) {\n        optionValues = colorValues;\n    } else if (token.startsWith(\"--directories=\")) {\n        optionValues = directoriesValues;\n    } else {\n        return [];\n    }\n    return optionValues.map(item => ({\n        label: \"--\" + item.flag + \"=\" + item.displayValue,\n        description: item.description,\n    }));\n});\n\nconst fileValueSuggestions = provide(async context => {\n    const tokenValue = \"--file=\";\n    const token = context.argument.value;\n\n    if (token.startsWith(tokenValue)) {\n        const workingDirectory = context.environment.pwd;\n        const optionValue = token.slice(tokenValue.length);\n        const fileSuggestions = await anyFilesSuggestions(optionValue, workingDirectory);\n        return fileSuggestions.map(item =>\n            ({label: tokenValue + item.label}));\n    } else {\n        return [];\n    }\n});\n\nconst excludeFromSuggestions = provide(async context => {\n    const tokenValue = \"--exclude-from=\";\n    const token = context.argument.value;\n\n    if (token.startsWith(tokenValue)) {\n        const workingDirectory = context.environment.pwd;\n        const optionValue = token.slice(tokenValue.length);\n        const fileSuggestions = await anyFilesSuggestions(optionValue, workingDirectory);\n        return fileSuggestions.map(item => ({\n            label: tokenValue + item.label,\n        }));\n    } else {\n        return [];\n    }\n});\n\nconst excludeDirSuggestions = provide(async context => {\n    const tokenValue = \"--exclude-dir=\";\n    const token = context.argument.value;\n\n    if (token.startsWith(tokenValue)) {\n        const workingDirectory = context.environment.pwd;\n        const optionValue = token.slice(tokenValue.length);\n        const directorySuggestions = await directoriesSuggestions(optionValue, workingDirectory);\n        return directorySuggestions.map(item =>\n            ({\n                label: tokenValue + item.label,\n            }));\n    } else {\n        return [];\n    }\n});\n\nconst commonOptions = combine([baseOptions, fixedValueSuggestions, fileValueSuggestions,\n    anyFilesSuggestionsProvider, excludeFromSuggestions, excludeDirSuggestions]);\n\nconst grepOptions = combine([commonOptions, fixedStringsOption, extendedRegexOption]);\n\nconst eGrepOptions = combine([commonOptions, extendedRegexOption]);\n\nconst fGrepOptions = combine([commonOptions, fixedStringsOption]);\n\nPluginManager.registerAutocompletionProvider(\"grep\", combine([grepOptions]));\n\nPluginManager.registerAutocompletionProvider(\"egrep\", combine([eGrepOptions]));\n\nPluginManager.registerAutocompletionProvider(\"fgrep\", combine([fGrepOptions]));\n"
  },
  {
    "path": "src/plugins/completion/History.ts",
    "content": "import {HistoryRecord} from \"../../services/HistoryService\";\nimport {scan} from \"../../shell/Scanner\";\nimport {isAbsolute} from \"path\";\nimport {services} from \"../../services/index\";\nimport {HistoryTrie} from \"../../utils/HistoryTrie\";\nimport {Suggestion} from \"../completion_utils/Common\";\n\nfunction cdIntoRelativePathFilter(record: HistoryRecord, pwd: string): boolean {\n    if (record.directory === pwd) {\n        return true;\n    }\n\n    const tokens = scan(record.expandedCommand);\n\n    if (tokens[0].value !== \"cd\") {\n        return true;\n    }\n\n    const directoryToken = tokens[1];\n\n    if (directoryToken && isAbsolute(directoryToken.value)) {\n        return true;\n    }\n\n    return false;\n}\n\nconst historyTrie = new HistoryTrie();\nservices.history.all.forEach(record => historyTrie.add(record.command));\nservices.history.onNewRecord.subscribe(record => historyTrie.add(record.command));\n\nexport function getHistorySuggestions(input: string, pwd: string): Suggestion[] {\n    const trieSuggestions = historyTrie.getContinuationsFor(input).map(continuation => ({\n        label: continuation.value,\n        description: `×${continuation.occurrences}`,\n    }));\n\n    if (trieSuggestions.length) {\n        return trieSuggestions;\n    } else {\n        return services.history.all\n            .filter(record => cdIntoRelativePathFilter(record, pwd))\n            .map(record => record.command)\n            .filter(command => command.toLowerCase().includes(input.toLowerCase()))\n            .map(command => ({\n                label: command.trim(),\n            }))\n            .reverse();\n    }\n}\n"
  },
  {
    "path": "src/plugins/completion/Ln.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {anyFilesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst combinedOptions = combine([anyFilesSuggestionsProvider, manPageOptions(\"ln\")]);\nPluginManager.registerAutocompletionProvider(\"ln\", combinedOptions);\nPluginManager.registerAutocompletionProvider(\"link\", combinedOptions);\n"
  },
  {
    "path": "src/plugins/completion/Locate.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"locate\", manPageOptions(\"locate\"));\n"
  },
  {
    "path": "src/plugins/completion/Ls.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst lsOptions = manPageOptions(\"ls\");\n\nPluginManager.registerAutocompletionProvider(\"ls\", combine([directoriesSuggestionsProvider, lsOptions]));\n"
  },
  {
    "path": "src/plugins/completion/Mkdir.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"mkdir\", combine([manPageOptions(\"mkdir\"), directoriesSuggestionsProvider]));\n"
  },
  {
    "path": "src/plugins/completion/Mv.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {anyFilesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"mv\", combine([manPageOptions(\"mv\"), anyFilesSuggestionsProvider]));\n"
  },
  {
    "path": "src/plugins/completion/NPM.ts",
    "content": "import * as Path from \"path\";\nimport {commandWithSubcommands} from \"../completion_utils/Common\";\nimport {io, mapObject} from \"../../utils/Common\";\nimport {PluginManager} from \"../../PluginManager\";\nimport {AutocompletionContext} from \"../../Interfaces\";\n\nconst npmCommandConfig = [\n    {\n        name: \"access\",\n        detail: \"Set access level on published packages\",\n    },\n    {\n        name: \"adduser\",\n        detail: \"Add a registry user account\",\n    },\n    {\n        name: \"bin\",\n        detail: \"Display npm bin folder\",\n    },\n    {\n        name: \"bugs\",\n        detail: \"Bugs for a package in a web browser maybe\",\n    },\n    {\n        name: \"build\",\n        detail: \"Build a package\",\n    },\n    {\n        name: \"bundle\",\n        detail: \"REMOVED\",\n    },\n    {\n        name: \"cache\",\n        detail: \"Manipulates packages cache\",\n    },\n    {\n        name: \"completion\",\n        detail: \"Tab Completion for npm\",\n    },\n    {\n        name: \"config\",\n        detail: \"Manage the npm configuration files\",\n    },\n    {\n        name: \"dedupe\",\n        detail: \"Reduce duplication\",\n    },\n    {\n        name: \"deprecate\",\n        detail: \"Deprecate a version of a package\",\n    },\n    {\n        name: \"dist-tag\",\n        detail: \"Modify package distribution tags\",\n    },\n    {\n        name: \"docs\",\n        detail: \"Docs for a package in a web browser maybe\",\n    },\n    {\n        name: \"edit\",\n        detail: \"Edit an installed package\",\n    },\n    {\n        name: \"explore\",\n        detail: \"Browse an installed package\",\n    },\n    {\n        name: \"help\",\n        detail: \"Get help on npm\",\n    },\n    {\n        name: \"help-search\",\n        detail: \"Search npm help documentation\",\n    },\n    {\n        name: \"init\",\n        detail: \"Interactively create a package.json file\",\n    },\n    {\n        name: \"install\",\n        detail: \"Install a package\",\n    },\n    {\n        name: \"install-test\",\n        detail: \"\",\n    },\n    {\n        name: \"link\",\n        detail: \"Symlink a package folder\",\n    },\n    {\n        name: \"logout\",\n        detail: \"Log out of the registry\",\n    },\n    {\n        name: \"ls\",\n        detail: \"List installed packages\",\n    },\n    {\n        name: \"npm\",\n        detail: \"javascript package manager\",\n    },\n    {\n        name: \"outdated\",\n        detail: \"Check for outdated packages\",\n    },\n    {\n        name: \"owner\",\n        detail: \"Manage package owners\",\n    },\n    {\n        name: \"pack\",\n        detail: \"Create a tarball from a package\",\n    },\n    {\n        name: \"ping\",\n        detail: \"Ping npm registry\",\n    },\n    {\n        name: \"prefix\",\n        detail: \"Display prefix\",\n    },\n    {\n        name: \"prune\",\n        detail: \"Remove extraneous packages\",\n    },\n    {\n        name: \"publish\",\n        detail: \"Publish a package\",\n    },\n    {\n        name: \"rebuild\",\n        detail: \"Rebuild a package\",\n    },\n    {\n        name: \"repo\",\n        detail: \"Open package repository page in the browser\",\n    },\n    {\n        name: \"restart\",\n        detail: \"Restart a package\",\n    },\n    {\n        name: \"root\",\n        detail: \"Display npm root\",\n    },\n    {\n        name: \"run\",\n        detail: \"Run arbitrary package scripts\",\n        provider: async (context: AutocompletionContext) => {\n            const packageFilePath = Path.join(context.environment.pwd, \"package.json\");\n            if (await io.fileExists(packageFilePath)) {\n                const parsed = JSON.parse(await io.readFile(packageFilePath)).scripts || {};\n                return mapObject(parsed, (key: string, value: string) => ({\n                    label: key,\n                    detail: value,\n                }));\n            } else {\n                return [];\n            }\n        },\n    },\n    {\n        name: \"search\",\n        detail: \"Search for packages\",\n    },\n    {\n        name: \"shrinkwrap\",\n        detail: \"Lock down dependency versions\",\n    },\n    {\n        name: \"star\",\n        detail: \"Mark your favorite packages\",\n    },\n    {\n        name: \"stars\",\n        detail: \"View packages marked as favorites\",\n    },\n    {\n        name: \"start\",\n        detail: \"Start a package\",\n    },\n    {\n        name: \"stop\",\n        detail: \"Stop a package\",\n    },\n    {\n        name: \"tag\",\n        detail: \"Tag a published version\",\n    },\n    {\n        name: \"team\",\n        detail: \"Manage organization teams and team memberships\",\n    },\n    {\n        name: \"test\",\n        detail: \"Test a package\",\n    },\n    {\n        name: \"uninstall\",\n        detail: \"Remove a package\",\n    },\n    {\n        name: \"unpublish\",\n        detail: \"Remove a package from the registry\",\n    },\n    {\n        name: \"update\",\n        detail: \"Update a package\",\n    },\n    {\n        name: \"version\",\n        detail: \"Bump a package version\",\n    },\n    {\n        name: \"view\",\n        detail: \"View registry info\",\n    },\n    {\n        name: \"whoami\",\n        detail: \"Display npm username\",\n    },\n];\n\nPluginManager.registerAutocompletionProvider(\"npm\", commandWithSubcommands(npmCommandConfig));\n"
  },
  {
    "path": "src/plugins/completion/Ps.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {provide, Suggestion} from \"../completion_utils/Common\";\nimport {AutocompletionContext, AutocompletionProvider} from \"../../Interfaces\";\nimport * as Process from \"../../utils/Process\";\nimport {find} from \"lodash\";\n\n// ps option suggestions based on linux  man file:\n// http://linux.die.net/man/1/ps\ninterface ShortFlagItem {\n    flag: string;\n    detail: string;\n}\n\nconst shortOptions: ShortFlagItem[] = [\n    {\n        flag: \"C\",\n        detail: `Select by command name.`,\n    },\n    {\n        flag: \"G\",\n        detail: `Select by real group ID (RGID) or name.`,\n    },\n    {\n        flag: \"U\",\n        detail: `Select by effective user ID (EUID) or name.`,\n    },\n];\n\ninterface TokenInfo {\n    params: string[];\n    start: string;\n}\n\nconst argInfo = (context: AutocompletionContext): TokenInfo => {\n    const token: string = context.argument.value;\n    const flag = token.substring(0, token.indexOf(\"=\") + 1);\n    let params: string[] = [];\n    let start = flag;\n    if (token.includes(\",\")) {\n        params = token.substring(start.length, token.lastIndexOf(\",\")).split(\",\");\n        start = token.substring(0, token.lastIndexOf(\",\") + 1);\n    }\n    return <TokenInfo>{params: params, start: start};\n};\n\ninterface LongFlagItem {\n    flag: string;\n    detail: string;\n    provider: AutocompletionProvider;\n}\n\nconst realUserSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const users = await Process.users();\n    return users\n        .filter(i => !arg.params.includes(i.ruser))\n        .map(i =>\n            ({\n                label: i.ruser, displayValue: i.ruser,\n                detail: `User '${i.ruser}' with id '${i.ruserid}'`,\n            }));\n});\n\nconst effectiveUserSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const users = await Process.users();\n    return users\n        .filter(i => !arg.params.includes(i.euser))\n        .map(i =>\n            ({\n                label: i.euser, displayValue: i.euser,\n                detail: `User '${i.euser}' with id '${i.euserid}'`,\n            }));\n});\n\nconst effectiveGroupSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const groups = await Process.groups();\n    return groups\n        .filter(i => !arg.params.includes(i.egroup))\n        .map(i =>\n            ({\n                label: i.egroup, displayValue: i.egroup,\n                detail: `Group '${i.egroup}' with id '${i.egroupid}'`,\n            }));\n});\n\nconst realGroupSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const groups = await Process.groups();\n    return groups\n        .filter(i => !arg.params.includes(i.rgroup))\n        .map(i =>\n            ({\n                label: i.rgroup, displayValue: i.rgroup,\n                detail: `Group '${i.rgroup}' with id '${i.rgroupid}'`,\n            }));\n});\n\nconst terminalSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const terminals = await Process.terminals();\n    return terminals\n        .filter(i => !arg.params.includes(i.name))\n        .map(i => ({\n            label: i.name, displayValue: i.name,\n            detail: `Terminal '${i.name}' with ruser '${i.ruser}'`,\n        }));\n});\n\nconst processSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const processes = await Process.processes();\n    return processes\n        .filter(i => !arg.params.includes(i.pid))\n        .map(i => ({\n            label: i.pid, displayValue: i.pid,\n            detail: `Process with command '${i.cmd.slice(0, 25)}'\n                     and ruser '${i.ruser}'`,\n        }));\n});\n\nconst sessionSuggestions = provide(async context => {\n    const arg = argInfo(context);\n    const sessions = await Process.sessions();\n    return sessions\n        .filter(i => !arg.params.includes(i.sid))\n        .map(i => ({\n            label: i.sid, displayValue: i.sid,\n            detail: `Session '${i.sid}' with ruser '${i.ruser} and\n                     rgroup '${i.rgroup}'`,\n        }));\n});\n\nconst longOptions: LongFlagItem[] = [\n    {\n        flag: \"user=\",\n        detail: `Select by effective user ID (EUID) or name. Identical to -u and U.`,\n        provider: effectiveUserSuggestions,\n    },\n    {\n        flag: \"User=\",\n        detail: `Select by real user ID (RUID) or name. Identical to -U.`,\n        provider: realUserSuggestions,\n    },\n    {\n        flag: \"group=\",\n        detail: `Select by effective group ID (EGID) or name.`,\n        provider: effectiveGroupSuggestions,\n    },\n    {\n        flag: \"Group=\",\n        detail: `Select by real group ID (RGID) or name. Identical to -G.`,\n        provider: realGroupSuggestions,\n    },\n    {\n        flag: \"tty=\",\n        detail: `selects the processes associated with the terminals given\n                 in ttylist. Identical to -T.`,\n        provider: terminalSuggestions,\n    },\n    {\n        flag: \"pid=\",\n        detail: `Select by process ID. Identical to -p and p.`,\n        provider: processSuggestions,\n    },\n    {\n        flag: \"sid=\",\n        detail: `Select by session ID. Identical to -s.`,\n        provider: sessionSuggestions,\n    },\n];\n\nconst psOptions = provide(async context => {\n    let suggestions: Suggestion[] = [];\n    const token: string = context.argument.value;\n\n    if (!token.includes(\"=\")) {\n        const shortOptSuggestions = shortOptions.map(s =>\n                <Suggestion>{label: \"-\" + s.flag, detail: s.detail});\n        const longOptSuggestions = longOptions.map(l =>\n                <Suggestion>{label: \"--\" + l.flag, detail: l.detail});\n        suggestions = [...shortOptSuggestions, ...longOptSuggestions];\n    }\n    return suggestions;\n});\n\nconst psLongOptionValues = provide(async context => {\n    let suggestions: Suggestion[] = [];\n    const token: string = context.argument.value;\n\n    if (token.startsWith(\"--\") && token.includes(\"=\")) {\n        const flag = token.slice(2, token.indexOf(\"=\") + 1);\n        const longOption = find(longOptions, {flag});\n        suggestions = longOption ? await longOption.provider(context) : [];\n    }\n    return suggestions;\n});\n\nPluginManager.registerAutocompletionProvider(\"ps\", combine([psOptions, psLongOptionValues]));\n"
  },
  {
    "path": "src/plugins/completion/Pwd.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"pwd\", manPageOptions(\"pwd\"));\n"
  },
  {
    "path": "src/plugins/completion/Rails.ts",
    "content": "import {commandWithSubcommands} from \"../completion_utils/Common\";\nimport {PluginManager} from \"../../PluginManager\";\n\nconst railsCommandConfig = [\n    {\n        name: \"runner\",\n        description: \"Run a piece of code in the application environment\",\n    },\n    {\n        name: \"console\",\n        description: \"Start the Rails console\",\n    },\n    {\n        name: \"server\",\n        description: \"Start the Rails server\",\n    },\n    {\n        name: \"generate\",\n        description: \"Generate new code'g')\",\n    },\n    {\n        name: \"destroy\",\n        description: \"generate\",\n    },\n    {\n        name: \"dbconsole\",\n        description: \"Start a console for the Rails database\",\n    },\n    {\n        name: \"new\",\n        description: \"Create a new Rails application\",\n    },\n    {\n        name: \"plugin new\",\n        description: \"Generates skeleton for developing a Rails plugin\",\n    },\n];\n\nPluginManager.registerAutocompletionProvider(\"rails\", commandWithSubcommands(railsCommandConfig));\n"
  },
  {
    "path": "src/plugins/completion/Rake.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst rakeOptions = manPageOptions(\"rake\", \"OPTIONS\");\n\nPluginManager.registerAutocompletionProvider(\"rake\", combine([directoriesSuggestionsProvider, rakeOptions]));\n"
  },
  {
    "path": "src/plugins/completion/Rm.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {anyFilesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"rm\", combine([manPageOptions(\"rm\"), anyFilesSuggestionsProvider]));\n"
  },
  {
    "path": "src/plugins/completion/Scp.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst scpOptions = manPageOptions(\"scp\");\n\nPluginManager.registerAutocompletionProvider(\"scp\", combine([directoriesSuggestionsProvider, scpOptions]));\n"
  },
  {
    "path": "src/plugins/completion/Shutdown.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nPluginManager.registerAutocompletionProvider(\"shutdown\", manPageOptions(\"shutdown\"));\n"
  },
  {
    "path": "src/plugins/completion/Tail.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {directoriesSuggestionsProvider} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {manPageOptions} from \"../../utils/ManPages\";\n\nconst tailOptions = manPageOptions(\"tail\");\n\nPluginManager.registerAutocompletionProvider(\"tail\", combine([directoriesSuggestionsProvider, tailOptions]));\n"
  },
  {
    "path": "src/plugins/completion/Top.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {shortFlag, mapSuggestions} from \"../completion_utils/Common\";\nimport {combine} from \"../completion_utils/Combine\";\nimport {mapObject} from \"../../utils/Common\";\n\nconst options = combine(mapObject(\n    {\n        b: {\n            short: \"Batch mode operation\",\n            long: \"Starts  top  in  ’Batch  mode’,  which could be useful for sending\\\n               output from top to other programs or to a file.  In this mode, top\\\n               will  not  accept input and runs until the iterations limit you’ve\\\n               set with the ’-n’ command-line option or until killed.\",\n        },\n\n        c: {\n            short: \"Command line/Program name toggle\",\n            long: \"Starts top with the last remembered ’c’ state reversed.  Thus,  if\\\n                top was displaying command lines, now that field will show program\\\n                names, and visa  versa.   See  the  ’c’  interactive  command  for\\\n                additional information.\",\n        },\n\n        d: {\n            short: \"Delay time interval as: -d ss.tt (seconds.tenths)\",\n            long: \"Specifies  the  delay  between  screen  updates, and overrides the\\\n                corresponding value in one’s personal configuration  file  or  the\\\n                startup  default.   Later  this can be changed with the ’d’ or ’s’\\\n                interactive commands.\\\n\\\n                Fractional seconds are honored,  but  a  negative  number  is  not\\\n                allowed.   In  all  cases, however, such changes are prohibited if\\\n                top is running in ’Secure mode’, except for root (unless  the  ’s’\\\n                command-line  option  was  used).   For  additional information on\\\n                ’Secure mode’ see topic 5a. SYSTEM Configuration File.\",\n        },\n\n        h: {\n            short: \"Help\",\n            long: \"Show library version and the usage prompt, then quit.\",\n        },\n\n        H: {\n            short: \"Threads toggle\",\n            long: \"Starts top with the last remembered ’H’ state reversed.  When this\\\n                toggle   is   On,   all  individual  threads  will  be  displayed.\\\n                Otherwise, top displays a summation of all threads in a process.\",\n        },\n\n        i: {\n            short: \"Idle Processes toggle\", long: \"Starts top with the last remembered ’i’ state reversed.  When this\\\n                toggle  is  Off,  tasks  that  are  idled  or  zombied will not be\\\n                displayed.\",\n        },\n\n        n: {\n            short: \"Number of iterations limit as: -n number\",\n            long: \"Specifies the maximum number of iterations, or frames, top  should\\\n                produce before ending.\",\n        },\n\n        u: {\n            short: \"Monitor by user as:  -u somebody\",\n            long: \"Monitor only processes with an effective UID or user name matching\\\n                that given.\",\n        },\n\n        U: {\n            short: \"Monitor by user as:  -U somebody\",\n            long: \"Monitor only processes with a  UID  or  user  name  matching  that\\\n                given.   This matches real, effective, saved, and filesystem UIDs.\",\n        },\n\n        p: {\n            short: \"Monitor PIDs as:  -pN1 -pN2 ...  or  -pN1, N2 [,...]\",\n            long: \"Monitor only processes with specified process  IDs.   This  option\\\n                can  be given up to 20 times, or you can provide a comma delimited\\\n                list  with  up  to  20  pids.   Co-mingling  both  approaches   is\\\n                permitted.\\\n\\\n                This is a command-line option only.  And should you wish to return\\\n                to normal operation, it is not necessary to quit and  and  restart\\\n                top  --  just issue the ’=’ interactive command.\",\n        },\n\n        s: {\n            short: \"Secure mode operation\",\n            long: \"Starts  top  with secure mode forced, even for root.  This mode is\\\n                far better controlled through the system configuration  file  (see\\\n                topic 5. FILES).\",\n        },\n\n        S: {\n            short: \"Cumulative time mode toggle\",\n            long: \"Starts  top  with  the  last  remembered ’S’ state reversed.  When\\\n                ’Cumulative mode’ is On, each process is listed with the cpu  time\\\n                that  it and its dead children have used.  See the ’S’ interactive\\\n                command for additional information regarding this mode.\",\n        },\n\n        v: {\n            short: \"Version\",\n            long: \"Show library version and the usage prompt, then quit.\",\n        },\n    },\n    (option, descriptions) => mapSuggestions(shortFlag(option), suggestion => ({...suggestion, detail: descriptions.short})),\n));\n\n\nPluginManager.registerAutocompletionProvider(\"top\", options);\n"
  },
  {
    "path": "src/plugins/completion/Vagrant.ts",
    "content": "import {PluginManager} from \"../../PluginManager\";\nimport {linedOutputOf} from \"../../PTY\";\nimport {commandWithSubcommands, emptyProvider, SubcommandConfig} from \"../completion_utils/Common\";\nimport {once} from \"lodash\";\nimport {homeDirectory} from \"../../utils/Common\";\n\nconst vargrantCommandConfig = once(async() => {\n    try {\n        return (await linedOutputOf(\"vagrant\", [\"list-commands\"], homeDirectory))\n            .map(line => {\n                const matches = line.match(/([\\-a-zA-Z0-9]+)  /);\n\n                if (matches) {\n                    const name = matches[1];\n                    const description = line.replace(matches[1], \"\").trim();\n\n                    return {\n                        name,\n                        description,\n                        provider: emptyProvider,\n                    };\n                }\n            })\n            .filter(suggestion => suggestion) as SubcommandConfig[];\n    } catch (e) {\n        return [] as SubcommandConfig[];\n    }\n});\n\nPluginManager.registerAutocompletionProvider(\"vagrant\", async (context) => {\n    return commandWithSubcommands(await vargrantCommandConfig())(context);\n});\n"
  },
  {
    "path": "src/plugins/completion_utils/Button.tsx",
    "content": "import * as React from \"react\";\nimport {colors} from \"../../views/css/colors\";\n\nconst buttonStyles = (color: string) => ({\n  borderColor: color,\n  borderStyle: \"solid\",\n  borderRadius: \"4px\",\n  borderWidth: \"1px\",\n  padding: \"2px\",\n  color: color,\n  WebkitUserSelect: \"none\",\n  fontSize: \"10px\",\n  margin: \"4px\",\n  cursor: \"pointer\",\n});\n\ntype ButtonProps = {\n  onClick: () => void;\n  children: string;\n  color?: string;\n};\n\nexport const Button = ({ onClick, children, color = colors.blue }: ButtonProps) => <span\n  style={buttonStyles(color)}\n  onClick={onClick}\n>{children}</span>;\n"
  },
  {
    "path": "src/plugins/completion_utils/Combine.ts",
    "content": "import * as _ from \"lodash\";\nimport {AutocompletionContext, AutocompletionProvider} from \"../../Interfaces\";\nimport {Suggestion} from \"./Common\";\n\nexport const combine = (providers: AutocompletionProvider[]): AutocompletionProvider => {\n    return async(context: AutocompletionContext): Promise<Suggestion[]> => {\n        return _.flatten(await Promise.all(providers.map(provider => provider(context))));\n    };\n};\n"
  },
  {
    "path": "src/plugins/completion_utils/Common.ts",
    "content": "import {directoryName, escapeFilePath, io, resolveDirectory, userFriendlyPath} from \"../../utils/Common\";\nimport {AutocompletionContext, AutocompletionProvider, FileInfo} from \"../../Interfaces\";\nimport {combine} from \"./Combine\";\nimport * as modeToPermissions from \"mode-to-permissions\";\nimport * as Path from \"path\";\nimport * as _ from \"lodash\";\n\n// Copy of monaco.languages.CompletionItem, but with optional `kind`.\nexport interface Suggestion {\n    /**\n     * The label of this completion item. By default\n     * this is also the text that is inserted when selecting\n     * this completion.\n     */\n    label: string;\n    /**\n     * The kind of this completion item. Based on the kind\n     * an icon is chosen by the editor.\n     */\n    kind?: monaco.languages.CompletionItemKind;\n    /**\n     * A human-readable string with additional information\n     * about this item, like type or symbol information.\n     */\n    detail?: string;\n    /**\n     * A human-readable string that represents a doc-comment.\n     */\n    documentation?: string;\n    /**\n     * A string that should be used when comparing this item\n     * with other items. When `falsy` the [label](#CompletionItem.label)\n     * is used.\n     */\n    sortText?: string;\n    /**\n     * A string that should be used when filtering a set of\n     * completion items. When `falsy` the [label](#CompletionItem.label)\n     * is used.\n     */\n    filterText?: string;\n    /**\n     * A string or snippet that should be inserted in a document when selecting\n     * this completion. When `falsy` the [label](#CompletionItem.label)\n     * is used.\n     */\n    insertText?: string | monaco.languages.SnippetString;\n    /**\n     * A range of text that should be replaced by this completion item.\n     *\n     * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the\n     * current position.\n     *\n     * *Note:* The range must be a [single line](#Range.isSingleLine) and it must\n     * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems).\n     */\n    range?: monaco.Range;\n    /**\n     * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`.\n     *\n     * ~~An [edit](#TextEdit) which is applied to a document when selecting\n     * this completion. When an edit is provided the value of\n     * [insertText](#CompletionItem.insertText) is ignored.~~\n     *\n     * ~~The [range](#Range) of the edit must be single-line and on the same\n     * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~\n     */\n    textEdit?: monaco.editor.ISingleEditOperation;\n}\n\nexport function provide(provider: AutocompletionProvider): AutocompletionProvider {\n    return provider;\n}\n\nexport const unique = (provider: AutocompletionProvider) => provide(async context => {\n    const suggestions = await provider(context);\n    return suggestions.filter(suggestion => !context.argument.command.hasArgument(suggestion.label, context.argument));\n});\n\nconst filesSuggestions = (filter: (info: FileInfo) => boolean) => async (tokenValue: string, directory: string): Promise<Suggestion[]> => {\n    const parentDirectory = userFriendlyPath(directory.replace(/\\/$/, \"\").split(Path.sep).slice(0, -1).join(Path.sep));\n    const suggestions: Suggestion[] = [];\n\n    if (_.last(tokenValue.split(Path.sep))!.startsWith(\".\")) {\n        suggestions.push({label: \"../\", detail: parentDirectory});\n    }\n\n    const tokenDirectory = directoryName(tokenValue);\n    const basePath = tokenValue.slice(tokenDirectory.length);\n    const directoryPath = resolveDirectory(directory, tokenDirectory);\n    const stats = await io.lstatsIn(directoryPath);\n\n    return stats\n        .filter(info => info.name.startsWith(\".\") ? basePath.startsWith(\".\") : true)\n        .filter(info => info.stat.isDirectory() || filter(info))\n        .map(info => {\n            const escapedName: string = escapeFilePath(info.name);\n\n            if (info.stat.isDirectory() || info.stat.isSymbolicLink()) {\n                return {label: escapedName + \"/\"};\n            } else {\n                return {label: escapedName};\n            }\n        }).concat(suggestions);\n};\n\nconst filesSuggestionsProvider =\n    (filter: (info: FileInfo) => boolean) =>\n        (context: AutocompletionContext, directory = context.environment.pwd): Promise<Suggestion[]> =>\n            filesSuggestions(filter)(context.argument.value, directory);\n\nexport const executableFilesSuggestions = filesSuggestions(info => info.stat.isFile() && modeToPermissions(info.stat.mode).execute.owner);\nexport const anyFilesSuggestions = filesSuggestions(() => true);\nexport const anyFilesSuggestionsProvider = unique(filesSuggestionsProvider(() => true));\nexport const directoriesSuggestions = filesSuggestions(info => info.stat.isDirectory() || info.stat.isSymbolicLink());\nexport const directoriesSuggestionsProvider = filesSuggestionsProvider(info => info.stat.isDirectory() || info.stat.isSymbolicLink());\n\nexport const environmentVariableSuggestions = provide(async context => {\n    if (context.argument.value.startsWith(\"$\")) {\n        return context.environment.map((key, value) =>\n            ({label: \"$\" + key, detail: value}),\n        );\n    } else {\n        return [];\n    }\n});\n\nexport function contextIndependent(provider: () => Promise<Suggestion[]>) {\n    return _.memoize(provider, () => \"\");\n}\n\nexport function staticSuggestionsProvider(suggestions: Suggestion[]) {\n    return contextIndependent(async () => suggestions);\n}\n\nexport const emptyProvider = provide(async() => []);\n\nexport const defaultAutocompletionProvider = provide(async context => {\n    if (context.argument.value.length) {\n        return combine([environmentVariableSuggestions, anyFilesSuggestionsProvider])(context);\n    } else {\n        return [];\n    }\n});\n\nexport const longAndShortFlag = (name: string, shortName = name[0]) => provide(async context => {\n    const longValue = `--${name}`;\n    const shortValue = `-${shortName}`;\n\n    if (context.argument.command.hasArgument(longValue, context.argument) || context.argument.command.hasArgument(shortValue, context.argument)) {\n        return [];\n    }\n\n    const value = context.argument.value === shortValue ? shortValue : longValue;\n\n    return [{label: value}];\n});\n\nexport const shortFlag = (char: string) => unique(async() => [{label: `-${char}`}]);\nexport const longFlag = (name: string) => unique(async() => [{label: `--${name}`}]);\n\nexport const mapSuggestions = (provider: AutocompletionProvider, mapper: (suggestion: Suggestion) => Suggestion) => provide(async context => (await provider(context)).map(mapper));\n\nexport interface SubcommandConfig {\n    name: string;\n    detail?: string;\n    provider?: AutocompletionProvider;\n}\n\nexport const commandWithSubcommands = (subCommands: SubcommandConfig[]) => {\n    return async (context: AutocompletionContext) => {\n        if (context.argument.position === 1) {\n            return subCommands.map(({ name, detail, provider }) => ({\n                label: name,\n                detail,\n                space: provider !== undefined,\n            }));\n        } else if (context.argument.position === 2) {\n            const firstArgument = context.argument.command.nthArgument(1);\n            if (firstArgument) {\n                const subCommandConfig = subCommands.find(config => config.name === firstArgument.value);\n                if (subCommandConfig && subCommandConfig.provider) {\n                    return await subCommandConfig.provider(context);\n                }\n            }\n        }\n        return [];\n    };\n};\n"
  },
  {
    "path": "src/plugins/completion_utils/Descriptions.ts",
    "content": "/* tslint:disable:max-line-length */\n\nexport const descriptions = {\n    git: {\n        subcommands: {\n            add: \"Add file contents to the index.\",\n            am: \"Apply a series of patches from a mailbox.\",\n            archive: \"Create an archive of files from a named tree.\",\n            bisect: \"Find by binary search the change that introduced a bug.\",\n            branch: \"List, create, or delete branches.\",\n            bundle: \"Move objects and refs by archive.\",\n            checkout: \"Switch branches or restore working tree files.\",\n            cherryPick: \"Apply the changes introduced by some existing commits.\",\n            citool: \"Graphical alternative to git-commit.\",\n            clean: \"Remove untracked files from the working tree.\",\n            clone: \"Clone a repository into a new directory.\",\n            commit: \"Record changes to the repository.\",\n            config: \"Get and set repository or global options\",\n            describe: \"Describe a commit using the most recent tag reachable from it.\",\n            diff: \"Show changes between commits, commit and working tree, etc.\",\n            fetch: \"Download objects and refs from another repository.\",\n            formatPatch: \"Prepare patches for e-mail submission.\",\n            gc: \"Cleanup unnecessary files and optimize the local repository.\",\n            grep: \"Print lines matching a pattern.\",\n            gui: \"A portable graphical interface to Git.\",\n            init: \"Create an empty Git repository or reinitialize an existing one.\",\n            log: \"Show commit logs.\",\n            merge: \"Join two or more development histories together.\",\n            mv: \"Move or rename a file, a directory, or a symlink.\",\n            notes: \"Add or inspect object notes.\",\n            pull: \"Fetch from and integrate with another repository or a local branch.\",\n            push: \"Update remote refs along with associated objects.\",\n            rebase: \"Forward-port local commits to the updated upstream head.\",\n            reset: \"Reset current HEAD to the specified state.\",\n            revert: \"Revert some existing commits.\",\n            rm: \"Remove files from the working tree and from the index.\",\n            shortlog: \"Summarize git log output.\",\n            show: \"Show various types of objects.\",\n            stash: \"Stash the changes in a dirty working directory away.\",\n            status: \"Show the working tree status.\",\n            submodule: \"Initialize, update or inspect submodules.\",\n            tag: \"Create, list, delete or verify a tag object signed with GPG.\",\n            worktree: \"Manage multiple worktrees.\",\n        },\n        add: {\n            patch: 'Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index. This effectively runs add --interactive, but bypasses the initial command menu and directly jumps to the patch subcommand. See \"Interactive mode\" for details.',\n            interactive: 'Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See \"Interactive mode\" for details.',\n            dryRun: \"Don't actually add the file(s), just show if they exist and/or will be ignored.\",\n            force: \"Allow adding otherwise ignored files.\",\n            edit: \"Open the diff vs. the index in an editor and let the user edit it. After the editor was closed, adjust the hunk headers and apply the patch to the index. The intent of this option is to pick and choose lines of the patch to apply, or even to modify the contents of lines to be staged. This can be quicker and more flexible than using the interactive hunk selector. However, it is easy to confuse oneself and create a patch that does not apply to the index. See EDITING PATCHES below.\",\n            update: \"Update the index just where it already has an entry matching <pathspec>. This removes as well as modifies index entries to match the working tree, but adds no new files. If no <pathspec> is given when -u option is used, all tracked files in the entire working tree are updated (old versions of Git used to limit the update to the current directory and its subdirectories).\",\n            noIgnoreRemoval: \"Update the index not only where the working tree has a file matching <pathspec> but also where the index already has an entry. This adds, modifies, and removes index entries to match the working tree. If no <pathspec> is given when -A option is used, all files in the entire working tree are updated (old versions of Git used to limit the update to the current directory and its subdirectories).\",\n            ignoreRemoval: 'Update the index by adding new files that are unknown to the index and files modified in the working tree, but ignore files that have been removed from the working tree. This option is a no-op when no <pathspec> is used. This option is primarily to help users who are used to older versions of Git, whose \"git add <pathspec>...\" was a synonym for \"git add --no-all <pathspec>...\", i.e. ignored removed files.',\n            intentToAdd: \"Record only the fact that the path will be added later. An entry for the path is placed in the index with no content. This is useful for, among other things, showing the unstaged content of such files with git diff and committing them with git commit -a.\",\n            refresh: \"Don't add the file(s), but only refresh their stat() information in the index.\",\n            ignoreErrors: \"If some files could not be added because of errors indexing them, do not abort the operation, but continue adding the others. The command shall still exit with non-zero status. The configuration variable add.ignoreErrors can be set to true to make this the default behaviour.\",\n            ignoreMissing: \"This option can only be used together with --dry-run. By using this option the user can check if any of the given files would be ignored, no matter if they are already present in the work tree or not.\",\n            chmod: \"Override the executable bit of the added files. The executable bit is only changed in the index, the files on disk are left unchanged.\",\n            separator: \"This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken for command-line options).\",\n            verbose: \"Be verbose.\",\n        },\n        checkout: {\n            branch: \"Create a new branch and start it at <start_point>; see git-branch(1) for details.\",\n        },\n        commit: {\n            message: \"Use the given <msg> as the commit message. If multiple -m options are given, their values are concatenated as separate paragraphs.\",\n            all: \"Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.\",\n            patch: \"Use the interactive patch selection interface to chose which changes to commit. See git-add(1) for details.\",\n            NULL: \"When showing short or porcelain status output, terminate entries in the status output with NULL, instead of LF. If no format is given, implies the --porcelain output format.\",\n            template: \"When editing the commit message, start the editor with the contents in the given file. The commit.template configuration variable is often used to give this option implicitly to the command. This mechanism can be used by projects that want to guide participants with some hints on what to write in the message in what order. If the user exits the editor without editing the message, the commit is aborted. This has no effect when a message is given by other means, e.g. with the -m or -F options.\",\n            signoff: \"Add Signed-off-by line by the committer at the end of the commit log message. The meaning of a signoff depends on the project, but it typically certifies that committer has the rights to submit this work under the same license and agrees to a Developer Certificate of Origin (see http://developercertificate.org/ for more information).\",\n            noVerify: \"This option bypasses the pre-commit and commit-msg hooks. See also githooks(5).\",\n            edit: \"The message taken from file with -F, command line with -m, and from commit object with -C are usually used as the commit log message unmodified. This option lets you further edit the message taken from these sources.\",\n            include: \"Before making a commit out of staged contents so far, stage the contents of paths given on the command line as well. This is usually not what you want unless you are concluding a conflicted merge.\",\n            only: \"Make a commit by taking the updated working tree contents of the paths specified on the command line, disregarding any contents that have been staged for other paths. This is the default mode of operation of git commit if any paths are given on the command line, in which case this option can be omitted. If this option is specified together with --amend, then no paths need to be specified, which can be used to amend the last commit without committing changes that have already been staged.\",\n            verbose: \"Show unified diff between the HEAD commit and what would be committed at the bottom of the commit message template to help the user describe the commit by reminding what changes the commit has. Note that this diff output doesn't have its lines prefixed with #. This diff will not be a part of the commit message. See the commit.verbose configuration variable in git-config(1). If specified twice, show in addition the unified diff between what would be committed and the worktree files, i.e. the unstaged changes to tracked files.\",\n            quiet: \"Suppress commit summary message.\",\n            resetAuthor: \"When used with -C/-c/--amend options, or when committing after a a conflicting cherry-pick, declare that the authorship of the resulting commit now belongs to the committer. This also renews the author timestamp.\",\n            short: \"When doing a dry-run, give the output in the short-format. See git-status(1) for details. Implies --dry-run.\",\n            branch: \"Show the branch and tracking info even in short-format.\",\n            porcelain: \"When doing a dry-run, give the output in a porcelain-ready format. See git-status(1) for details. Implies --dry-run.\",\n            long: \"When doing a dry-run, give the output in a the long-format. Implies --dry-run.\",\n            allowEmpty: \"Usually recording a commit that has the exact same tree as its sole parent commit is a mistake, and the command prevents you from making such a commit. This option bypasses the safety, and is primarily for use by foreign SCM interface scripts.\",\n            allowEmptyMessage: \"Like --allow-empty this command is primarily for use by foreign SCM interface scripts. It allows you to create a commit with an empty commit message without using plumbing commands like git-commit-tree(1).\",\n            noEdit: \"Use the selected commit message without launching an editor. For example, git commit --amend --no-edit amends a commit without changing its commit message.\",\n            noPostRewrite: \"Bypass the post-rewrite hook.\",\n            dryRun: \"Do not create a commit, but show a list of paths that are to be committed, paths with local changes that will be left uncommitted and paths that are untracked.\",\n            status: \"Include the output of git-status(1) in the commit message template when using an editor to prepare the commit message. Defaults to on, but can be used to override configuration variable commit.status.\",\n            noStatus: \"Do not include the output of git-status(1) in the commit message template when using an editor to prepare the default commit message.\",\n            noGpgSign: \"Countermand commit.gpgSign configuration variable that is set to force each and every commit to be signed.\",\n        },\n        status: {\n            short: \"Give the output in the short-format.\",\n            branch: \"Show the branch and tracking info even in short-format.\",\n            porcelain: \"Give the output in an easy-to-parse format for scripts. This is similar to the short output, but will remain stable across Git versions and regardless of user configuration. See below for details. The version parameter is used to specify the format version. This is optional and defaults to the original version v1 format.\",\n            long: \"Give the output in the long-format. This is the default.\",\n            verbose: \"In addition to the names of files that have been changed, also show the textual changes that are staged to be committed (i.e., like the output of git diff --cached). If -v is specified twice, then also show the changes in the working tree that have not yet been staged (i.e., like the output of git diff).\",\n            untrackedFiles: \"Show untracked files. The mode parameter is used to specify the handling of untracked files. It is optional: it defaults to all, and if specified, it must be stuck to the option (e.g.  -uno, but not -u no). The possible options are: o   no - Show no untracked files. o   normal - Shows untracked files and directories. o   all - Also shows individual files in untracked directories. When -u option is not used, untracked files and directories are shown (i.e. the same as specifying normal), to help you avoid forgetting to add newly created files. Because it takes extra work to find untracked files in the filesystem, this mode may take some time in a large working tree. Consider enabling untracked cache and split index if supported (see git update-index --untracked-cache and git update-index --split-index), Otherwise you can use no to have git status return more quickly without showing untracked files. The default can be changed using the status.showUntrackedFiles configuration variable documented in git-config(1).\",\n            ignoreSubmodules: 'Ignore changes to submodules when looking for changes. <when> can be either \"none\", \"untracked\", \"dirty\" or \"all\", which is the default. Using \"none\" will consider the submodule modified when it either contains untracked or modified files or its HEAD differs from the commit recorded in the superproject and can be used to override any settings of the ignore option in git- config(1) or gitmodules(5). When \"untracked\" is used submodules are not considered dirty when they only contain untracked content (but they are still scanned for modified content). Using \"dirty\" ignores all changes to the work tree of submodules, only changes to the commits stored in the superproject are shown (this was the behavior before 1.7.0). Using \"all\" hides all changes to submodules (and suppresses the output of submodule summaries when the config option status.submoduleSummary is set).',\n            ignored: \"Show ignored files as well.\",\n            terminateWithNull: \"Terminate entries with NUL, instead of LF. This implies the --porcelain=v1 output format if no other format is given.\",\n            column:\n                \"Display untracked files in columns. See configuration variable column.status for option syntax.--column and --no-column without options are equivalent to always and never respectively.\",\n        },\n        push: {\n            all: \"Push all branches (i.e. refs under refs/heads/); cannot be used with other <refspec>.\",\n            prune: \"Remove remote branches that don't have a local counterpart.\",\n        },\n    },\n};\n"
  },
  {
    "path": "src/references.d.ts",
    "content": "/// <reference path=\"../typings/Overrides.d.ts\" />\n/// <reference path=\"../typings/Interfaces.d.ts\" />\n\n/// <reference path=\"../typings/mode-to-permissions.d.ts\" />\n/// <reference path=\"../typings/child-process-promise.d.ts\" />\n/// <reference path=\"../typings/uuid.d.ts\" />\n/// <reference path=\"../typings/dirStat.d.ts\" />\n\n/// <reference path=\"../node_modules/immutable/dist/immutable.d.ts\" />\n"
  },
  {
    "path": "src/services/FontService.ts",
    "content": "import {Subject} from \"rxjs/Subject\";\n\nfunction getLetterSize(size: number, fontFamily: string) {\n    const height = size + 2;\n\n    if (process.env.NODE_ENV === \"test\") {\n\n        return {width: (size / 2) + 1.5, height: height};\n    } else {\n        const canvas = document.createElement(\"canvas\");\n        const context = canvas.getContext(\"2d\")!;\n        context.font = `${size}px ${fontFamily}`;\n        const metrics = context.measureText(\"m\");\n        return {width: metrics.width, height: height};\n    }\n}\n\nconst fontSize = 16;\nconst fontFamily = \"'Ubuntu Mono', 'Fira Code', 'Menlo', monospace\";\n\nexport class FontService {\n    size: number;\n    letterWidth: number;\n    letterHeight: number;\n    family: string;\n    readonly onChange = new Subject<void>();\n\n    constructor() {\n        this.updateFont(fontSize, fontFamily);\n    }\n\n    resetSize() {\n        this.updateFont(fontSize, fontFamily);\n        this.onChange.next();\n    }\n\n    increaseSize() {\n        this.updateFont(this.size + 1, fontFamily);\n        this.onChange.next();\n    }\n\n    decreaseSize() {\n        this.updateFont(Math.max(4, this.size - 1), fontFamily);\n        this.onChange.next();\n    }\n\n    private updateFont(size: number, family: string) {\n        const letterSize = getLetterSize(size, family);\n\n        this.size = size;\n        this.family = family;\n        this.letterWidth = letterSize.width;\n        this.letterHeight = letterSize.height;\n    }\n}\n"
  },
  {
    "path": "src/services/GitService.ts",
    "content": "import {Observable} from \"rxjs/Observable\";\nimport {BehaviorSubject} from \"rxjs/BehaviorSubject\";\nimport \"rxjs/add/observable/timer\";\nimport \"rxjs/add/operator/concatMap\";\nimport \"rxjs/add/operator/filter\";\nimport \"rxjs/add/operator/merge\";\nimport \"rxjs/add/operator/share\";\nimport \"rxjs/add/operator/distinctUntilChanged\";\nimport \"rxjs/add/operator/multicast\";\n\nimport {currentBranchName, GitDirectoryPath, repositoryState, RepositoryState} from \"../utils/Git\";\nimport {services} from \"./index\";\n\nconst INTERVAL = 5000;\n\nasync function getState(directory: string): Promise<GitState> {\n    const state = await repositoryState(directory);\n\n    if (state === RepositoryState.NotRepository) {\n        return {kind: \"not-repository\"};\n    } else {\n        return {\n            kind: \"repository\",\n            branch: await currentBranchName(<GitDirectoryPath>directory),\n            status: state,\n        };\n    }\n}\n\nfunction createObservable(directory: string) {\n    return Observable\n        .timer(0, INTERVAL)\n        .merge(services.jobs.onFinish.filter(job => job.session.directory === directory))\n        .concatMap(() => getState(directory))\n        // Don't emit if a value didn't change.\n        .distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y))\n        // Remember the last value to emit immediately to new subscriptions.\n        .multicast(new BehaviorSubject<GitState>({kind: \"not-repository\"}))\n        // Automatically stop checking git status when there are no subscriptions anymore.\n        .refCount();\n}\n\nexport class GitService {\n    private observables: Map<string, Observable<GitState>> = new Map();\n\n    observableFor(directory: string): Observable<GitState> {\n        if (!this.observables.has(directory)) {\n            this.observables.set(directory, createObservable(directory));\n        }\n\n        return this.observables.get(directory)!;\n    }\n}\n"
  },
  {
    "path": "src/services/HistoryService.ts",
    "content": "import {readFileSync} from \"fs\";\nimport {historyFilePath} from \"../utils/Common\";\nimport * as _ from \"lodash\";\n\nimport csvParse = require(\"csv-parse/lib/sync\");\nimport {SessionID} from \"../shell/Session\";\nimport {Subject} from \"rxjs/Subject\";\n\ninterface HistoryRecordWithoutID {\n    command: string;\n    expandedCommand: string;\n    timestamp: number;\n    directory: string;\n    sessionID: SessionID;\n}\n\nexport interface HistoryRecord extends HistoryRecordWithoutID {\n    id: number;\n}\n\nconst readHistoryFileData = (): HistoryRecord[] => {\n    try {\n        return csvParse(readFileSync(historyFilePath).toString()).map((array: string[]) => ({\n            id: Number.parseInt(array[0], 10),\n            command: array[1],\n            expandedCommand: array[2],\n            timestamp: Number.parseInt(array[3], 10),\n            directory: array[4],\n            sessionID: Number.parseInt(array[5], 10),\n        }));\n    } catch (e) {\n        return [];\n    }\n};\n\nexport class HistoryService {\n    readonly onNewRecord = new Subject<HistoryRecord>();\n    private maxRecordsCount: number = 5000;\n    private storage: HistoryRecord[] = [];\n\n    constructor() {\n        this.storage = readHistoryFileData();\n    }\n\n    get all(): HistoryRecord[] {\n        return this.storage;\n    }\n\n    get latest(): HistoryRecord | undefined {\n        return _.last(this.storage);\n    }\n\n    add(recordWithoutID: HistoryRecordWithoutID) {\n        const record = {id: this.nextID, ...recordWithoutID};\n        this.storage.push(record);\n\n        if (this.storage.length > this.maxRecordsCount) {\n            this.storage.shift();\n        }\n\n        this.onNewRecord.next(record);\n        return record;\n    }\n\n    get(id: number): HistoryRecord {\n        return this.all.find(record => record.id === id)!;\n    }\n\n    private get nextID(): number {\n        if (this.latest) {\n            return this.latest.id + 1;\n        } else {\n            return 1;\n        }\n    }\n}\n"
  },
  {
    "path": "src/services/JobsService.ts",
    "content": "import {Subject} from \"rxjs/Subject\";\nimport \"rxjs/add/observable/fromEvent\";\nimport {Job} from \"../shell/Job\";\n\n\nexport class JobsService {\n    readonly onStart = new Subject<Job>();\n    readonly onFinish = new Subject<Job>();\n}\n"
  },
  {
    "path": "src/services/SessionsService.ts",
    "content": "import {Session, SessionID} from \"../shell/Session\";\nimport {Observable} from \"rxjs/Observable\";\nimport {Subject} from \"rxjs/Subject\";\nimport \"rxjs/add/observable/fromEvent\";\nimport {services} from \"./index\";\n\n\nexport class SessionsService {\n    readonly onClose = new Subject<SessionID>();\n    private readonly sessions: Map<SessionID, Session> = new Map;\n\n    create() {\n        const session = new Session();\n        this.sessions.set(session.id, session);\n\n        Observable.fromEvent(session, \"job-started\").subscribe(\n            () => services.jobs.onStart.next(session.lastJob!),\n        );\n\n        Observable.fromEvent(session, \"job-finished\").subscribe(\n            () => services.jobs.onFinish.next(session.lastJob!),\n        );\n\n        return session.id;\n    }\n\n    get(id: SessionID) {\n        return this.sessions.get(id)!;\n    }\n\n    close(ids: SessionID | SessionID[]) {\n        if (Array.isArray(ids)) {\n            ids.forEach(id => this.close(id));\n            return;\n        }\n\n        const id = ids;\n        const session = this.get(id);\n\n        session.jobs.forEach(job => {\n            job.removeAllListeners();\n            job.interrupt();\n        });\n\n        session.removeAllListeners();\n\n        this.sessions.delete(id);\n        this.onClose.next(id);\n    }\n\n    closeAll() {\n        this.sessions.forEach((_session, id) => this.close(id));\n    }\n}\n"
  },
  {
    "path": "src/services/UpdatesService.ts",
    "content": "import {remote} from \"electron\";\nimport * as https from \"https\";\n\nexport class UpdatesService {\n    isAvailable = false;\n    private currentVersion: string;\n    private INTERVAL = 1000 * 60 * 60 * 12;\n\n    constructor() {\n        if (process.env.NODE_ENV === \"test\") {\n            return;\n        }\n\n        this.currentVersion = \"v\" + remote.app.getVersion();\n        this.checkUpdate();\n        setInterval(() => this.checkUpdate(), this.INTERVAL);\n    }\n\n    private checkUpdate() {\n        if (this.isAvailable || !navigator.onLine) {\n            return;\n        }\n\n        https.get(\n            {\n                host: \"api.github.com\",\n                path: \"/repos/railsware/upterm/releases/latest\",\n                headers: {\n                    \"User-Agent\": \"Upterm\",\n                },\n            },\n            (response) => {\n                let body = \"\";\n                response.on(\"data\", data => body += data);\n                response.on(\"end\", () => {\n                    const parsed = JSON.parse(body);\n                    this.isAvailable = parsed.tag_name !== this.currentVersion;\n                });\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "src/services/WindowService.ts",
    "content": "import {remote} from \"electron\";\nimport {Observable} from \"rxjs/Observable\";\nimport \"rxjs/add/observable/fromEvent\";\nimport \"rxjs/add/operator/map\";\nimport \"rxjs/add/operator/do\";\nimport {NeverObservable} from \"rxjs/observable/NeverObservable\";\nimport {Subject} from \"rxjs/Subject\";\n\nexport class WindowService {\n    readonly onResize: Observable<{}>;\n    readonly onClose = new Subject<{}>();\n    readonly onBoundsChange: Observable<Electron.Rectangle>;\n\n    constructor() {\n        if (remote) {\n            const electronWindow = remote.BrowserWindow.getAllWindows()[0];\n\n            this.onResize = Observable.fromEvent(electronWindow, \"resize\")\n                .merge(Observable.fromEvent(electronWindow.webContents, \"devtools-opened\"))\n                .merge(Observable.fromEvent(electronWindow.webContents, \"devtools-closed\"));\n\n            this.onBoundsChange = Observable.fromEvent(electronWindow, \"move\")\n                .merge(Observable.fromEvent(electronWindow, \"resize\"))\n                .map(() => electronWindow.getBounds());\n\n            window.onbeforeunload = () => {\n                electronWindow\n                    .removeAllListeners()\n                    .webContents\n                    .removeAllListeners(\"devtools-opened\")\n                    .removeAllListeners(\"devtools-closed\")\n                    .removeAllListeners(\"found-in-page\");\n\n                this.onClose.next();\n            };\n        } else {\n            this.onResize = new NeverObservable();\n            this.onBoundsChange = new NeverObservable();\n        }\n    }\n}\n"
  },
  {
    "path": "src/services/index.ts",
    "content": "import {FontService} from \"./FontService\";\nimport {HistoryService} from \"./HistoryService\";\nimport {UpdatesService} from \"./UpdatesService\";\nimport {GitService} from \"./GitService\";\nimport {SessionsService} from \"./SessionsService\";\nimport {WindowService} from \"./WindowService\";\nimport {JobsService} from \"./JobsService\";\n\n// To help IDE with \"find usages\" and \"go to definition\".\ninterface Services {\n    font: FontService;\n    history: HistoryService;\n    updates: UpdatesService;\n    git: GitService;\n    sessions: SessionsService;\n    jobs: JobsService;\n    window: WindowService;\n}\n\nexport const services: Services = {\n    font: new FontService(),\n    history: new HistoryService(),\n    updates: new UpdatesService,\n    git: new GitService(),\n    sessions: new SessionsService(),\n    jobs: new JobsService(),\n    window: new WindowService(),\n};\n"
  },
  {
    "path": "src/shell/Aliases.ts",
    "content": "import {loginShell} from \"../utils/Shell\";\nimport * as _ from \"lodash\";\n\nexport const aliasesFromConfig: Dictionary<string> = {};\n\nexport async function loadAliasesFromConfig(): Promise<void> {\n    const lines = await loginShell.loadAliases();\n    lines.map(parseAlias).forEach(parsed => aliasesFromConfig[parsed.name] = parsed.value);\n}\n\nexport function parseAlias(line: string) {\n    let [short, long] = line.split(\"=\");\n\n    if (short && long) {\n        const nameCapture = /(alias )?(.*)/.exec(short);\n        const valueCapture = /'?([^']*)'?/.exec(long);\n\n        if (nameCapture && valueCapture) {\n            return {\n                name: nameCapture[2],\n                value: valueCapture[1],\n            };\n        } else {\n            throw `Alias line is incorrect: ${line}`;\n        }\n    } else {\n        throw `Can't parse alias line: ${line}`;\n    }\n}\n\n\nexport class Aliases {\n    private storage: Dictionary<string>;\n\n    constructor(aliases: Dictionary<string>) {\n        this.storage = _.clone(aliases);\n    }\n\n    add(name: string, value: string) {\n        this.storage[name] = value;\n    }\n\n    has(name: string): name is ExistingAlias {\n        return name in this.storage;\n    }\n\n    get(name: ExistingAlias): string {\n        return this.storage[name];\n    }\n\n    getNameByValue(value: string): string | undefined {\n        return _.findKey(this.storage, storageValue => storageValue === value);\n    }\n\n    remove(name: string) {\n        delete this.storage[name];\n    }\n\n    toObject(): Dictionary<string> {\n        return this.storage;\n    }\n}\n"
  },
  {
    "path": "src/shell/BuiltInCommands.ts",
    "content": "import {Job} from \"./Job\";\nimport {existsSync, statSync} from \"fs\";\nimport {homeDirectory, pluralize, resolveDirectory, resolveFile, mapObject} from \"../utils/Common\";\nimport {readFileSync} from \"fs\";\nimport {EOL} from \"os\";\nimport {Session} from \"./Session\";\nimport {OrderedSet} from \"../utils/OrderedSet\";\nimport {parseAlias} from \"./Aliases\";\nimport {stringLiteralValue} from \"./Scanner\";\n\nconst executors: Dictionary<(i: Job, a: string[]) => void> = {\n    cd: (job: Job, args: string[]): void => {\n        let fullPath: string;\n\n        if (!args.length) {\n            fullPath = homeDirectory;\n        } else {\n            const enteredPath = args[0];\n\n            if (isHistoricalDirectory(enteredPath)) {\n                fullPath = expandHistoricalDirectory(enteredPath, job.session.historicalPresentDirectoriesStack);\n            } else {\n                fullPath = job.environment.cdpath\n                    .map(path => resolveDirectory(path, enteredPath))\n                    .filter(resolved => existsSync(resolved))\n                    .filter(resolved => statSync(resolved).isDirectory())[0];\n\n                if (!fullPath) {\n                    throw new Error(`The directory \"${enteredPath}\" doesn't exist.`);\n                }\n            }\n        }\n\n        job.session.directory = fullPath;\n    },\n    clear: (job: Job, _args: string[]): void => {\n        setTimeout(() => job.session.clearJobs(), 0);\n    },\n    exit: (job: Job, _args: string[]): void => {\n        job.session.close();\n    },\n    export: (job: Job, args: string[]): void => {\n        if (args.length === 0) {\n            job.output.write(job.environment.map((key, value) => `${key}=${value}`).join(\"\\r\\n\"));\n        } else {\n            args.forEach(argument => {\n                const firstEqualIndex = argument.indexOf(\"=\");\n                const key = argument.slice(0, firstEqualIndex);\n                const value = argument.slice(firstEqualIndex + 1);\n\n                job.session.environment.set(key, value);\n            });\n        }\n    },\n    // FIXME: make the implementation more reliable.\n    source: (job: Job, args: string[]): void => {\n        sourceFile(job.session, args[0]);\n    },\n    alias: (job: Job, args: string[]): void => {\n        if (args.length === 0) {\n            job.output.write(mapObject(job.session.aliases.toObject(), (key, value) => `${key}=${value}`).join(\"\\r\\n\"));\n        } else if (args.length === 1) {\n            const parsed = parseAlias(args[0]);\n            job.session.aliases.add(parsed.name, parsed.value);\n        } else {\n            throw `Don't know what to do with ${args.length} arguments.`;\n        }\n    },\n    unalias: (job: Job, args: string[]): void => {\n        if (args.length === 1) {\n            const name = args[0];\n\n            if (job.session.aliases.has(name)) {\n                job.session.aliases.remove(args[0]);\n            } else {\n                throw `There is such alias: ${name}.`;\n            }\n        } else {\n            throw `Don't know what to do with ${args.length} arguments.`;\n        }\n    },\n    show: (job: Job, args: string[]): void => {\n        const imgs = args.map(argument => resolveFile(job.environment.pwd, argument));\n        job.output.write(imgs.join(EOL));\n    },\n};\n\nexport function sourceFile(session: Session, fileName: string) {\n    const content = readFileSync(resolveFile(session.directory, fileName)).toString();\n\n    content.split(EOL).forEach(line => {\n        if (line.startsWith(\"export \")) {\n            const [variableName, variableValueLiteral] = line.split(\" \")[1].split(\"=\");\n\n            const variableValue = stringLiteralValue(variableValueLiteral);\n            if (variableValue) {\n                session.environment.set(variableName, variableValue);\n            }\n        }\n    });\n}\n\n// A class representing built in commands\nexport class Command {\n    static allCommandNames = Object.keys(executors);\n\n    static executor(command: string): (i: Job, args: string[]) => void {\n        return executors[command];\n    }\n\n    static isBuiltIn(command: string): boolean {\n        return this.allCommandNames.includes(command);\n    }\n}\n\nexport function expandHistoricalDirectory(alias: string, historicalDirectories: OrderedSet<string>): string {\n    if (alias === \"-\") {\n        alias = \"-1\";\n    }\n    const index = historicalDirectories.size + parseInt(alias, 10);\n\n    if (index < 0) {\n        throw new Error(`Error: you only have ${historicalDirectories.size} ${pluralize(\"directory\", historicalDirectories.size)} in the stack.`);\n    } else {\n        const directory = historicalDirectories.at(index);\n\n        if (directory) {\n            return directory;\n        } else {\n            throw `No directory with index ${index}`;\n        }\n    }\n}\n\nexport function isHistoricalDirectory(directory: string): boolean {\n    return /^-\\d*$/.test(directory);\n}\n"
  },
  {
    "path": "src/shell/CommandExecutor.ts",
    "content": "import {Job} from \"./Job\";\nimport {Command} from \"./BuiltInCommands\";\nimport {PTY} from \"../PTY\";\nimport * as Path from \"path\";\nimport {resolveFile, isWindows, filterAsync, io} from \"../utils/Common\";\nimport {loginShell} from \"../utils/Shell\";\n\nexport class NonZeroExitCodeError extends Error {\n}\n\nabstract class CommandExecutionStrategy {\n    static async canExecute(_job: Job): Promise<boolean> {\n        return false;\n    }\n\n    constructor(protected job: Job) {\n    }\n\n    abstract startExecution(): Promise<{}>;\n}\n\nclass BuiltInCommandExecutionStrategy extends CommandExecutionStrategy {\n    static async canExecute(job: Job) {\n        return Command.isBuiltIn(job.prompt.commandName);\n    }\n\n    startExecution() {\n        return new Promise((resolve, reject) => {\n            try {\n                Command.executor(this.job.prompt.commandName)(this.job, this.job.prompt.arguments.map(token => token.value));\n                resolve();\n            } catch (error) {\n                reject(error.message);\n            }\n        });\n    }\n}\n\nclass ShellExecutionStrategy extends CommandExecutionStrategy {\n    static async canExecute(job: Job) {\n        return loginShell.preCommandModifiers.includes(job.prompt.commandName) ||\n            await this.isExecutableFromPath(job) ||\n            await this.isPathOfExecutable(job) ||\n            this.isBashFunc(job);\n    }\n\n    private static isBashFunc(job: Job): boolean {\n        return job.environment.has(`BASH_FUNC_${job.prompt.commandName}%%`);\n    }\n\n    private static async isExecutableFromPath(job: Job): Promise<boolean> {\n        return (await io.executablesInPaths(job.environment.path)).includes(job.prompt.commandName);\n    }\n\n    private static async isPathOfExecutable(job: Job): Promise<boolean> {\n        return await io.fileExists(resolveFile(job.session.directory, job.prompt.commandName));\n    }\n\n    startExecution() {\n        return new Promise((resolve, reject) => {\n            this.job.setPty(new PTY(\n                this.job.prompt.expandedTokens.map(token => token.escapedValue),\n                this.job.environment.toObject(),\n                this.job.session.dimensions,\n                (data: string) => this.job.output.write(data),\n                (exitCode: number) => exitCode === 0 ? resolve() : reject(new NonZeroExitCodeError(exitCode.toString())),\n            ));\n        });\n    }\n}\n\nclass WindowsShellExecutionStrategy extends CommandExecutionStrategy {\n    static async canExecute(_job: Job) {\n        return isWindows;\n    }\n\n    startExecution() {\n        return new Promise((resolve) => {\n            this.job.setPty(new PTY(\n                [\n                    this.cmdPath,\n                    \"/s\" as EscapedShellWord,\n                    \"/c\" as EscapedShellWord,\n                    ...this.job.prompt.expandedTokens.map(token => token.escapedValue),\n                ],\n                this.job.environment.toObject(), this.job.session.dimensions,\n                (data: string) => this.job.output.write(data),\n                (_exitCode: number) => resolve(),\n            ));\n        });\n    }\n\n    private get cmdPath(): EscapedShellWord {\n        if (this.job.environment.has(\"comspec\")) {\n            return this.job.environment.get(\"comspec\") as EscapedShellWord;\n        } else if (this.job.environment.has(\"SystemRoot\")) {\n            return Path.join(this.job.environment.get(\"SystemRoot\"), \"System32\", \"cmd.exe\") as EscapedShellWord;\n        } else {\n            return \"cmd.exe\" as EscapedShellWord;\n        }\n    }\n}\n\nexport class CommandExecutor {\n    private static executors = [\n        BuiltInCommandExecutionStrategy,\n        WindowsShellExecutionStrategy,\n        ShellExecutionStrategy,\n    ];\n\n    static async execute(job: Job): Promise<{}> {\n        const applicableExecutors = await filterAsync(this.executors, executor => executor.canExecute(job));\n\n        if (applicableExecutors.length) {\n            return new applicableExecutors[0](job).startExecution();\n        } else {\n            throw `Upterm: command \"${job.prompt.commandName}\" not found.\\n`;\n        }\n    }\n}\n"
  },
  {
    "path": "src/shell/Environment.ts",
    "content": "import {delimiter} from \"path\";\nimport * as _ from \"lodash\";\nimport {executeCommandWithShellConfig} from \"../PTY\";\nimport {clone} from \"lodash\";\nimport {homeDirectory, resolveDirectory} from \"../utils/Common\";\nimport * as Path from \"path\";\nimport {AbstractOrderedSet} from \"../utils/OrderedSet\";\nimport {loginShell} from \"../utils/Shell\";\n\nconst ignoredEnvironmentVariables = [\n    \"NODE_ENV\",\n];\n\nconst isIgnoredEnvironmentVariable = (varName: string) => {\n    if (ignoredEnvironmentVariables.includes(varName)) {\n        return true;\n    } else {\n        return false;\n    }\n};\n\nexport const preprocessEnv = (lines: string[]) => {\n    // Bash functions in the env have newlines in them, which need to be removed\n    const joinedFunctionLines: string[] = [];\n    for (let i = 0; i < lines.length; i++) {\n        if (/^BASH_FUNC\\w+%%/.test(lines[i])) {\n            const finalLineOfFunction = lines.indexOf(\"}\", i);\n            joinedFunctionLines.push(lines.slice(i, finalLineOfFunction + 1).join(\"\\n\"));\n            i = finalLineOfFunction;\n        } else {\n            joinedFunctionLines.push(lines[i]);\n        }\n    }\n    return joinedFunctionLines;\n};\n\nexport const processEnvironment: Dictionary<string> = {};\nexport async function loadEnvironment(): Promise<void> {\n    const lines = preprocessEnv(await executeCommandWithShellConfig(loginShell.environmentCommand));\n\n    lines.forEach(line => {\n        const [key, ...valueComponents] = line.trim().split(\"=\");\n        const value = valueComponents.join(\"=\");\n\n        if (!isIgnoredEnvironmentVariable(key)) {\n            processEnvironment[key] = value;\n        }\n    });\n}\n\nexport class Environment {\n    private storage: Dictionary<string>;\n\n    constructor(environment: Dictionary<string>) {\n        this.storage = clone(environment);\n    }\n\n    set(key: string, value: string): void {\n        this.storage[key] = value;\n    }\n\n    setMany(pairs: Dictionary<string>): void {\n        for (const key of Object.keys(pairs)) {\n            this.set(key, pairs[key]);\n        }\n    }\n\n    toObject(): ProcessEnvironment {\n        return <ProcessEnvironment>this.storage;\n    }\n\n    map<R>(mapper: (key: string, value: string) => R): Array<R> {\n        const result: Array<R> = [];\n\n        for (const key of Object.keys(this.storage)) {\n            result.push(mapper(key, this.storage[key]));\n        }\n\n        return result;\n    }\n\n    get(key: string): string {\n        return this.storage[key];\n    }\n\n    has(key: string): boolean {\n        return key in this.storage;\n    }\n\n    get path(): EnvironmentPath {\n        return new EnvironmentPath(this);\n    }\n\n    get cdpath(): FullPath[] {\n        return _.uniq((this.get(\"CDPATH\") || \".\").split(delimiter).map(path => path || this.pwd).map(path => resolveDirectory(this.pwd, path)));\n    }\n\n    get pwd(): string {\n        if (!this.get(\"PWD\")) {\n            this.pwd = homeDirectory;\n        }\n\n        return this.get(\"PWD\");\n    }\n\n    set pwd(value: string) {\n        this.set(\"PWD\", value);\n    }\n}\n\nexport class EnvironmentPath extends AbstractOrderedSet<string> {\n    constructor(private environment: Environment) {\n        super(\n            () => {\n                const path = this.environment.get(\"PATH\");\n\n                if (path) {\n                    return path.split(Path.delimiter);\n                } else {\n                    return [];\n                }\n            },\n            updatedPaths => this.environment.set(\"PATH\", updatedPaths.join(Path.delimiter)),\n        );\n    }\n}\n"
  },
  {
    "path": "src/shell/Job.ts",
    "content": "import * as _ from \"lodash\";\nimport * as i from \"../Interfaces\";\nimport * as React from \"react\";\nimport {Session} from \"./Session\";\nimport {Prompt} from \"./Prompt\";\nimport {Output} from \"../Output\";\nimport {CommandExecutor, NonZeroExitCodeError} from \"./CommandExecutor\";\nimport {PTY} from \"../PTY\";\nimport {PluginManager} from \"../PluginManager\";\nimport {EmitterWithUniqueID} from \"../EmitterWithUniqueID\";\nimport {Status} from \"../Enums\";\nimport {Environment} from \"./Environment\";\nimport {normalizeProcessInput} from \"../utils/Common\";\nimport {TerminalLikeDevice} from \"../Interfaces\";\n\nexport class Job extends EmitterWithUniqueID implements TerminalLikeDevice {\n    public status: Status = Status.InProgress;\n    readonly startTime = Date.now();\n    private readonly _output: Output;\n    private readonly throttledDataEmitter = _.throttle(() => this.emit(\"data\"), 1000 / 60);\n    private pty: PTY | undefined;\n\n    constructor(private _session: Session, private _prompt: Prompt) {\n        super();\n        this._output = new Output(this, this._session.dimensions);\n        this._output.on(\"data\", this.throttledDataEmitter);\n    }\n\n    async execute(): Promise<void> {\n        try {\n            await CommandExecutor.execute(this);\n\n            // Need to wipe out PTY so that we\n            // don't keep trying to write to it.\n            this.pty = undefined;\n\n            // Need to check the status here because it's\n            // executed even after the process was interrupted.\n            if (this.status === Status.InProgress) {\n                this.setStatus(Status.Success);\n            }\n        } catch (exception) {\n            this.handleError(exception);\n        } finally {\n            this.emit(\"end\");\n        }\n    }\n\n    handleError(message: NonZeroExitCodeError | string): void {\n        this.setStatus(Status.Failed);\n        if (message) {\n            if (message instanceof NonZeroExitCodeError) {\n                // Do nothing.\n            } else {\n                this._output.write(message);\n            }\n        }\n        this.emit(\"end\");\n    }\n\n    isRunningPty(): boolean {\n        return this.pty !== undefined;\n    }\n\n    setPty(pty: PTY) {\n        this.pty = pty;\n    }\n\n    // Writes to the process' STDIN.\n    write(input: string | KeyboardEvent) {\n        this.pty!.write(normalizeProcessInput(input, this.output.isCursorKeysModeSet));\n    }\n\n    get session(): Session {\n        return this._session;\n    }\n\n    hasOutput(): boolean {\n        return !this._output.isEmpty();\n    }\n\n    interrupt(): void {\n        if (this.pty && this.status === Status.InProgress) {\n            this.pty.kill(\"SIGINT\");\n            this.setStatus(Status.Failed);\n            this.emit(\"end\");\n        }\n    }\n\n    resize(): void {\n        if (this.pty && this.status === Status.InProgress) {\n            this.pty.resize(this.session.dimensions);\n            this.output.dimensions = this.session.dimensions;\n        }\n    }\n\n    canBePrettified(): boolean {\n        return this.status !== Status.InProgress && !!this.firstApplicablePrettyfier;\n    }\n\n    prettify(): React.ReactElement<any> {\n        if (this.firstApplicablePrettyfier) {\n            return this.firstApplicablePrettyfier.prettify(this);\n        } else {\n            throw \"No applicable prettyfier found.\";\n        }\n    }\n\n    get environment(): Environment {\n        return this.session.environment;\n    }\n\n    private get firstApplicablePrettyfier(): i.Prettyfier | undefined {\n        return PluginManager.prettyfiers.find(prettyfier => prettyfier.isApplicable(this));\n    }\n\n    get output(): Output {\n        return this._output;\n    }\n\n    get prompt(): Prompt {\n        return this._prompt;\n    }\n\n    setStatus(status: Status): void {\n        this.status = status;\n        this.emit(\"status\", status);\n    }\n}\n"
  },
  {
    "path": "src/shell/Parser.ts",
    "content": "import * as Scanner from \"./Scanner\";\nimport * as _ from \"lodash\";\nimport {memoizeAccessor} from \"../Decorators\";\nimport {commandDescriptions} from \"../plugins/completion/Executable\";\nimport {io, mapObject} from \"../utils/Common\";\nimport {loginShell} from \"../utils/Shell\";\nimport {PreliminaryAutocompletionContext} from \"../Interfaces\";\nimport {PluginManager} from \"../PluginManager\";\nimport {Aliases} from \"./Aliases\";\nimport {combine} from \"../plugins/completion_utils/Combine\";\nimport {\n    anyFilesSuggestions,\n    environmentVariableSuggestions,\n    executableFilesSuggestions, Suggestion,\n} from \"../plugins/completion_utils/Common\";\n\n\nexport abstract class ASTNode {\n    abstract get fullStart(): number;\n\n    abstract get fullEnd(): number;\n}\n\nconst isEndOfCommand = (token: Scanner.Token) => token instanceof Scanner.Semicolon || token instanceof Scanner.NewLine;\n\nabstract class LeafNode extends ASTNode {\n    constructor(private token: Scanner.Token) {\n        super();\n    }\n\n    get fullStart(): number {\n        return this.token.fullStart;\n    }\n\n    get fullEnd(): number {\n        return this.fullStart + this.token.raw.length;\n    }\n\n    get precedingSpaces(): string {\n        const match = this.token.raw.match(/^(\\s*)/);\n        if (match) {\n            return match[1];\n        } else {\n            return \"\";\n        }\n    }\n\n    get followingSpaces(): string {\n        // Consists only of spaces. They're considered preceding.\n        if (this.token.raw.match(/^(\\s*)$/)) {\n            return \"\";\n        }\n\n        const match = this.token.raw.match(/(\\s*)$/);\n        if (match) {\n            return match[1];\n        } else {\n            return \"\";\n        }\n    }\n\n    get raw(): string {\n        return this.token.raw;\n    }\n\n    get value(): string {\n        return this.token.value;\n    }\n\n    abstract suggestions(context: PreliminaryAutocompletionContext): Promise<Suggestion[]>;\n}\n\nabstract class BranchNode extends ASTNode {\n    readonly tokens: Scanner.Token[];\n    abstract get children(): ASTNode[];\n\n    constructor(tokens: Scanner.Token[]) {\n        super();\n        this.tokens = tokens;\n    }\n\n    get fullStart(): number {\n        return this.children[0].fullStart;\n    }\n\n    get fullEnd(): number {\n        return _.last(this.children)!.fullEnd;\n    }\n}\n\n/**\n * The whole command, the input string.\n */\nexport class CompleteCommand extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        const lastChild = _.last(this.tokens)!;\n        const endsWithSeparator = isEndOfCommand(lastChild);\n\n        if (endsWithSeparator) {\n            return [\n                new List(this.tokens.slice(0, -1)),\n                new ShellSyntaxNode(lastChild),\n            ];\n        } else {\n            return [\n                new List(this.tokens),\n            ];\n        }\n    }\n\n    get firstCommand(): Command {\n        for (const current of traverse(this)) {\n            if (current instanceof Command) {\n                return current;\n            }\n        }\n        return undefined as any as Command;\n    }\n}\n\nclass List extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        const separatorOpIndex = _.findLastIndex(this.tokens, isEndOfCommand);\n\n        if (separatorOpIndex !== -1) {\n            return [\n                new List(this.tokens.slice(0, separatorOpIndex)),\n                new ShellSyntaxNode(this.tokens[separatorOpIndex]),\n                new AndOr(this.tokens.slice(separatorOpIndex + 1)),\n            ];\n        } else {\n            return [\n                new AndOr(this.tokens),\n            ];\n        }\n    }\n}\n\nclass AndOr extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        const andOrTokenIndex = _.findLastIndex(this.tokens, token => token instanceof Scanner.And || token instanceof Scanner.Or);\n\n        if (andOrTokenIndex !== -1) {\n            return [\n                new AndOr(this.tokens.slice(0, andOrTokenIndex)),\n                new ShellSyntaxNode(this.tokens[andOrTokenIndex]),\n                new Pipeline(this.tokens.slice(andOrTokenIndex + 1)),\n            ];\n        } else {\n            return [\n                new Pipeline(this.tokens),\n            ];\n        }\n    }\n}\n\nclass Pipeline extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        return [new PipeSequence(this.tokens)];\n    }\n}\n\nclass PipeSequence extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        const pipeIndex = _.findLastIndex(this.tokens, token => token instanceof Scanner.Pipe);\n\n        if (pipeIndex !== -1) {\n            return [\n                new PipeSequence(this.tokens.slice(0, pipeIndex)),\n                new ShellSyntaxNode(this.tokens[pipeIndex]),\n                new Command(this.tokens.slice(pipeIndex + 1)),\n            ];\n        } else {\n            return [\n                new Command(this.tokens),\n            ];\n        }\n    }\n}\n\nclass Command extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        if (!this.tokens.length) {\n            return [new EmptyNode()];\n        }\n\n        const children: ASTNode[] = [];\n\n        if (this.parameterAssignments) {\n            children.push(this.parameterAssignments);\n        }\n\n        if (this.commandWord) {\n            children.push(this.commandWord);\n        }\n\n        if (this.argumentList) {\n            children.push(this.argumentList);\n        }\n\n        if (this.ioRedirect) {\n            children.push(this.ioRedirect);\n        }\n\n        return children;\n    }\n\n    @memoizeAccessor\n    get commandWord(): CommandWord | undefined {\n        if (this.categorizedTokens.commandWord) {\n            return new CommandWord(this.categorizedTokens.commandWord);\n        }\n    }\n\n    @memoizeAccessor\n    get parameterAssignments(): ParameterAssignmentList | undefined {\n        if (this.categorizedTokens.parameterAssignment.length) {\n            return new ParameterAssignmentList(this.categorizedTokens.parameterAssignment);\n        }\n    }\n\n    @memoizeAccessor\n    get argumentList(): ArgumentList | undefined {\n        if (this.categorizedTokens.argumentList.length) {\n            return new ArgumentList(this.categorizedTokens.argumentList, this);\n        }\n    }\n\n    nthArgument(position: OneBasedPosition): Argument | undefined {\n        if (this.argumentList) {\n            return this.argumentList.arguments[position - 1];\n        }\n    }\n\n    hasArgument(value: string, currentArgument: Argument): boolean {\n        if (this.argumentList) {\n            return this.argumentList.arguments.filter(argument => argument !== currentArgument).map(argument => argument.value).includes(value);\n        } else {\n            return false;\n        }\n    }\n\n    @memoizeAccessor\n    private get ioRedirect(): IORedirect | undefined {\n        if (this.categorizedTokens.ioRedirect.length) {\n            return new IORedirect(this.categorizedTokens.ioRedirect);\n        }\n    }\n\n    @memoizeAccessor\n    private get categorizedTokens() {\n        /**\n         * @link http://stackoverflow.com/a/10939280/1149074\n         */\n        const parameterAssignmentTokens = _.takeWhile(this.tokens, token => token.value.includes(\"=\"));\n\n        const commandWordToken = this.tokens[parameterAssignmentTokens.length];\n\n        const beforeArgumentListTokensCount = parameterAssignmentTokens.length + 1;\n        const argumentListTokens = _.takeWhile(this.tokens.slice(beforeArgumentListTokensCount), token => !(\n            token instanceof Scanner.InputRedirectionSymbol ||\n            token instanceof Scanner.OutputRedirectionSymbol ||\n            token instanceof Scanner.AppendingOutputRedirectionSymbol\n        ));\n\n        const ioRedirectTokens = this.tokens.slice(beforeArgumentListTokensCount + argumentListTokens.length);\n\n        return {\n            parameterAssignment: parameterAssignmentTokens,\n            commandWord: commandWordToken,\n            argumentList: argumentListTokens,\n            ioRedirect: ioRedirectTokens,\n        };\n    }\n}\n\nclass IORedirect extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        if (this.tokens.length === 1) {\n            return [\n                new ShellSyntaxNode(this.tokens[0]),\n                new EmptyNode(),\n            ];\n        } else {\n            return [\n                new ShellSyntaxNode(this.tokens[0]),\n                new IOFile(this.tokens[1]),\n                ...this.tokens.slice(2).map(token => new UnknownNode(token)),\n            ];\n        }\n    }\n}\n\nclass IOFile extends LeafNode {\n    suggestions(context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        return anyFilesSuggestions(this.value, context.environment.pwd);\n    }\n}\n\nclass ShellSyntaxNode extends LeafNode {\n    async suggestions(_context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        return [];\n    }\n}\n\nclass ParameterAssignmentList extends BranchNode {\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        return this.tokens.map(token => new ParameterAssignment(token));\n    }\n}\n\nexport class ParameterAssignment extends LeafNode {\n    async suggestions(_context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        return [];\n    }\n}\n\nexport class CommandWord extends LeafNode {\n    async suggestions({\n        environment,\n        aliases,\n    }: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        if (this.value.length === 0) {\n            return [];\n        }\n\n        const relativeExecutablesSuggestions = await executableFilesSuggestions(this.value, environment.pwd);\n        const executables = await io.executablesInPaths(environment.path);\n\n        return [\n            ...mapObject(aliases.toObject(), (key, value) => ({label: key, detail: value})),\n            ...loginShell.preCommandModifiers.map(modifier => ({label: modifier})),\n            ...executables.map(name => ({label: name, detail: commandDescriptions[name] || \"\"})),\n            ...relativeExecutablesSuggestions,\n        ];\n    }\n}\n\nclass ArgumentList extends BranchNode {\n    constructor(childTokens: Scanner.Token[], private command: Command) {\n        super(childTokens);\n    }\n\n    @memoizeAccessor\n    get children(): ASTNode[] {\n        return this.arguments;\n    }\n\n    @memoizeAccessor\n    get arguments(): Argument[] {\n        return this.tokens.map((token, index) => new Argument(token, this.command, index + 1));\n    }\n}\n\nexport class Argument extends LeafNode {\n    readonly position: number;\n    readonly command: Command;\n\n    constructor(token: Scanner.Token, command: Command, position: number) {\n        super(token);\n        this.command = command;\n        this.position = position;\n    }\n\n    async suggestions(context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        const argument = argumentOfExpandedAST(this, context.aliases);\n        const provider = combine([\n            environmentVariableSuggestions,\n            PluginManager.autocompletionProviderFor(argument.command.commandWord!.value),\n        ]);\n\n        return provider({...context, argument: argument});\n    }\n}\n\n// FIXME: find a better way to search for the argument in the new tree.\nfunction argumentOfExpandedAST(argument: Argument, aliases: Aliases) {\n    const commandWord = argument.command.commandWord!;\n\n    if (aliases.has(commandWord.value)) {\n        const tree = new CompleteCommand(Scanner.scan(serializeReplacing(argument.command, commandWord, aliases.get(commandWord.value))));\n\n        for (const current of traverse(tree)) {\n            if (current instanceof Argument && current.value === argument.value) {\n                return current;\n            }\n        }\n\n        throw \"Couldn't find the argument.\";\n    } else {\n        return argument;\n    }\n}\n\nexport class UnknownNode extends LeafNode {\n    async suggestions(_context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        return [];\n    }\n}\n\nexport class EmptyNode extends LeafNode {\n    constructor() {\n        super(new Scanner.Empty());\n    }\n\n    // FIXME: a workaround for leafNodeAt to parse, say, `ls |` correctly.\n    get fullEnd() {\n        return Number.MAX_SAFE_INTEGER;\n    }\n\n    async suggestions(_context: PreliminaryAutocompletionContext): Promise<Suggestion[]> {\n        return [];\n    }\n}\n\nexport function leafNodeAt(position: number, node: ASTNode): LeafNode {\n    if (node instanceof LeafNode) {\n        return node;\n    } else if (node instanceof BranchNode) {\n        const childAt = node.children.find(child => child.fullStart <= position && child.fullEnd >= position);\n\n        if (childAt) {\n            return leafNodeAt(position, childAt);\n        } else {\n            throw `Couldn't find a child at position ${position}`;\n        }\n    } else {\n        throw \"Should never happen\";\n    }\n}\n\nfunction *traverse(node: ASTNode): Iterable<ASTNode> {\n    yield node;\n\n    if (node instanceof BranchNode) {\n        for (const child of node.children) {\n            yield * traverse(child);\n        }\n    }\n}\n\nexport function serializeReplacing(tree: ASTNode, focused: LeafNode, replacement: string) {\n    let serialized = \"\";\n\n    for (const current of traverse(tree)) {\n        if (current instanceof LeafNode) {\n            if (current === focused) {\n                serialized += focused.precedingSpaces + replacement + focused.followingSpaces;\n            } else {\n                serialized += current.raw;\n            }\n        }\n    }\n\n    return serialized;\n}\n"
  },
  {
    "path": "src/shell/Prompt.ts",
    "content": "import * as events from \"events\";\nimport {scan, Token, expandAliases} from \"./Scanner\";\nimport {CompleteCommand} from \"./Parser\";\nimport {Session} from \"./Session\";\n\nexport class Prompt extends events.EventEmitter {\n    private _value = \"\";\n    private _expandedAst: CompleteCommand;\n\n    constructor(private session: Session) {\n        super();\n    }\n\n    get value(): string {\n        return this._value;\n    }\n\n    setValue(value: string): void {\n        this._value = value;\n\n        const tokens = scan(this.value);\n        this._expandedAst = new CompleteCommand(expandAliases(tokens, this.session.aliases));\n    }\n\n    get expandedTokens(): Token[] {\n        return this._expandedAst.tokens;\n    }\n\n    get commandName(): string {\n        if (!this._expandedAst || !this._expandedAst.firstCommand.commandWord) {\n            return \"\";\n        }\n        return this._expandedAst.firstCommand.commandWord.value;\n    }\n\n    get arguments(): Token[] {\n        const argumentList = this._expandedAst.firstCommand.argumentList;\n\n        if (argumentList) {\n            return argumentList.tokens;\n        } else {\n            return [];\n        }\n    }\n}\n"
  },
  {
    "path": "src/shell/Scanner.ts",
    "content": "import {Aliases} from \"./Aliases\";\n\nexport abstract class Token {\n    readonly raw: string;\n    readonly fullStart: number;\n\n    constructor(raw: string, fullStart: number) {\n        this.raw = raw;\n        this.fullStart = fullStart;\n    }\n\n    abstract get value(): string;\n\n    /**\n     * @deprecated\n     */\n    abstract get escapedValue(): EscapedShellWord;\n}\n\nexport class Empty extends Token {\n    constructor() {\n        super(\"\", 0);\n    }\n\n    get value() {\n        return \"\";\n    }\n\n    get escapedValue() {\n        return this.raw.trim() as EscapedShellWord;\n    }\n}\n\nexport abstract class StringLiteral extends Token {\n    get value() {\n        return this.raw.trim().slice(1, -1);\n    }\n}\n\nexport class Word extends StringLiteral {\n    get value() {\n        return this.raw.trim().replace(/\\\\\\s/g, \" \");\n    }\n\n    get escapedValue() {\n        return this.raw.trim() as EscapedShellWord;\n    }\n}\n\nexport class Pipe extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class Semicolon extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class And extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class Or extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class InputRedirectionSymbol extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class OutputRedirectionSymbol extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class AppendingOutputRedirectionSymbol extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class CompositeStringLiteral extends StringLiteral {\n    private tokens: Token[];\n\n    constructor(tokens: Token[]) {\n        let raw = tokens.map(token => token.raw).join(\"\");\n        super(raw, tokens[0].fullStart);\n        this.tokens = tokens;\n    }\n\n    get value() {\n        return this.tokens.map(token => token.value).join(\"\");\n    }\n\n    get escapedValue() {\n        return this.tokens.map(token => token.escapedValue).join(\"\") as EscapedShellWord;\n    }\n}\n\nexport class SingleQuotedStringLiteral extends StringLiteral {\n    get escapedValue(): EscapedShellWord {\n        return `'${this.value}'` as EscapedShellWord;\n    }\n}\n\nexport class DoubleQuotedStringLiteral extends StringLiteral {\n    get escapedValue(): EscapedShellWord {\n        return `\"${this.value}\"` as EscapedShellWord;\n    }\n}\n\nexport class NewLine extends Token {\n    get value() {\n        return this.raw;\n    }\n\n    get escapedValue() {\n        return this.value as EscapedShellWord;\n    }\n}\n\nexport class Invalid extends Token {\n    get value() {\n        return this.raw.trim();\n    }\n\n    get escapedValue(): EscapedShellWord {\n        return this.value as EscapedShellWord;\n    }\n}\n\n// All these regex start ^ so that we can look only at the first token. Maybe that should\n// be part of the scanner so that the regex can be just for the token\nconst patterns = [\n    {\n        regularExpression: /^(\\n)/,\n        tokenConstructor: NewLine,\n    },\n    {\n        regularExpression: /^(\\s*\\|\\s*)/,\n        tokenConstructor: Pipe,\n    },\n    {\n        regularExpression: /^(\\s*;)/,\n        tokenConstructor: Semicolon,\n    },\n    {\n        regularExpression: /^(\\s*&&)/,\n        tokenConstructor: And,\n    },\n    {\n        regularExpression: /^(\\s*\\|\\|)/,\n        tokenConstructor: Or,\n    },\n    {\n        regularExpression: /^(\\s*>>)/,\n        tokenConstructor: AppendingOutputRedirectionSymbol,\n    },\n    {\n        regularExpression: /^(\\s*<)/,\n        tokenConstructor: InputRedirectionSymbol,\n    },\n    {\n        regularExpression: /^(\\s*[012]?>)/,\n        tokenConstructor: OutputRedirectionSymbol,\n    },\n    {\n        regularExpression: /^(\\s*\"(?:\\\\\"|[^\"])*\")/,\n        tokenConstructor: DoubleQuotedStringLiteral,\n    },\n    {\n        regularExpression: /^(\\s*'(?:\\\\'|[^'])*')/,\n        tokenConstructor : SingleQuotedStringLiteral,\n    },\n    {\n        regularExpression: /^(\\s*(?:\\\\\\(|\\\\\\)|\\\\\\s|[a-zA-Z0-9\\u0080-\\uFFFF+~!@#%^&*_=,.:/?\\\\-])+)/,\n        tokenConstructor : Word,\n    },\n];\n\nexport function scan(input: string): Token[] {\n    const tokens: Token[] = [];\n\n    let position = 0;\n\n    while (true) {\n        if (input.length === 0) {\n            return squashLiterals(tokens);\n        }\n\n        let foundMatch = false;\n        for (const pattern of patterns) {\n            const match = input.match(pattern.regularExpression);\n\n            if (match) {\n                const token = match[1];\n                tokens.push(new pattern.tokenConstructor(token, position));\n                position += token.length;\n                input = input.slice(token.length);\n                foundMatch = true;\n                break;\n            }\n        }\n\n        if (!foundMatch) {\n            tokens.push(new Invalid(input, position));\n            return squashLiterals(tokens);\n        }\n    }\n}\n\n// Find sequences of literals with no spaces between them and squash:\n// they should be one token.\nfunction squashLiterals(tokens: Token[]): Token[] {\n    let result: Token[] = [];\n    let i: number = 0;\n    while (i < tokens.length) {\n        let currentComposite: Token[] = [];\n        while (i < tokens.length - 1 && shouldSquash(tokens[i], tokens[i + 1])) {\n            currentComposite.push(tokens[i++]);\n        }\n        if (currentComposite.length > 0) {\n            currentComposite.push(tokens[i++]); // last token in the sequence\n            result.push(new CompositeStringLiteral(currentComposite));\n        }\n        if (i < tokens.length) {\n            result.push(tokens[i++]);\n        }\n    }\n    return result;\n}\n\nfunction concatTokens(left: Token[], right: Token[]): Token[] {\n    return left.concat(right);\n}\n\nfunction shouldSquash(left: Token, right: Token): boolean {\n    return left instanceof StringLiteral\n            && right instanceof StringLiteral\n            && !/\\s$/.test(left.raw) // ends with spaces\n            && !/^\\s/.test(right.raw); // starts with spaces\n}\n\nexport function expandAliases(tokens: Token[], aliases: Aliases): Token[] {\n    if (tokens.length === 0) {\n        return [];\n    }\n\n    const commandWordToken = tokens[0];\n    const argumentTokens = tokens.slice(1);\n\n    if (aliases.has(commandWordToken.value)) {\n        const alias = aliases.get(commandWordToken.value);\n        const aliasTokens = scan(alias);\n        const isRecursive = aliasTokens[0].value === commandWordToken.value;\n\n        if (isRecursive) {\n            return concatTokens(aliasTokens, argumentTokens);\n        } else {\n            return concatTokens(expandAliases(scan(alias), aliases), argumentTokens);\n        }\n    } else {\n        return tokens;\n    }\n}\n\nexport function stringLiteralValue(literal: string): string | undefined {\n    const tokens = scan(literal);\n\n    if (tokens.length !== 1) {\n        return;\n    }\n\n    const token = tokens[0];\n\n    if (token instanceof DoubleQuotedStringLiteral) {\n        return token.value;\n    }\n\n    if (token instanceof SingleQuotedStringLiteral) {\n        return token.value;\n    }\n\n    if (token instanceof Word) {\n        return token.value;\n    }\n}\n"
  },
  {
    "path": "src/shell/Session.ts",
    "content": "import {readFileSync} from \"fs\";\nimport {Job} from \"./Job\";\nimport * as events from \"events\";\nimport {PluginManager} from \"../PluginManager\";\nimport {Environment, processEnvironment} from \"./Environment\";\nimport {\n    homeDirectory, normalizeDirectory,\n    presentWorkingDirectoryFilePath,\n} from \"../utils/Common\";\nimport {OrderedSet} from \"../utils/OrderedSet\";\nimport {Aliases, aliasesFromConfig} from \"./Aliases\";\nimport * as _ from \"lodash\";\nimport {Prompt} from \"./Prompt\";\nimport {services} from \"../services/index\";\n\nexport type SessionID = number & {__isSessionID: true};\n\nexport class Session extends events.EventEmitter {\n    readonly id: SessionID = <SessionID>Date.now();\n    jobs: Array<Job> = [];\n    readonly environment = new Environment(processEnvironment);\n    readonly aliases = new Aliases(aliasesFromConfig);\n    historicalPresentDirectoriesStack = new OrderedSet<string>();\n\n    constructor(private _dimensions: Dimensions = {columns: 80, rows: 25}) {\n        super();\n\n        // TODO: We want to deserialize properties only for the first instance\n        // TODO: of Session for the application.\n        this.deserialize();\n    }\n\n    createJob(prompt: Prompt): void {\n        const job = new Job(this, prompt);\n        job.execute();\n\n        job.once(\"end\", () => {\n            this.emit(\"job-finished\");\n            this.emit(\"jobs-changed\");\n        });\n\n        this.jobs.push(job);\n\n        this.emit(\"job-started\");\n        this.emit(\"jobs-changed\");\n    }\n\n    get dimensions(): Dimensions {\n        return this._dimensions;\n    }\n\n    set dimensions(value: Dimensions) {\n        this._dimensions = value;\n        this.jobs.forEach(job => job.resize());\n    }\n\n    get lastJob(): Job | undefined {\n        return _.last(this.jobs);\n    }\n\n    clearJobs(): void {\n        this.jobs = [];\n        this.emit(\"jobs-changed\");\n    }\n\n    close(): void {\n        services.sessions.close(this.id);\n    }\n\n    get directory(): string {\n        return this.environment.pwd;\n    }\n\n    set directory(value: string) {\n        let normalizedDirectory = normalizeDirectory(value);\n        if (normalizedDirectory === this.directory) {\n            return;\n        }\n\n        PluginManager.environmentObservers.forEach(observer =>\n            observer.presentWorkingDirectoryWillChange(this, normalizedDirectory),\n        );\n\n        this.environment.pwd = normalizedDirectory;\n        this.historicalPresentDirectoriesStack.prepend(normalizedDirectory);\n\n        PluginManager.environmentObservers.forEach(observer =>\n            observer.presentWorkingDirectoryDidChange(this, normalizedDirectory),\n        );\n    }\n\n    private deserialize(): void {\n        this.directory = this.readSerialized(presentWorkingDirectoryFilePath, homeDirectory);\n    }\n\n    private readSerialized<T>(file: string, defaultValue: T): T {\n        try {\n            return JSON.parse(readFileSync(file).toString());\n        } catch (error) {\n            return defaultValue;\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/Common.ts",
    "content": "import * as walk from \"klaw\";\nimport * as Path from \"path\";\nimport * as i from \"./../Interfaces\";\nimport * as e from \"./../Enums\";\nimport * as _ from \"lodash\";\nimport * as fs from \"fs-extra\";\nimport {KeyCode} from \"./../Enums\";\nimport {EnvironmentPath} from \"../shell/Environment\";\n\ninterface FSExtraWalkObject {\n    path: string;\n    stats: fs.Stats;\n}\n\nexport function info(...args: any[]): void {\n    print(e.LogLevel.Info, args);\n}\n\nexport function log(...args: any[]): void {\n    print(e.LogLevel.Log, args);\n}\n\nexport function error(...args: any[]): void {\n    print(e.LogLevel.Error, args);\n}\n\nexport function print(level: e.LogLevel, args: Array<any>): void {\n    if ((typeof window !== \"undefined\") && window.DEBUG) {\n        (<Function>(<any>console)[level])(...args);\n    }\n}\n\nexport function times(n: number, action: Function): void {\n    for (let i = 0; i !== n; ++i) {\n        action();\n    }\n}\n\nexport const io = {\n    filesIn: async (directoryPath: FullPath): Promise<string[]> => {\n        if (await io.directoryExists(directoryPath)) {\n            return await fs.readdir(directoryPath);\n        } else {\n            return [];\n        }\n    },\n    recursiveFilesIn: (directoryPath: string): Promise<string[]> => {\n        let files: string[] = [];\n\n        return new Promise(resolve =>\n            walk(directoryPath)\n                .on(\"data\", (file: FSExtraWalkObject) => file.stats.isFile() && files.push(file.path))\n                .on(\"end\", () => resolve(files)),\n        );\n    },\n    lstatsIn: async (directoryPath: FullPath): Promise<i.FileInfo[]> => {\n        return Promise.all((await io.filesIn(directoryPath)).map(async (fileName) => {\n            return {name: fileName, stat: await fs.lstat(Path.join(directoryPath, fileName))};\n        }));\n    },\n    fileExists: async (filePath: string): Promise<boolean> => {\n        if (!await fs.pathExists(filePath)) {\n            return false;\n        }\n\n        const stat = await fs.lstat(filePath);\n\n        if (stat.isFile()) {\n            return true;\n        }\n\n        if (stat.isSymbolicLink()) {\n            const realPath = await io.realPath(filePath);\n            return io.fileExists(realPath);\n        }\n\n        return false;\n    },\n    directoryExists: async (directoryPath: string): Promise<boolean> => {\n        if (await fs.pathExists(directoryPath)) {\n            return (await fs.lstat(directoryPath)).isDirectory();\n        } else {\n            return false;\n        }\n    },\n    readFile: async (filePath: string) => (await fs.readFile(filePath)).toString(),\n    executablesInPaths: async (path: EnvironmentPath): Promise<string[]> => {\n        const allFiles: string[][] = await Promise.all(path.toArray().map(io.filesIn));\n        return _.uniq(_.flatten(allFiles));\n    },\n    realPath: fs.realpath,\n};\n\n/**\n * Unlike Path.join, doesn't remove ./ and ../ parts.\n */\nexport function joinPath(...parts: string[]) {\n    const initialParts = parts.slice(0, -1).filter(part => part.length);\n    const lastPart = parts[parts.length - 1];\n\n    return initialParts.map(normalizeDirectory).join(\"\") + lastPart;\n}\n\nexport function normalizeDirectory(directoryPath: string): string {\n    if (directoryPath.endsWith(Path.sep)) {\n        return directoryPath;\n    } else {\n        return directoryPath + Path.sep;\n    }\n}\n\nexport function directoryName(path: string): string {\n    const directoryParts = path.split(Path.sep).slice(0, -1);\n\n    if (directoryParts.length === 0) {\n        return \"\";\n    } else {\n        return normalizeDirectory(directoryParts.join(Path.sep));\n    }\n}\n\nexport const isWindows = process.platform === \"win32\";\nexport const homeDirectory = process.env[(isWindows) ? \"USERPROFILE\" : \"HOME\"]!;\n\nexport function resolveDirectory(pwd: string, directory: string): FullPath {\n    return <FullPath>normalizeDirectory(resolveFile(pwd, directory));\n}\n\nexport function resolveFile(pwd: string, file: string): FullPath {\n    return <FullPath>Path.resolve(pwd, file.replace(/^~/, homeDirectory));\n}\n\nexport function userFriendlyPath(path: string): string {\n    return path.replace(homeDirectory, \"~\");\n}\n\nexport async function filterAsync<T>(values: T[], asyncPredicate: (t: T) => Promise<boolean>): Promise<T[]> {\n    const filtered = await Promise.all(values.map(asyncPredicate));\n    return values.filter((_value: T, index: number) => filtered[index]);\n}\n\nexport async function reduceAsync<A, E>(array: E[], initial: A, callback: (a: A, e: E) => Promise<A>): Promise<A> {\n    let accumulator = initial;\n\n    for (const element of array) {\n        accumulator = await callback(accumulator, element);\n    }\n\n    return accumulator;\n}\n\nexport function pluralize(word: string, count = 2) {\n    return count === 1 ? word : pluralFormOf(word);\n}\n\nconst imageExtensions = [\n    \".jpg\",\n    \".jpeg\",\n    \".png\",\n    \".gif\",\n];\n\nexport function isImage(extension: string) {\n    return imageExtensions.includes(extension);\n}\n\nfunction pluralFormOf(word: string) {\n    if (word.endsWith(\"y\")) {\n        return word.substring(0, word.length - 1) + \"ies\";\n    } else {\n        return word + \"s\";\n    }\n}\n\nexport function groupWhen<T>(grouper: (a: T, b: T) => boolean, row: T[]): T[][] {\n    if (row.length === 0) return [];\n    if (row.length === 1) return [row];\n\n    const result: T[][] = [];\n    const firstValue = row[0];\n    let currentGroup: T[] = [firstValue];\n    let previousValue: T = firstValue;\n\n    row.slice(1).forEach(currentValue => {\n        if (grouper(currentValue, previousValue)) {\n            currentGroup.push(currentValue);\n        } else {\n            result.push(currentGroup);\n            currentGroup = [currentValue];\n        }\n\n        previousValue = currentValue;\n    });\n    result.push(currentGroup);\n\n    return result;\n}\n\nexport function csi(char: string) {\n    return `\\x1b[${char}`;\n}\n\nexport function ss3(char: string) {\n    return `\\x1bO${char}`;\n}\n\nexport function normalizeProcessInput(input: string | KeyboardEvent, isCursorKeysModeSet: boolean): string {\n    let text: string;\n\n    if (typeof input === \"string\") {\n        text = input;\n    } else {\n        if (input.ctrlKey) {\n            /**\n             * @link https://unix.stackexchange.com/a/158298/201739\n             */\n            text = String.fromCharCode(input.key.toUpperCase().charCodeAt(0) - 64);\n        } else if (input.altKey) {\n            /**\n             * The alt key can mean two things:\n             *   - send an escape character before special keys such as cursor-keys, or\n             *   - act as an extended shift, allowing you to enter codes for Latin-1 values from 160 to 255.\n             *\n             * We currently don't support the second one since it's less frequently used.\n             * For future reference, the correct extended code would be keyCode + 160.\n             * @link http://invisible-island.net/ncurses/ncurses.faq.html#bash_meta_mode\n             */\n            let char = String.fromCharCode(input.keyCode);\n            if (input.shiftKey) {\n                char = char.toUpperCase();\n            } else {\n                char = char.toLowerCase();\n            }\n            text = `\\x1b${char}`;\n        } else {\n            text = normalizeKey(input.key, isCursorKeysModeSet);\n        }\n    }\n\n    return text;\n}\n\n/**\n * @link https://www.w3.org/TR/uievents/#widl-KeyboardEvent-key\n */\nfunction normalizeKey(key: string, isCursorKeysModeSet: boolean): string {\n    switch (key) {\n        case \"Backspace\":\n            return String.fromCharCode(127);\n        case \"Delete\":\n            /**\n             * @link http://www.macfreek.nl/memory/Backspace_and_Delete_key_reversed\n             */\n            return csi(\"3~\");\n        case \"Tab\":\n            return String.fromCharCode(KeyCode.Tab);\n        case \"Enter\":\n            return String.fromCharCode(KeyCode.CarriageReturn);\n        case \"Escape\":\n            return String.fromCharCode(KeyCode.Escape);\n        case \"ArrowLeft\":\n            return isCursorKeysModeSet ? ss3(\"D\") : csi(\"D\");\n        case \"ArrowUp\":\n            return isCursorKeysModeSet ? ss3(\"A\") : csi(\"A\");\n        case \"ArrowRight\":\n            return isCursorKeysModeSet ? ss3(\"C\") : csi(\"C\");\n        case \"ArrowDown\":\n            return isCursorKeysModeSet ? ss3(\"B\") : csi(\"B\");\n        case \"F1\":\n            return ss3(\"P\");\n        case \"F2\":\n            return ss3(\"Q\");\n        case \"F3\":\n            return ss3(\"R\");\n        case \"F4\":\n            return ss3(\"S\");\n        case \"F5\":\n            return csi(\"15~\");\n        case \"F6\":\n            return csi(\"17~\");\n        case \"F7\":\n            return csi(\"18~\");\n        case \"F8\":\n            return csi(\"19~\");\n        case \"F9\":\n            return csi(\"20~\");\n        case \"F10\":\n            return csi(\"21~\");\n        case \"F11\":\n            return csi(\"23~\");\n        case \"F12\":\n            return csi(\"24~\");\n        default:\n            return key;\n    }\n}\n\nexport function commonPrefix(left: string, right: string) {\n    let i = 0;\n\n    while (i < left.length && left.charAt(i) === right.charAt(i)) {\n        ++i;\n    }\n    return left.substring(0, i);\n}\n\nexport function mapObject<T, R>(object: Dictionary<T>, mapper: (key: string, value: T) => R): R[] {\n    const result: R[] = [];\n\n    for (const key of Object.keys(object)) {\n        result.push(mapper(key, object[key]));\n    }\n\n    return result;\n}\n\nexport function escapeFilePath(unescaped: string): string {\n    return unescaped.replace(/([\\s'\"\\[\\]<>#$%^&*()])/g, \"\\\\$1\");\n}\n\nconst baseConfigDirectory = Path.join(homeDirectory, \".upterm\");\nexport const presentWorkingDirectoryFilePath = Path.join(baseConfigDirectory, \"presentWorkingDirectory\");\nexport const historyFilePath = Path.join(baseConfigDirectory, \"history.csv\");\nexport const windowBoundsFilePath = Path.join(baseConfigDirectory, \"windowBounds\");\n\nexport function fuzzyMatch(input: string, candidate: string): boolean {\n    function tokenize(string: string) {\n        return string.split(/-|_|:|\\//);\n    }\n\n    const lowerCasedInput = input.toLowerCase();\n\n    // A user wants to match by an exact prefix.\n    if (candidate.toLowerCase().startsWith(lowerCasedInput)) {\n        return true;\n    }\n\n    // A user wants to match by a part of the word, e.g. chr for google-chrome.\n    if (tokenize(candidate).find(token => token.toLowerCase().startsWith(lowerCasedInput))) {\n        return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "src/utils/Git.ts",
    "content": "import {linedOutputOf} from \"../PTY\";\nimport * as Path from \"path\";\nimport * as fs from \"fs\";\nimport {executeCommand} from \"../PTY\";\nimport * as _ from \"lodash\";\nimport * as ChildProcess from \"child_process\";\n\nexport class Branch {\n    constructor(private refName: string, private _isCurrent: boolean) {\n    }\n\n    toString(): string {\n        return this.refName;\n    }\n\n    isCurrent(): boolean {\n        return this._isCurrent;\n    }\n}\n\nexport interface ConfigVariable {\n    name: string;\n    value: string;\n}\n\nexport type StatusCode =\n    \"Unmodified\" |\n\n    \"UnstagedModified\" |\n    \"UnstagedDeleted\" |\n    \"StagedModified\" |\n    \"StagedModifiedUnstagedModified\" |\n    \"StagedModifiedUnstagedDeleted\" |\n    \"StagedAdded\" |\n    \"StagedAddedUnstagedModified\" |\n    \"StagedAddedUnstagedDeleted\" |\n    \"StagedDeleted\" |\n    \"StagedDeletedUnstagedModified\" |\n    \"StagedRenamed\" |\n    \"StagedRenamedUnstagedModified\" |\n    \"StagedRenamedUnstagedDeleted\" |\n    \"StagedCopied\" |\n    \"StagedCopiedUnstagedModified\" |\n    \"StagedCopiedUnstagedDeleted\" |\n\n    \"UnmergedBothDeleted\" |\n    \"UnmergedAddedByUs\" |\n    \"UnmergedDeletedByThem\" |\n    \"UnmergedAddedByThem\" |\n    \"UnmergedDeletedByUs\" |\n    \"UnmergedBothAdded\" |\n    \"UnmergedBothModified\" |\n\n    \"Untracked\" |\n    \"Ignored\" |\n\n    \"Invalid\"\n;\n\nfunction lettersToStatusCode(letters: string): StatusCode {\n    switch (letters) {\n        case \"  \": return \"Unmodified\";\n\n        case \" M\": return \"UnstagedModified\";\n        case \" D\": return \"UnstagedDeleted\";\n        case \"M \": return \"StagedModified\";\n        case \"MM\": return \"StagedModifiedUnstagedModified\";\n        case \"MD\": return \"StagedModifiedUnstagedDeleted\";\n        case \"A \": return \"StagedAdded\";\n        case \"AM\": return \"StagedAddedUnstagedModified\";\n        case \"AD\": return \"StagedAddedUnstagedDeleted\";\n        case \"D \": return \"StagedDeleted\";\n        case \"DM\": return \"StagedDeletedUnstagedModified\";\n        case \"R \": return \"StagedRenamed\";\n        case \"RM\": return \"StagedRenamedUnstagedModified\";\n        case \"RD\": return \"StagedRenamedUnstagedDeleted\";\n        case \"C \": return \"StagedCopied\";\n        case \"CM\": return \"StagedCopiedUnstagedModified\";\n        case \"CD\": return \"StagedCopiedUnstagedDeleted\";\n\n        case \"DD\": return \"UnmergedBothDeleted\";\n        case \"AU\": return \"UnmergedAddedByUs\";\n        case \"UD\": return \"UnmergedDeletedByThem\";\n        case \"UA\": return \"UnmergedAddedByThem\";\n        case \"DU\": return \"UnmergedDeletedByUs\";\n        case \"AA\": return \"UnmergedBothAdded\";\n        case \"UU\": return \"UnmergedBothModified\";\n\n        case \"??\": return \"Untracked\";\n        case \"!!\": return \"Ignored\";\n\n        default: return \"Invalid\";\n    }\n}\n\nexport class FileStatus {\n    constructor(private _line: string) {\n    }\n\n    get value(): string {\n        return this._line.slice(3).trim();\n    }\n\n    get code(): StatusCode {\n        return lettersToStatusCode(this._line.slice(0, 2));\n    }\n}\n\nexport type GitDirectoryPath = string & { __isGitDirectoryPath: boolean };\n\nexport function isGitDirectory(directory: string): directory is GitDirectoryPath {\n    return fs.existsSync(Path.join(directory, \".git\") );\n}\n\ntype BranchesOptions = {\n    directory: GitDirectoryPath;\n    remotes: boolean;\n    tags: boolean;\n};\n\nexport async function currentBranchName(directory: GitDirectoryPath): Promise<string> {\n    try {\n        const output = await executeCommand(\n            \"git\",\n            ['\"symbolic-ref\"', '\"--short\"', '\"-q\"', '\"HEAD\"'],\n            directory,\n        );\n\n        return output.trim();\n    } catch (error) {\n        // Doesn't have a name.\n        const output = await executeCommand(\n            \"git\",\n            ['\"rev-parse\"', '\"--short\"', '\"HEAD\"'],\n            directory,\n        );\n\n        return output.trim();\n    }\n}\n\nexport enum RepositoryState {\n    Clean = \"clean\",\n    Dirty = \"dirty\",\n    NotRepository = \"not-repository\",\n}\n\n/**\n * @link https://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommited-changes\n */\nexport async function repositoryState(directory: string): Promise<RepositoryState> {\n    return new Promise<RepositoryState>((resolve, reject) => {\n        const process = ChildProcess.spawn(\n            \"git\",\n            [\"diff-index\", \"--quiet\", \"HEAD\", \"--\"],\n            {cwd: directory},\n        );\n\n        process.once(\"exit\", code => {\n            switch (code) {\n                case 0:\n                    resolve(RepositoryState.Clean);\n                    break;\n                case 1:\n                    resolve(RepositoryState.Dirty);\n                    break;\n                case 128:\n                    resolve(RepositoryState.NotRepository);\n                    break;\n                default:\n                    reject(`Unknown Git code: ${code}.`);\n            }\n        });\n    });\n}\n\nexport async function branches({\n    directory,\n    remotes,\n    tags,\n}: BranchesOptions): Promise<Branch[]> {\n    const currentBranch = await currentBranchName(directory);\n    const promiseHeadsTags = linedOutputOf(\n        \"git\",\n        [\"for-each-ref\", \"refs/heads \", tags ? \"refs/tags \" : \"\",\n        \"--format='%(refname:strip=2)'\"],\n        directory,\n    );\n    const promiseRemotes = linedOutputOf(\n        \"git\",\n        [\"for-each-ref\", \"refs/remotes\",\n        \"--format='%(refname:strip=3)'\"],\n        directory,\n    );\n    let promiseBranches = [promiseHeadsTags];\n    if (remotes)\n        promiseBranches.push(promiseRemotes);\n    const allBranches = _.flatten(await Promise.all(promiseBranches));\n    return allBranches.map(branch => {\n        return new Branch(branch, branch === currentBranch);\n    });\n}\n\nexport async function configVariables(directory: string): Promise<ConfigVariable[]> {\n    const lines = await linedOutputOf(\n        \"git\",\n        [\"config\", \"--list\"],\n        directory,\n    );\n\n    return lines.map(line => {\n        const parts = line.split(\"=\");\n\n        return {\n            name: parts[0].trim(),\n            value: parts[1] ? parts[1].trim() : \"\",\n        };\n    });\n}\n\nexport async function aliases(directory: string): Promise<ConfigVariable[]> {\n    const variables = await configVariables(directory);\n\n    return variables\n        .filter(variable => variable.name.indexOf(\"alias.\") === 0)\n        .map(variable => {\n            return {\n                name: variable.name.replace(\"alias.\", \"\"),\n                value: variable.value,\n            };\n        });\n}\n\nexport async function remotes(directory: GitDirectoryPath): Promise<string[]> {\n    return await linedOutputOf(\"git\", [\"remote\"], directory);\n}\n\nexport async function status(directory: GitDirectoryPath): Promise<FileStatus[]> {\n    let lines = await linedOutputOf(\"git\", [\"status\", \"--porcelain\"], directory);\n    return lines.map(line => new FileStatus(line));\n}\n\nexport async function repoRoot(directory: GitDirectoryPath): Promise<GitDirectoryPath> {\n    return (await linedOutputOf(\"git\", [\"rev-parse\", \"--show-toplevel\"], directory))[0] as GitDirectoryPath;\n}\n"
  },
  {
    "path": "src/utils/HistoryTrie.ts",
    "content": "import * as _ from \"lodash\";\nimport {fuzzyMatch} from \"./Common\";\nimport {scan} from \"../shell/Scanner\";\n\ninterface TrieNode {\n    value: string;\n    occurrences: number;\n    children: Map<string, TrieNode>;\n}\n\ninterface Continuation {\n    value: string;\n    occurrences: number;\n    space: boolean;\n}\n\nfunction untokenize(tokens: string[]): string {\n    return tokens.join(\" \");\n}\n\nfunction tokenize(string: string) {\n    return scan(string).map(token => token.escapedValue);\n}\n\nfunction getContinuation(node: TrieNode): Continuation {\n    return {\n        value: node.value,\n        occurrences: node.occurrences,\n        space: node.children.size > 0,\n    };\n}\n\nexport class HistoryTrie {\n    private root: TrieNode = {value: \"\", occurrences: 0, children: new Map()};\n\n    add(input: string | string[], node: TrieNode = this.root) {\n        const tokens = (typeof input === \"string\") ? tokenize(input.trim()) : input;\n        const token = tokens[0];\n\n        if (!token) {\n            return;\n        }\n\n        if (!node.children.has(token)) {\n            node.children.set(token, {value: token, occurrences: 0, children: new Map()});\n        }\n\n        const value = node.children.get(token)!;\n        value.occurrences += 1;\n        this.add(tokens.slice(1), value);\n    }\n\n    getContinuationsFor(input: string): Continuation[] {\n        const tokens = tokenize(input);\n\n        if (!tokens.length) {\n            return [];\n        }\n\n        const path = tokens.slice(0, -1);\n        const currentToken = tokens[tokens.length - 1];\n        const parentNode = this.getNodeAt(path);\n\n        if (!parentNode) {\n            return [];\n        }\n\n        const continuationNodes: TrieNode[] = [];\n\n        for (let node of parentNode.children.values()) {\n            if (fuzzyMatch(currentToken, node.value)) {\n                continuationNodes.push(node);\n            }\n        }\n\n        if (continuationNodes.length === 0) {\n            return [];\n        }\n\n        const isContinuationNonAmbiguous = continuationNodes.length === 1;\n\n        if (isContinuationNonAmbiguous) {\n            const continuationNode = continuationNodes[0];\n            const longestNonAmbiguousContinuation = this.getLongestNonAmbiguousPrefix(continuationNode, [continuationNode.value]);\n            const continuation = getContinuation(continuationNode);\n\n            if (longestNonAmbiguousContinuation.value !== continuation.value) {\n                return [continuation, longestNonAmbiguousContinuation];\n            } else {\n                return [continuation];\n            }\n        } else {\n            return _.sortBy(continuationNodes, node => -node.occurrences).map(node => getContinuation(node));\n        }\n    }\n\n    private getNodeAt(path: string[], node: TrieNode = this.root): TrieNode | undefined {\n        const token = path[0];\n\n        if (!token) {\n            return node;\n        }\n\n        if (node.children.has(token)) {\n            return this.getNodeAt(path.slice(1), node.children.get(token)!);\n        }\n    }\n\n    private getLongestNonAmbiguousPrefix(node: TrieNode, path: string[]): Continuation {\n        if (node.children.size === 1) {\n            const key = node.children.keys().next().value;\n            path.push(key);\n            return this.getLongestNonAmbiguousPrefix(node.children.get(key)!, path);\n        } else {\n            const continuation = getContinuation(node);\n\n            return {\n                value: untokenize(path),\n                occurrences: continuation.occurrences,\n                space: continuation.space,\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/JSONTree.tsx",
    "content": "import * as React from \"react\";\nimport {colors} from \"../views/css/colors\";\n\n/*\n =========================\n * React JSONTree\n * http://eskimospy.com/stuff/react/json/\n * Copyright 2014, David Vedder\n * MIT Licence\n =========================\n */\n\nconst listStyle = {\n    padding: \"2px 0\",\n    listStyleType: \"none\",\n};\n\nconst labelStyle = {\n    display: \"inline-block\",\n    marginRight: \"0.5em\",\n    color: colors.magenta,\n};\n\nconst childrenCount = {\n    WebkitUserSelect: \"none\",\n    userSelect: \"none\",\n    cursor: \"default\",\n};\n\ntype JSONProps = {\n    data: any,\n    keyName?: any,\n    key?: any,\n    initialExpanded?: boolean,\n};\n\ntype JSONState = {\n    expanded?: any,\n    createdChildNodes?: any,\n};\n\ntype JSONValueProps = {\n    value: any,\n    keyName: any,\n    key: any,\n};\n\n/**\n * Returns the type of an object as a string.\n *\n * @param obj Object The object you want to inspect\n * @return String The object's type\n */\nlet objType  = function (obj: any) {\n    return Object.prototype.toString.call(obj).slice(8, -1);\n};\n\n/**\n * Creates a React JSON Viewer component for a key and it's associated data\n *\n * @param key String The JSON key (property name) for the node\n * @param value Mixed The associated data for the JSON key\n * @return Component The React Component for that node\n */\nlet grabNode = function (key: any, value: any): any {\n    let nodeType = objType(value);\n    let aKey = key + Date.now();\n    if (nodeType === \"Object\") {\n        return <JSONObjectNode data={value} keyName={key} key={aKey} />;\n    } else if (nodeType === \"Array\") {\n        return <JSONArrayNode data={value} keyName={key} key={aKey} />;\n    } else if (nodeType === \"String\") {\n        return <JSONStringNode keyName={key} value={value} key={aKey} />;\n    } else if (nodeType === \"Number\") {\n        return <JSONNumberNode keyName={key} value={value} key={aKey} />;\n    } else if (nodeType === \"Boolean\") {\n        return <JSONBooleanNode keyName={key} value={value} key={aKey} />;\n    } else if (nodeType === \"Null\") {\n        return <JSONNullNode keyName={key} value={value} key={aKey} />;\n    } else {\n        return <div>How did this happen? {nodeType}</div>;\n    }\n};\n\n/**\n * Mixin for stopping events from propagating and collapsing our tree all\n * willy nilly.\n */\nlet squashClick = function (e: any) {\n    e.stopPropagation();\n};\n\n/**\n * Array node class. If you have an array, this is what you should use to\n * display it.\n */\nclass JSONArrayNode extends React.Component<JSONProps, JSONState> {\n    private itemString: boolean | string;\n    private needsChildNodes: boolean | Array<any>;\n    private renderedChildren: Array<any>;\n    constructor(props: JSONProps) {\n        super(props);\n\n        this.renderedChildren = [];\n        this.needsChildNodes = [];\n        this.itemString = false;\n\n        this.state = {\n            expanded: props.initialExpanded,\n            createdChildNodes: false,\n        };\n\n    }\n\n    getDefaultProps() {\n        return { data: [], initialExpanded: false };\n    }\n\n    componentWillReceiveProps() {\n        // resets our caches and flags we need to build child nodes again\n        this.renderedChildren = [];\n        this.itemString = false;\n        this.needsChildNodes = true;\n    }\n    /**\n     * Returns the child nodes for each element in the array. If we have\n     * generated them previously, we return from cache, otherwise we create\n     * them.\n     */\n    getChildNodes() {\n        let childNodes: Array<any> = [];\n        if (this.state.expanded && this.needsChildNodes) {\n            for (let i = 0; i < this.props.data.length; i += 1) {\n                childNodes.push(grabNode(i, this.props.data[i]));\n            }\n            this.needsChildNodes = false;\n            this.renderedChildren = childNodes;\n        }\n        return this.renderedChildren;\n    }\n    /**\n     * Returns the \"n Items\" string for this node, generating and\n     * caching it if it hasn't been created yet.\n     */\n    getItemString() {\n        if (!this.itemString) {\n            let lenWord = (this.props.data.length === 1) ? \" Item\" : \" Items\";\n            this.itemString = this.props.data.length + lenWord;\n        }\n        return this.itemString;\n    }\n    render(): JSX.Element {\n        let childNodes = this.getChildNodes();\n        let childListStyle = {\n            display: (this.state.expanded) ? \"block\" : \"none\",\n        };\n        let cls = \"array jsonTreeParentNode\";\n        cls += (this.state.expanded) ? \" expanded\" : \"\";\n        return <li style={listStyle} className={cls} onClick={e => {\n            e.stopPropagation();\n            this.setState({expanded: !this.state.expanded});\n        }}>\n            <label style={labelStyle}>{this.props.keyName}: </label>{\"[...] \"}\n            <span style={childrenCount}>{this.getItemString()}</span>\n            <ol style={childListStyle}>{childNodes}</ol>\n        </li>;\n    }\n}\n\n/**\n * Object node class. If you have an object, this is what you should use to\n * display it.\n */\nclass JSONObjectNode extends React.Component<JSONProps, any> {\n    private itemString: boolean | string;\n    private needsChildNodes: boolean | Array<any>;\n    private renderedChildren: Array<any>;\n\n    constructor(props: JSONProps) {\n        super(props);\n        this.renderedChildren = [];\n        this.needsChildNodes = [];\n        this.itemString = false;\n\n        this.state = {\n            expanded: props.initialExpanded,\n            createdChildNodes: false,\n        };\n    }\n    getDefaultProps() {\n        return { data: [], initialExpanded: false };\n    }\n\n    componentWillReceiveProps() {\n        // resets our caches and flags we need to build child nodes again\n        this.renderedChildren = [];\n        this.itemString = false;\n        this.needsChildNodes = true;\n    }\n    /**\n     * Returns the child nodes for each element in the object. If we have\n     * generated them previously, we return from cache, otherwise we create\n     * them.\n     */\n    getChildNodes() {\n        if (this.state.expanded && this.needsChildNodes) {\n            let obj = this.props.data;\n            let childNodes: Array<any> = [];\n            for (let k in obj) {\n                if (obj.hasOwnProperty(k)) {\n                    childNodes.push( grabNode(k, obj[k]));\n                }\n            }\n            this.needsChildNodes = false;\n            this.renderedChildren = childNodes;\n        }\n        return this.renderedChildren;\n    }\n    /**\n     * Returns the \"n Items\" string for this node, generating and\n     * caching it if it hasn't been created yet.\n     */\n    getItemString() {\n        if (!this.itemString) {\n            let obj = this.props.data;\n            let len = 0;\n            let lenWord = \" Items\";\n            for (let k in obj) {\n                if (obj.hasOwnProperty(k)) {\n                    len += 1;\n                }\n            }\n            if (len === 1) {\n                lenWord = \" Item\";\n            }\n            this.itemString = len + lenWord;\n        }\n        return this.itemString;\n    }\n\n    render(): JSX.Element {\n        let childListStyle = {\n            display: (this.state.expanded) ? \"block\" : \"none\",\n        };\n        let cls = \"object jsonTreeParentNode\";\n        cls += (this.state.expanded) ? \" expanded\" : \"\";\n        return (\n            <li style={listStyle} className={cls} onClick={e => {\n                e.stopPropagation();\n                this.setState({expanded: !this.state.expanded});\n            }}>\n                <label style={labelStyle}>{this.props.keyName}: </label>{\"{...} \"}\n                <span style={childrenCount}>{this.getItemString()}</span>\n                <ul style={childListStyle}>{this.getChildNodes()}</ul>\n            </li>\n        );\n    }\n}\n\n/**\n * String node component\n */\nclass JSONStringNode extends React.Component<JSONValueProps, any> {\n    render(): JSX.Element {\n        return (\n            <li style={listStyle} className=\"string\" onClick={squashClick}>\n                <label style={labelStyle}>{this.props.keyName}: </label>\n                <span style={{color: colors.green}}>\"{this.props.value}\"</span>\n            </li>\n        );\n    }\n}\n\n/**\n * Number node component\n */\nclass JSONNumberNode extends React.Component<JSONValueProps, any> {\n    render(): JSX.Element {\n        return (\n            <li style={listStyle} className=\"\" onClick={squashClick}>\n                <label style={labelStyle}>{this.props.keyName}: </label>\n                <span style={{color: colors.blue}}>{this.props.value}</span>\n            </li>\n        );\n    }\n}\n\n\n/**\n * Null node component\n */\nclass JSONNullNode extends React.Component<JSONValueProps, any> {\n    render(): JSX.Element {\n        return (\n            <li style={listStyle} className=\"\" onClick={squashClick}>\n                <label style={labelStyle}>{this.props.keyName}: </label>\n                <span style={{color: colors.red}}>null</span>\n            </li>\n        );\n    }\n}\n\n/**\n * Boolean node component\n */\nclass JSONBooleanNode extends React.Component<JSONValueProps, any> {\n    render(): JSX.Element {\n        let truthString = (this.props.value) ? \"true\" : \"false\";\n        return <li style={listStyle} className={\"boolean \" + truthString} onClick={squashClick}>\n            <label style={labelStyle}>{this.props.keyName}: </label>\n            <span style={{color: colors.cyan}}>{truthString}</span>\n        </li>;\n    }\n}\n\n/**\n * JSONTree component. This is the 'viewer' base. Pass it a `data` prop and it\n * will render that data, or pass it a `source` URL prop and it will make\n * an XMLHttpRequest for said URL and render that when it loads the data.\n *\n * The first node it draws will be expanded by default.\n */\nexport class JSONTree extends React.Component<JSONProps, any> {\n    render(): JSX.Element {\n        let nodeType = objType(this.props.data);\n        let rootNode: JSX.Element;\n        if (nodeType === \"Object\") {\n            rootNode = <JSONObjectNode data={this.props.data} keyName=\"(root)\" initialExpanded={true} />;\n        } else if (nodeType === \"Array\") {\n            rootNode = <JSONArrayNode data={this.props.data} initialExpanded={true} keyName=\"(root)\" />;\n        } else {\n            return <span>How did you manage that?</span>;\n        }\n        return <ul>{rootNode}</ul>;\n    }\n}\n"
  },
  {
    "path": "src/utils/Link.tsx",
    "content": "import * as React from \"react\";\nimport * as e from \"electron\";\n\nexport const Link: React.StatelessComponent<{absolutePath: string, children: any}> = ({\n  absolutePath,\n  children,\n}) => <span\n  style={{cursor: \"pointer\"}}\n  className=\"underlineOnHover\"\n  onClick={() => e.shell.openExternal(`file://${absolutePath}`)}\n>{children}</span>;\n"
  },
  {
    "path": "src/utils/ManPageParsingUtils.ts",
    "content": "import {Suggestion} from \"../plugins/completion_utils/Common\";\n\nexport const combineManPageLines = (lines: string[]) => lines\n    .map(line => line.trim())\n    .reduce(\n        (memo, next) => {\n            if (next.endsWith(\"-\")) {\n                return memo.concat(next.slice(0, -1));\n            } else {\n                return memo.concat(next, \" \");\n            }\n        },\n        \"\",\n    )\n    .trim();\n\n// Man pages have backspace literals, so \"apply\" them, and remove excess whitespace.\nexport const preprocessManPage = (contents: string) => contents.replace(/.\\x08/g, \"\").trim();\n\nexport const extractManPageSections = (contents: string) => {\n    const lines = contents.split(\"\\n\");\n\n    let currentSection = \"\";\n    let sections: { [section: string]: string[] } = {};\n    lines.forEach((line: string) => {\n        if (line.startsWith(\" \") || line === \"\") {\n            sections[currentSection].push(line);\n        } else {\n            currentSection = line;\n            if (!sections[currentSection]) {\n                sections[currentSection] = [];\n            }\n        }\n    });\n\n    return sections;\n};\n\nconst isShortFlagWithoutArgument = (manPageLine: string) => /^ *-(\\w) *(.*)$/.test(manPageLine);\n\nexport const extractManPageSectionParagraphs = (contents: string[]) => {\n    let filteredContents: string[] | undefined = undefined;\n    const firstFlag = contents.find(isShortFlagWithoutArgument);\n    if (firstFlag) {\n        const flagMatch = firstFlag.match(/^( *-\\w *)/);\n        const flagIndentation = \" \".repeat(((flagMatch || [\"\"])[0]).length);\n        filteredContents = contents.filter((line, index, array) => {\n            if (index === 0 || index === array.length - 1) {\n                return true;\n            }\n            if (\n                line === \"\" &&\n                array[index - 1].startsWith(flagIndentation) &&\n                array[index + 1].startsWith(flagIndentation)\n            ) {\n                return false;\n            }\n            return true;\n        });\n    }\n\n    return (filteredContents ? filteredContents : contents)\n    .reduce(\n        (memo, next) => {\n            if (next === \"\") {\n                memo.push([]);\n            } else {\n                memo[memo.length - 1].push(next);\n            }\n            return memo;\n        },\n        <string[][]>[[]],\n    )\n    .filter(lines => lines.length > 0);\n};\n\nexport const suggestionFromFlagParagraph = (paragraph: string[]): Suggestion | undefined => {\n    const shortFlagWithArgument = paragraph[0].match(/^ *-(\\w) (\\w*)$/);\n    const shortFlagWithoutArgument = paragraph[0].match(/^ *-(\\w) *(.*)$/);\n    if (shortFlagWithArgument) {\n        const flag = shortFlagWithArgument[1];\n        const detail = combineManPageLines(paragraph.slice(1));\n\n        return {\n            label: `-${flag}`,\n            detail: detail,\n        };\n    } else if (shortFlagWithoutArgument) {\n        const flag = shortFlagWithoutArgument[1];\n        const detail = combineManPageLines([shortFlagWithoutArgument[2], ...paragraph.slice(1)]);\n\n        return {\n            label: `-${flag}`,\n            detail: detail,\n        };\n    } else {\n        return undefined;\n    }\n};\n"
  },
  {
    "path": "src/utils/ManPages.ts",
    "content": "import {execFile} from \"child-process-promise\";\nimport {contextIndependent, unique, Suggestion} from \"../plugins/completion_utils/Common\";\nimport {\n    preprocessManPage,\n    extractManPageSections,\n    extractManPageSectionParagraphs,\n    suggestionFromFlagParagraph,\n} from \"./ManPageParsingUtils\";\n\n// Note: this is still pretty experimental. If you want to do man page parsing\n// for a new command, expect to have to make some changes here.\n\n// TODO: Fix -l option for locate\n// TODO: Handle nested options. Unblocks:\n// dd\n\nconst manPageToOptions = async (command: string, section = \"DESCRIPTION\"): Promise<Suggestion[]> => {\n    // use execFile to prevent a command like \"; echo test\" from running the \"echo test\"\n    const {stdout, stderr} =  await execFile(\"man\", [command], {});\n    if (stderr) {\n        throw `Error in retrieving man page: ${command}`;\n    }\n    // \"Apply\" backspace literals\n    const manContents = preprocessManPage(stdout);\n\n    const manSections = extractManPageSections(manContents);\n\n    // Make sure section is in manSections\n    if (!(section in manSections)) {\n        throw `Error in retrieving section \"${section}\" from man page: ${command}`;\n    }\n\n    // Split the description section (which contains the flags) into paragraphs\n    /* tslint:disable:no-string-literal */\n    const manDescriptionParagraphs: string[][] = extractManPageSectionParagraphs(manSections[section]);\n    /* tslint:enable:no-string-literal */\n\n    // Extract the paragraphs that describe flags, and parse out the flag data\n    return manDescriptionParagraphs.map(suggestionFromFlagParagraph).filter((s: Suggestion | undefined) => s !== undefined) as Suggestion[];\n};\n\nexport const manPageOptions = (command: string, section = \"DESCRIPTION\") => unique(contextIndependent(() => manPageToOptions(command, section)));\n"
  },
  {
    "path": "src/utils/OrderedSet.ts",
    "content": "export abstract class AbstractOrderedSet<T> {\n    constructor(private storageGetter: () => T[], private storageSetter: (t: T[]) => void) {\n    }\n\n    prepend(element: T) {\n        this.remove(element);\n        this.storageSetter([element].concat(this.storageGetter()));\n    }\n\n    remove(toRemove: T) {\n        this.removeWhere(existing => existing === toRemove);\n    }\n\n    removeWhere(removePredicate: (existing: T) => boolean) {\n        this.storageSetter(this.storageGetter().filter(path => !removePredicate(path)));\n    }\n\n    get size() {\n        return this.storageGetter().length;\n    }\n\n    at(index: number): T | undefined {\n        if (index >= this.size) {\n            return undefined;\n        } else {\n            return this.storageGetter()[index];\n        }\n    }\n\n    toArray() {\n        return this.storageGetter();\n    }\n}\n\nexport class OrderedSet<T> extends AbstractOrderedSet<T> {\n    constructor() {\n        let storage: T[] = [];\n        super(() => storage, updatedStorage => storage = updatedStorage);\n    }\n}\n"
  },
  {
    "path": "src/utils/Process.ts",
    "content": "import {executeCommandWithShellConfig} from \"../PTY\";\nimport {intersection} from \"lodash\";\n\n// http://linuxcommand.org/man_pages/ps1.html\n\nexport interface User {\n    ruserid: string;\n    ruser: string;\n    euserid: string;\n    euser: string;\n}\n\nexport interface Group {\n    rgroupid: string;\n    rgroup: string;\n    egroupid: string;\n    egroup: string;\n}\n\nexport interface Terminal {\n    name: string;\n    ruser: string;\n}\n\nexport interface Process {\n    pid: string;\n    time: string;\n    ruser: string;\n    cmd: string;\n}\n\nexport interface Session {\n    sid: string;\n    ruser: string;\n    rgroup: string;\n}\n\nconst ignoreUsers: string[] = [\"nobody\"];\n\nconst ignoreGroups: string[] = [\"nobody\"];\n\nconst resultSet = (result: string[]) => {\n        const numColumns = result[0].trim().replace(/ +(?= )/g, \"\").split(\" \").length;\n        return result.splice(1)\n                     .map(i => i.trim().replace(/ +(?= )/g, \"\").split(\" \", numColumns));\n    };\n\nexport const users =\n    async(): Promise<User[]> => {\n        const pInfo: string[][] =\n                resultSet(await executeCommandWithShellConfig(\"ps -eo ruid,ruser,euid,euser\"));\n        return pInfo.map(p => <User> {ruserid: p[0], ruser: p[1], euserid: p[2], euser: p[3]})\n                    .filter(p => intersection(ignoreUsers, [p.ruser, p.euser]).length === 0);\n    };\n\nexport const groups =\n    async(): Promise<Group[]> => {\n    const pInfo: string[][] =\n            resultSet(await executeCommandWithShellConfig(\"ps -eo rgid,rgroup,egid,egroup\"));\n    return pInfo.map(p => <Group> {rgroupid: p[0], rgroup: p[1], egroupid: p[2], egroup: p[3]})\n                .filter(p => intersection(ignoreGroups, [p.rgroup, p.egroup]).length === 0);\n    };\n\nexport const terminals =\n    async(): Promise<Terminal[]> => {\n        const pInfo: string[][] =\n                resultSet(await executeCommandWithShellConfig(\"ps -eo tty,ruser\"));\n        return pInfo.filter(p => p[0] !== \"?\")\n                    .map(p => <Terminal> {name: p[0], ruser: p[1]});\n    };\n\nexport const processes =\n    async(): Promise<Process[]> => {\n        const pInfo: string[][] =\n                resultSet(await executeCommandWithShellConfig(\"ps -eo pid,time,ruser,cmd\"));\n        return pInfo.map(p => <Process> {pid: p[0], time: p[1], ruser: p[2], cmd: p[3]});\n    };\n\nexport const sessions =\n    async(): Promise<Session[]> => {\n    const pInfo: string[][] =\n            resultSet(await executeCommandWithShellConfig(\"ps -eo sid,ruser,rgroup\"));\n    return pInfo.map(p => <Session> {sid: p[0], rgroup: p[1]});\n};\n\n"
  },
  {
    "path": "src/utils/Shell.ts",
    "content": "import {basename} from \"path\";\nimport * as Path from \"path\";\nimport {resolveFile, io, isWindows, filterAsync, homeDirectory} from \"./Common\";\nimport {executeCommandWithShellConfig} from \"../PTY\";\n\nabstract class Shell {\n    abstract get executableName(): string;\n    abstract get configFiles(): string[];\n    abstract get noConfigSwitches(): string[];\n    abstract get executeCommandSwitches(): string[];\n    abstract get interactiveCommandSwitches(): string[];\n    abstract get preCommandModifiers(): string[];\n    abstract get commandExecutorPath(): string;\n    abstract get environmentCommand(): string;\n    abstract loadAliases(): Promise<string[]>;\n\n    abstract combineCommands(commands: string[]): string;\n\n    async existingConfigFiles(): Promise<string[]> {\n        const resolvedConfigFiles = this.configFiles.map(fileName => resolveFile(homeDirectory, fileName));\n        return await filterAsync(resolvedConfigFiles, io.fileExists);\n    }\n}\n\nabstract class UnixShell extends Shell {\n    get executeCommandSwitches() {\n        return [\"-c\"];\n    }\n\n    get interactiveCommandSwitches() {\n        return [\"-i\", \"-c\"];\n    }\n\n    get commandExecutorPath() {\n        return \"/bin/bash\";\n    }\n\n    get environmentCommand() {\n        return \"env\";\n    }\n\n    loadAliases() {\n        return executeCommandWithShellConfig(\"alias\");\n    }\n\n    combineCommands(commands: string[]) {\n        return `'${commands.join(\"; \")}'`;\n    }\n}\n\nclass Bash extends UnixShell {\n    get executableName() {\n        return \"bash\";\n    }\n\n    get configFiles() {\n        return [\n            // List drawn from GNU bash 4.3 man page INVOCATION section.\n            // ~/.bashrc is only supposed to be used for non-login shells\n            // and ~/.bash_profile is only supposed to be used for login\n            // shells, but load both anyway because that's what people expect.\n            \"/etc/profile\",\n            \"~/.bash_profile\",\n            \"~/.bash_login\",\n            \"~/.profile\",\n            \"~/.bashrc\",\n        ];\n    }\n\n    get noConfigSwitches() {\n        return [\"--noprofile\", \"--norc\"];\n    }\n\n    get preCommandModifiers(): string[] {\n        return [];\n    }\n}\n\nclass ZSH extends UnixShell {\n    get executableName() {\n        return \"zsh\";\n    }\n\n    get configFiles() {\n        return [\n            // List drawn from zhs 5.0.8 man page STARTUP/SHUTDOWN FILES section.\n            \"/etc/zshenv\",\n            Path.join(process.env.ZDOTDIR || \"~\", \".zshenv\"),\n            \"/etc/zprofile\",\n            Path.join(process.env.ZDOTDIR || \"~\", \".zprofile\"),\n            \"/etc/zshrc\",\n            Path.join(process.env.ZDOTDIR || \"~\", \".zshrc\"),\n            \"/etc/zlogin\",\n            Path.join(process.env.ZDOTDIR || \"~\", \".zlogin\"),\n            // This one is not listed in the man pages, but some zsh installations do use it.\n            \"~/.zsh_profile\",\n        ];\n    }\n\n    get noConfigSwitches() {\n        return [\"--no-globalrcs\", \"--no-rcs\"];\n    }\n\n    get preCommandModifiers() {\n        return [\n            \"-\",\n            \"noglob\",\n            \"nocorrect\",\n            \"exec\",\n            \"command\",\n        ];\n    }\n}\n\nclass Cmd extends Shell {\n    static get cmdPath() {\n        return Path.join(process.env.WINDIR!, \"System32\", \"cmd.exe\");\n    }\n\n    get executableName() {\n        return \"cmd.exe\";\n    }\n\n    get configFiles() {\n        return [\n        ];\n    }\n\n    get executeCommandSwitches() {\n        return [\"/c\"];\n    }\n\n    get interactiveCommandSwitches() {\n        return [\"/c\"];\n    }\n\n    get noConfigSwitches() {\n        return [];\n    }\n\n    get preCommandModifiers(): string[] {\n        return [];\n    }\n\n    get commandExecutorPath() {\n        return Cmd.cmdPath;\n    }\n\n    get environmentCommand() {\n        return \"set\";\n    }\n\n    combineCommands(commands: string[]) {\n        return `\"${commands.join(\" && \")}`;\n    }\n\n    async loadAliases() {\n        return [];\n    }\n}\n\nconst supportedShells: Dictionary<Shell> = {\n    bash: new Bash(),\n    zsh: new ZSH(),\n    \"cmd.exe\": new Cmd(),\n};\n\nconst shell = (): string => {\n    const shellName = process.env.SHELL ? basename(process.env.SHELL!) : \"\";\n    if (shellName in supportedShells) {\n        return shellName;\n    } else {\n        const defaultShell = isWindows ? Cmd.cmdPath : \"/bin/bash\";\n        console.error(`${shellName} is not supported; defaulting to ${defaultShell}`);\n        return defaultShell;\n    }\n};\n\nexport const loginShell: Shell = supportedShells[basename(shell())];\n"
  },
  {
    "path": "src/views/ApplicationComponent.tsx",
    "content": "import {type as osType} from \"os\";\nimport * as classNames from \"classnames\";\nimport {TabHeaderComponent, Props} from \"./TabHeaderComponent\";\nimport * as React from \"react\";\nimport {ipcRenderer} from \"electron\";\nimport {remote} from \"electron\";\nimport * as css from \"./css/styles\";\nimport {SearchComponent} from \"./SearchComponent\";\nimport {TabComponent} from \"./TabComponent\";\nimport {SessionID} from \"../shell/Session\";\nimport {services} from \"../services\";\nimport * as _ from \"lodash\";\n\ntype ApplicationState = {\n    tabs: Array<{id: number, sessionIDs: SessionID[]; focusedSessionID: SessionID}>;\n    focusedTabIndex: number;\n};\n\nexport class ApplicationComponent extends React.Component<{}, ApplicationState> {\n    tabComponents: TabComponent[];\n\n    constructor(props: {}) {\n        super(props);\n\n        const sessionID = services.sessions.create();\n        this.state = {\n            tabs: [{\n                id: Date.now(),\n                sessionIDs: [sessionID],\n                focusedSessionID: sessionID,\n            }],\n            focusedTabIndex: 0,\n        };\n\n        services.window.onResize.subscribe(() => this.resizeAllSessions());\n        services.window.onClose.subscribe(() => services.sessions.closeAll());\n        services.sessions.onClose.subscribe(id => this.removeSessionFromState(id));\n        services.font.onChange.subscribe(() => {\n            this.forceUpdate();\n            this.resizeAllSessions();\n        });\n\n        ipcRenderer.on(\"change-working-directory\", (_event: Event, directory: string) =>\n            this.focusedSession.directory = directory,\n        );\n    }\n\n    render() {\n        let tabs: React.ReactElement<Props>[] | undefined;\n\n        if (this.state.tabs.length > 1) {\n            tabs = this.state.tabs.map((tab, index: number) =>\n                <TabHeaderComponent\n                    isFocused={index === this.state.focusedTabIndex}\n                    key={tab.id}\n                    position={index + 1}\n                    activate={() => this.setState({focusedTabIndex: index})}\n                    closeHandler={(event: React.MouseEvent<HTMLSpanElement>) => {\n                        services.sessions.close(this.state.tabs[index].sessionIDs);\n                        event.stopPropagation();\n                        event.preventDefault();\n                    }}\n                />,\n            );\n        }\n\n        this.tabComponents = [];\n\n        return (\n            <div className=\"application\" style={css.application()}>\n                <div className={classNames(\"title-bar\", {\"reversed\": this.isMacOS()})}>\n                    <SearchComponent/>\n                    <ul className=\"tabs\">{tabs}</ul>\n                </div>\n                {this.state.tabs.map((tabProps, index) =>\n                    <TabComponent {...tabProps}\n                                  isFocused={index === this.state.focusedTabIndex}\n                                  key={tabProps.id}\n                                  onSessionFocus={(id: SessionID) => {\n                                      const state = this.cloneState();\n                                      state.tabs[state.focusedTabIndex].focusedSessionID = id;\n                                      this.setState(state);\n                                  }}\n                                  ref={tabComponent => this.tabComponents[index] = tabComponent!}/>)}\n            </div>\n        );\n    }\n\n    /**\n     * is Mac OS\n     */\n\n    isMacOS() {\n      return \"Darwin\" === osType();\n    }\n\n    /**\n     * Tab methods.\n     */\n\n    get focusedTabComponent() {\n        return this.tabComponents[this.state.focusedTabIndex];\n    }\n\n    addTab(): void {\n        if (this.state.tabs.length < 9) {\n            const id = services.sessions.create();\n\n            const state = this.cloneState();\n            state.tabs.push({\n                id: Date.now(),\n                sessionIDs: [id],\n                focusedSessionID: id,\n            });\n            state.focusedTabIndex = state.tabs.length - 1;\n\n            this.setState(state);\n        } else {\n            remote.shell.beep();\n        }\n    }\n\n    focusPreviousTab() {\n        if (this.state.focusedTabIndex !== 0) {\n            this.focusTab(this.state.focusedTabIndex - 1);\n        }\n    }\n\n    focusNextTab() {\n        if (this.state.focusedTabIndex !== this.state.tabs.length - 1) {\n            this.focusTab(this.state.focusedTabIndex + 1);\n        }\n    }\n\n    focusTab(index: number): void {\n        if (index === 8) {\n            index = this.state.tabs.length - 1;\n        }\n\n        if (this.state.tabs.length > index) {\n            this.setState({focusedTabIndex: index});\n        } else {\n            remote.shell.beep();\n        }\n    }\n\n    closeFocusedTab() {\n        const sessionIDs = this.state.tabs[this.state.focusedTabIndex].sessionIDs;\n        services.sessions.close(sessionIDs);\n    }\n\n    /**\n     * Session methods.\n     */\n\n    get focusedSession() {\n        return services.sessions.get(this.state.tabs[this.state.focusedTabIndex].focusedSessionID);\n    }\n\n    closeFocusedSession() {\n        services.sessions.close(this.focusedSession.id);\n    }\n\n    otherSession(): void {\n        const state = this.cloneState();\n        const tabState = state.tabs[state.focusedTabIndex];\n\n        if (tabState.sessionIDs.length < 2) {\n            const id = services.sessions.create();\n            tabState.sessionIDs.push(id);\n            tabState.focusedSessionID = id;\n\n            this.setState(state, () => this.resizeTabSessions(state.focusedTabIndex));\n        } else {\n            tabState.focusedSessionID = tabState.sessionIDs.find(id => id !== tabState.focusedSessionID)!;\n            this.setState(state);\n        }\n    }\n\n    private resizeTabSessions(tabIndex: number): void {\n        this.tabComponents[tabIndex].sessionComponents.forEach(sessionComponent => sessionComponent.resizeSession());\n    }\n\n    private resizeAllSessions() {\n        this.tabComponents.forEach(tabComponent => {\n            tabComponent.sessionComponents.forEach(sessionComponent => sessionComponent.resizeSession());\n        });\n    }\n\n    private removeSessionFromState(id: SessionID) {\n        const state = this.cloneState();\n        const tabIndex = state.tabs.findIndex(tabState => tabState.sessionIDs.includes(id));\n        const tabState = state.tabs[tabIndex];\n\n        if (tabState.sessionIDs.length === 1) {\n            this.removeTabFromState(tabIndex);\n        } else {\n            const sessionIndex = tabState.sessionIDs.findIndex(id => id === tabState.focusedSessionID);\n            tabState.sessionIDs.splice(sessionIndex, 1);\n            tabState.focusedSessionID = tabState.sessionIDs[0];\n\n            this.setState(state, () => this.resizeTabSessions(tabIndex));\n        }\n    }\n\n    private removeTabFromState(index: number): void {\n        const state = this.cloneState();\n\n        state.tabs.splice(index, 1);\n        state.focusedTabIndex = Math.max(0, index - 1);\n\n        if (state.tabs.length === 0) {\n            ipcRenderer.send(\"quit\");\n        } else {\n            this.setState(state);\n        }\n    }\n\n    /**\n     * Return a deep clone of the state in order not to\n     * accidentally mutate it.\n     */\n    private cloneState(): ApplicationState {\n        return _.cloneDeep(this.state);\n    }\n}\n"
  },
  {
    "path": "src/views/JobComponent.tsx",
    "content": "import * as React from \"react\";\nimport {Job} from \"../shell/Job\";\nimport {OutputComponent} from \"./OutputComponent\";\nimport {JobHeaderComponent} from \"./JobHeaderComponent\";\nimport {Status} from \"../Enums\";\n\ninterface Props {\n    job: Job;\n    jobStatus: Status;\n    isFocused: boolean;\n}\n\ninterface State {\n    prettify: boolean;\n}\n\nexport class JobComponent extends React.Component<Props, State> {\n    constructor(props: Props) {\n        super(props);\n\n        this.state = {\n            prettify: true,\n        };\n    }\n\n    componentDidMount() {\n        this.props.job.on(\"status\", () => this.forceUpdate());\n        /**\n         * Without this a job is below viewport when you\n         * scroll up and then execute a job.\n         */\n        requestAnimationFrame(() => this.jobNode.scrollIntoView());\n    }\n\n    shouldComponentUpdate(nextProps: Props, nextState: State) {\n        return this.state.prettify !== nextState.prettify ||\n            this.props.jobStatus === Status.InProgress ||\n            this.props.jobStatus !== nextProps.jobStatus;\n    }\n\n    render() {\n        let output: React.ReactElement<any>;\n        let canBePrettified = this.props.job.canBePrettified();\n        if (canBePrettified && this.state.prettify) {\n            output = this.props.job.prettify();\n        } else {\n            output = <OutputComponent job={this.props.job}/>;\n        }\n\n        return (\n            <div className={\"job\"} data-status={this.props.job.status} ref=\"job\">\n                <JobHeaderComponent\n                    job={this.props.job}\n                    showPrettifyToggle={canBePrettified}\n                    prettifyToggler={() => {\n                        // Show not prettified output\n                        this.setState({prettify: !this.state.prettify});\n                    }}\n                    isPrettified={this.state.prettify}\n                 />\n                {output}\n            </div>\n        );\n    }\n\n    private get jobNode(): HTMLDivElement {\n        /* tslint:disable:no-string-literal */\n        return this.refs[\"job\"] as HTMLDivElement;\n    }\n}\n"
  },
  {
    "path": "src/views/JobHeaderComponent.tsx",
    "content": "import * as React from \"react\";\nimport {PrettifyToggleComponent} from \"./PrettifyToggleComponent\";\nimport {Job} from \"../shell/Job\";\n\ninterface Props {\n    job: Job;\n    prettifyToggler: () => void;\n    isPrettified: boolean;\n    showPrettifyToggle: boolean;\n}\n\n\nexport class JobHeaderComponent extends React.Component<Props, {}> {\n    render() {\n        const prettifyToggle = this.props.showPrettifyToggle ?\n            <PrettifyToggleComponent prettifyToggler={this.props.prettifyToggler} isPrettified={this.props.isPrettified}/> :\n            null;\n\n        return (\n            <div className=\"job-header\">\n                <div>{this.props.job.prompt.value}</div>\n                <div className=\"job-actions\">\n                    {prettifyToggle}\n                </div>\n            </div>\n        );\n    }\n}\n"
  },
  {
    "path": "src/views/Main.tsx",
    "content": "import {handleUserEvent} from \"./keyevents/Keybindings\";\nimport {handleMouseEvent} from \"./mouseevents/MouseEvents\";\n\nprocess.env.PATH = \"/usr/local/bin:\" + process.env.PATH;\nprocess.env.NODE_ENV = process.env.NODE_ENV || \"production\";\nprocess.env.LANG = process.env.LANG || \"en_US.UTF-8\";\nprocess.env.COLORTERM = \"truecolor\";\nprocess.env.TERM = \"xterm-256color\";\n\nimport {loadAliasesFromConfig} from \"../shell/Aliases\";\nconst reactDOM = require(\"react-dom\");\nimport * as React from \"react\";\nimport {ApplicationComponent} from \"./ApplicationComponent\";\nimport {loadAllPlugins} from \"../PluginManager\";\nimport {loadEnvironment} from \"../shell/Environment\";\nimport {UserEvent, MouseEvent} from \"../Interfaces\";\nimport {remote} from \"electron\";\nimport {buildMenuTemplate} from \"./menu/Menu\";\n\nconst browserWindow = remote.BrowserWindow.getAllWindows()[0];\n\ndocument.addEventListener(\n    \"dragover\",\n    function(event) {\n        event.preventDefault();\n        return false;\n    },\n    false,\n);\n\nasync function main() {\n    // Should be required before mounting Application.\n    require(\"../monaco/PromptTheme\");\n    require(\"../monaco/ShellLanguage\");\n    require(\"../monaco/ShellHistoryLanguage\");\n\n    // FIXME: Remove loadAllPlugins after switching to Webpack (because all the files will be loaded at start anyway).\n    await Promise.all([loadAllPlugins(), loadEnvironment(), loadAliasesFromConfig()]);\n    const application: ApplicationComponent = reactDOM.render(\n        <ApplicationComponent/>,\n        document.getElementById(\"react-entry-point\"),\n    );\n\n    const template = buildMenuTemplate(remote.app, browserWindow, application);\n    remote.Menu.setApplicationMenu(remote.Menu.buildFromTemplate(template));\n\n    const userEventHandler = (event: UserEvent) => handleUserEvent(\n        application,\n        window.search,\n        event,\n    );\n\n    const mouseEventHandler = (event: MouseEvent) => handleMouseEvent(\n        application,\n        event,\n    );\n\n    document.body.addEventListener(\"keydown\", userEventHandler, true);\n    document.body.addEventListener(\"paste\", userEventHandler, true);\n    document.body.addEventListener(\"drop\", mouseEventHandler, true);\n\n    require(\"../plugins/JobFinishedNotifications\");\n    require(\"../plugins/UpdateLastPresentWorkingDirectory\");\n    require(\"../plugins/SaveHistory\");\n    require(\"../plugins/SaveWindowBounds\");\n    require(\"../plugins/AliasSuggestions\");\n}\n\nif (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", main, false);\n} else {\n    main();\n}\n"
  },
  {
    "path": "src/views/OutputComponent.tsx",
    "content": "import * as React from \"react\";\nimport {Char} from \"../Char\";\nimport {groupWhen} from \"../utils/Common\";\nimport {List} from \"immutable\";\nimport * as css from \"./css/styles\";\nimport {Job} from \"../shell/Job\";\nimport {Status} from \"../Enums\";\n\nconst CharGroupComponent = ({group}: {group: Char[]}) => {\n    const attributes = group[0].attributes;\n    return (\n        <span\n            className=\"char-group\"\n            style={attributes && css.charGroup(attributes)}>\n        {group.map(char => char.value).join(\"\")}\n        </span>\n    );\n};\n\ninterface RowProps {\n    row: List<Char>;\n}\n\nexport class RowComponent extends React.Component<RowProps, {}> {\n    shouldComponentUpdate(nextProps: RowProps) {\n        return this.props.row !== nextProps.row;\n    }\n\n    render() {\n        const charGroups = groupWhen((a, b) => a.attributes === b.attributes, this.props.row.toArray());\n        const charGroupComponents = charGroups.map((charGroup: Char[], index: number) =>\n            <CharGroupComponent group={charGroup} key={index}/>,\n        );\n\n        return (\n            <div className=\"row\">\n                {charGroupComponents}\n            </div>\n        );\n    }\n}\n\ninterface Props {\n    job: Job;\n}\n\nexport class OutputComponent extends React.Component<Props, {}> {\n    componentDidMount() {\n        this.props.job.once(\"data\", () => this.forceUpdate());\n    }\n\n    componentDidUpdate() {\n        this.props.job.once(\"data\", () => this.forceUpdate());\n    }\n\n    render() {\n        const output = this.props.job.output;\n        const buffer = output.activeBuffer;\n        const showCursor = this.props.job.status === Status.InProgress && (buffer._showCursor || buffer._blinkCursor);\n        const cursorComponent = showCursor\n            ? <span className=\"cursor\"\n                    style={{\n                        \"--scrollback-size\": buffer.scrollbackSize,\n                        \"--row-index\": buffer.cursorRowIndex,\n                        \"--column-index\": buffer.cursorColumnIndex,\n                    }}/>\n            : undefined;\n\n        const rowComponents = buffer.map(row => <RowComponent key={row.hashCode()} row={row}/>);\n\n        return (\n            <div className=\"output\"\n                 data-screen-mode={output.screenMode}\n                 data-buffer-type={output.activeBufferType}>\n                {cursorComponent}\n                {rowComponents}\n            </div>\n        );\n    }\n}\n"
  },
  {
    "path": "src/views/PrettifyToggleComponent.tsx",
    "content": "import * as React from \"react\";\nimport {fontAwesome} from \"./css/FontAwesome\";\n\ninterface Props {\n    prettifyToggler: () => void;\n    isPrettified: boolean;\n}\n\nexport const PrettifyToggleComponent = (props: Props) =>\n    <span className=\"prettify-toggle\" data-enabled={props.isPrettified} onClick={props.prettifyToggler}>\n        {fontAwesome.magic}\n    </span>;\n"
  },
  {
    "path": "src/views/PromptComponent.tsx",
    "content": "/// <reference path=\"../../node_modules/monaco-editor/monaco.d.ts\" />\n\nimport * as _ from \"lodash\";\nimport * as React from \"react\";\nimport {Prompt} from \"../shell/Prompt\";\nimport {scan} from \"../shell/Scanner\";\nimport {Session} from \"../shell/Session\";\nimport {services} from \"../services/index\";\n\ninterface Props {\n    session: Session;\n    isFocused: boolean;\n}\n\nenum Mode {\n    Normal = \"normal\",\n    HistorySearch = \"history-search\",\n}\n\ninterface State {\n    displayedHistoryRecordID: number | undefined;\n    mode: Mode;\n}\n\nexport class PromptComponent extends React.Component<Props, State> {\n    private prompt: Prompt;\n    private editor: monaco.editor.IStandaloneCodeEditor;\n    private model = monaco.editor.createModel(\"\", \"shell\", monaco.Uri.parse(`shell://${this.props.session.id}`));\n    private historyModel = monaco.editor.createModel(\"\", \"shell-history\", monaco.Uri.parse(`shell-history://${this.props.session.id}`));\n\n    /* tslint:disable:member-ordering */\n    constructor(props: Props) {\n        super(props);\n        this.prompt = new Prompt(this.props.session);\n\n        this.state = {\n            displayedHistoryRecordID: undefined,\n            mode: Mode.Normal,\n        };\n    }\n\n    componentDidMount() {\n        this.editor = monaco.editor.create(this.promptContentNode, {\n            theme: \"upterm-prompt-theme\",\n            model: this.model,\n            lineNumbers: \"off\",\n            fontSize: services.font.size + 2,\n            fontFamily: services.font.family,\n            suggestFontSize: services.font.size,\n            minimap: {enabled: false},\n            scrollbar: {\n                vertical: \"hidden\",\n                horizontal: \"hidden\",\n            },\n            overviewRulerLanes: 0,\n            quickSuggestions: true,\n            quickSuggestionsDelay: 0,\n            parameterHints: true,\n            iconsInSuggestions: false,\n            wordBasedSuggestions: false,\n        });\n\n        services.font.onChange.subscribe(() => {\n            this.editor.updateOptions({\n                fontSize: services.font.size * 1.2,\n                fontFamily: services.font.family,\n                suggestFontSize: services.font.size,\n            });\n            this.editor.layout();\n        });\n\n        this.editor.addCommand(\n            monaco.KeyCode.UpArrow,\n            () => this.setPreviousHistoryItem(),\n            \"!suggestWidgetVisible\",\n        );\n        this.editor.addCommand(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_P,\n            () => this.setPreviousHistoryItem(),\n            \"!suggestWidgetVisible\",\n        );\n        this.editor.addCommand(\n            monaco.KeyCode.DownArrow,\n            () => this.setNextHistoryItem(),\n            \"!suggestWidgetVisible\",\n        );\n        this.editor.addCommand(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_N,\n            () => this.setNextHistoryItem(),\n            \"!suggestWidgetVisible\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_B,\n            \"cursorLeft\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.Alt | monaco.KeyCode.KEY_B,\n            \"cursorWordStartLeft\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_F,\n            \"cursorRight\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.Alt | monaco.KeyCode.KEY_F,\n            \"cursorWordEndRight\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_W,\n            \"deleteWordLeft\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.Alt | monaco.KeyCode.KEY_D,\n            \"deleteWordRight\",\n        );\n        this.addShortcut(\n            monaco.KeyMod.WinCtrl | monaco.KeyCode.KEY_D,\n            \"deleteRight\",\n        );\n\n        this.unbindDefaultAction(\"editor.action.outdentLines\");\n        this.unbindDefaultAction(\"editor.action.indentLines\");\n        this.unbindDefaultAction(\"actions.find\");\n        this.unbindDefaultAction(\"editor.action.gotoLine\");\n\n        this.focus();\n    }\n\n    componentDidUpdate(prevProps: Props, prevState: State) {\n        if (!prevProps.isFocused && this.props.isFocused) {\n            this.focus();\n        }\n\n        if (prevState.mode !== this.state.mode) {\n            if (this.isInHistorySearchMode) {\n                this.editor.setModel(this.historyModel);\n                this.setValue(this.model.getValue());\n                this.triggerSuggest();\n            } else {\n                this.editor.setModel(this.model);\n                this.setValue(this.historyModel.getValue());\n            }\n        }\n    }\n\n    render() {\n        return (\n            <div className=\"prompt\" data-mode={this.state.mode}>\n                <div className=\"prompt-content\" ref=\"prompt-content\"/>\n            </div>\n        );\n    }\n\n    setHistorySearchMode() {\n        this.setState({mode: Mode.HistorySearch});\n    }\n\n    setNormalMode() {\n        this.setState({mode: Mode.Normal});\n    }\n\n    acceptSelectedSuggestion() {\n        this.editor.trigger(\"\", \"acceptSelectedSuggestion\", {});\n\n        if (this.isInHistorySearchMode) {\n            this.setNormalMode();\n        } else {\n            this.triggerSuggest();\n        }\n    }\n\n    get isInHistorySearchMode(): boolean {\n        return this.state.mode === Mode.HistorySearch;\n    }\n\n    focus(): void {\n        this.editor.focus();\n    }\n\n    clear(): void {\n        this.setValue(\"\");\n    }\n\n    onReturnKeyPress(): void {\n        if (this.isInHistorySearchMode) {\n            this.acceptSelectedSuggestion();\n        } else {\n            this.execute();\n        }\n    }\n\n    async appendLastLArgumentOfPreviousCommand(): Promise<void> {\n        const latestHistoryRecord = services.history.latest;\n\n        if (latestHistoryRecord) {\n            this.setValue(this.prompt.value + _.last(scan(latestHistoryRecord.command))!.value);\n        }\n    }\n\n    setValue(value: string): void {\n        this.editor.setValue(value);\n        this.editor.setPosition({lineNumber: 1, column: value.length + 1});\n        this.prompt.setValue(value);\n        this.focus();\n    }\n\n    insertValueInPlace(value: string): void {\n        this.editor.trigger(\"keyboard\", \"type\", {text: value});\n        this.focus();\n    }\n\n    private async execute(): Promise<void> {\n        let promptText = this.editor.getValue();\n        this.prompt.setValue(promptText);\n\n        if (!this.isEmpty()) {\n            this.props.session.createJob(this.prompt);\n            this.editor.setValue(\"\");\n            this.setState({\n                displayedHistoryRecordID: undefined,\n            });\n        }\n    }\n\n    private setPreviousHistoryItem(): void {\n        const currentID = this.state.displayedHistoryRecordID;\n        if (currentID) {\n            const currentRecord = services.history.get(currentID);\n            const previousRecord = _.findLast(\n                services.history.all,\n                record => record.id < currentID && record.command !== currentRecord.command,\n            );\n\n            if (previousRecord) {\n                this.setValue(previousRecord.command);\n                this.setState({displayedHistoryRecordID: previousRecord.id});\n            }\n        } else {\n            const previousRecord = services.history.latest;\n            if (previousRecord) {\n                this.setValue(previousRecord.command);\n                this.setState({displayedHistoryRecordID: previousRecord.id});\n            }\n        }\n    }\n\n    private setNextHistoryItem(): void {\n        const currentID = this.state.displayedHistoryRecordID;\n        if (currentID) {\n            const currentRecord = services.history.get(currentID);\n            const nextRecord = _.find(\n                services.history.all,\n                record => record.id > currentID && record.command !== currentRecord.command,\n            );\n            if (nextRecord) {\n                this.setValue(nextRecord.command);\n                this.setState({displayedHistoryRecordID: nextRecord.id});\n            } else {\n                this.setValue(\"\");\n                this.setState({displayedHistoryRecordID: undefined});\n            }\n        }\n    }\n\n    private get promptContentNode(): HTMLDivElement {\n        /* tslint:disable:no-string-literal */\n        return this.refs[\"prompt-content\"] as HTMLDivElement;\n    }\n\n    private isEmpty(): boolean {\n        return this.prompt.value.replace(/\\s/g, \"\").length === 0;\n    }\n\n    private triggerSuggest() {\n        this.editor.trigger(this.editor.getValue(), \"editor.action.triggerSuggest\", {});\n    }\n\n    private addShortcut(keybinding: number, handlerId: string) {\n        this.editor.addCommand(\n            keybinding,\n            () => this.editor.trigger(\"\", handlerId, {}),\n            \"\",\n        );\n    }\n\n    private unbindDefaultAction(handlerId: string) {\n        (this.editor as any)._standaloneKeybindingService.addDynamicKeybinding(`-${handlerId}`);\n    }\n}\n"
  },
  {
    "path": "src/views/SearchComponent.tsx",
    "content": "import * as React from \"react\";\nimport {remote} from \"electron\";\nimport {fontAwesome} from \"./css/FontAwesome\";\n\nexport class SearchComponent extends React.Component<{}, {}> {\n    private webContents: Electron.WebContents = remote.BrowserWindow.getAllWindows()[0].webContents;\n\n    constructor(props: any) {\n        super(props);\n        // FIXME: find a better design.\n        window.search = this;\n    }\n\n    render() {\n        return (\n            <div className=\"search\">\n                <span className=\"search-icon\">{fontAwesome.search}</span>\n                <input\n                    ref=\"input\"\n                    className=\"search-input\"\n                    onInput={(event: any) => this.handleInput(event)}\n                    type=\"search\"/>\n            </div>\n        );\n    }\n\n    get isFocused(): boolean {\n        return document.activeElement === this.input;\n    }\n\n    clearSelection(): void {\n        this.webContents.stopFindInPage(\"clearSelection\");\n        this.input.value = \"\";\n    }\n\n    blur() {\n        this.input.blur();\n    }\n\n    private handleInput(event: React.KeyboardEvent<HTMLInputElement>) {\n        const text = (event.target as HTMLInputElement).value;\n\n        if (text) {\n            this.webContents.findInPage(text);\n            this.webContents.on(\"found-in-page\", () => this.input.focus());\n        } else {\n            this.clearSelection();\n            setTimeout(() => this.input.select(), 0);\n        }\n    }\n\n    private get input(): HTMLInputElement {\n        /* tslint:disable:no-string-literal */\n        return this.refs[\"input\"] as HTMLInputElement;\n    }\n}\n"
  },
  {
    "path": "src/views/SessionComponent.tsx",
    "content": "import * as React from \"react\";\nimport * as _ from \"lodash\";\nimport {SessionID} from \"../shell/Session\";\nimport {Job} from \"../shell/Job\";\nimport {JobComponent} from \"./JobComponent\";\nimport * as css from \"./css/styles\";\nimport {PromptComponent} from \"./PromptComponent\";\nimport {userFriendlyPath} from \"../utils/Common\";\nimport {shell} from \"electron\";\nimport {services} from \"../services/index\";\nimport {colors} from \"./css/colors\";\nimport {Subscription} from \"rxjs/Subscription\";\n\ninterface Props {\n    sessionID: SessionID;\n    isFocused: boolean;\n    focus: () => void;\n}\n\nexport class SessionComponent extends React.Component<Props, {}> {\n    RENDER_JOBS_COUNT = 10;\n    promptComponent: PromptComponent;\n\n    constructor(props: Props) {\n        super(props);\n    }\n\n    componentDidMount() {\n        this.resizeSession();\n        this.session.on(\"jobs-changed\", () => this.forceUpdate());\n    }\n\n    render() {\n        const jobs = _.takeRight(this.session.jobs, this.RENDER_JOBS_COUNT).slice().reverse().map((job: Job, index: number) =>\n            <JobComponent\n                key={job.id}\n                job={job}\n                jobStatus={job.status}\n                isFocused={this.props.isFocused && index === this.session.jobs.length - 1}\n            />,\n        );\n\n        return (\n            <div className=\"session\"\n                 data-status={this.status}\n                 ref=\"session\"\n                 onClick={this.handleClick.bind(this)}>\n\n                <div className=\"jobs\">\n                    {jobs}\n                </div>\n                {this.props.isFocused ? null : <div className=\"shutter\"/>}\n                <PromptComponent\n                    ref={component => this.promptComponent = component!}\n                    session={this.session}\n                    isFocused={this.props.isFocused}\n                />\n                <div className=\"footer\" ref=\"footer\">\n                    <span className=\"present-directory\">{userFriendlyPath(this.session.directory)}</span>\n                    <GitStatusComponent directory={this.session.directory}/>\n                    <ReleaseComponent/>\n                </div>\n            </div>\n        );\n    }\n\n    resizeSession(): void {\n        this.session.dimensions = {\n            columns: Math.floor(this.size.width / services.font.letterWidth),\n            rows: Math.floor(this.size.height / services.font.letterHeight),\n        };\n    }\n\n    get status() {\n        const job = this.session.lastJob;\n        return job && job.status;\n    }\n\n    private get session() {\n        return services.sessions.get(this.props.sessionID);\n    }\n\n    private get sessionRef() {\n        return this.refs.session as HTMLDivElement | undefined;\n    }\n\n    private get footerRef() {\n        return this.refs.footer as HTMLDivElement | undefined;\n    }\n\n    private handleClick() {\n        if (!this.props.isFocused) {\n            this.props.focus();\n        }\n    }\n\n    private get size(): Size {\n        if (this.sessionRef && this.footerRef) {\n            return {\n                width: this.sessionRef.clientWidth - (2 * css.contentPadding),\n                height: this.sessionRef.clientHeight - this.footerRef.clientHeight,\n            };\n        } else {\n            // For tests that are run in electron-mocha\n            return {\n                width: 800,\n                height: 600,\n            };\n        }\n    }\n}\n\ntype GitStatusProps = { directory: string };\n\nclass GitStatusComponent extends React.Component<GitStatusProps, GitState> {\n    private subscription: Subscription;\n\n    constructor(props: GitStatusProps) {\n        super(props);\n\n        this.state = {\n            kind: \"not-repository\",\n        };\n    }\n\n    componentDidMount() {\n        this.subscribe(this.props.directory);\n    }\n\n    componentWillUpdate(nextProps: GitStatusProps) {\n        if (this.props.directory !== nextProps.directory) {\n            this.subscription.unsubscribe();\n            this.subscribe(nextProps.directory);\n        }\n    }\n\n    componentWillUnmount() {\n        this.subscription.unsubscribe();\n    }\n\n    render() {\n        if (this.state.kind === \"repository\") {\n            return (\n                <span className=\"vcs-data\" style={{color: this.state.status === \"dirty\" ? colors.blue : colors.white}}>\n                {this.state.branch}\n            </span>\n            );\n        } else {\n            return null;\n        }\n    }\n\n    private subscribe(directory: string) {\n        this.subscription = services.git.observableFor(directory).subscribe((data: GitState) => {\n            this.setState(data);\n        });\n    }\n}\n\nconst ReleaseComponent = () => {\n    if (process.env.NODE_ENV === \"production\" && services.updates.isAvailable) {\n        return (\n            <span\n                className=\"release-component-link\"\n                onClick={() => shell.openExternal(\"http://l.rw.rw/upterm_releases\")}>\n                Download New Release\n            </span>\n        );\n    } else {\n        return null;\n    }\n};\n"
  },
  {
    "path": "src/views/TabComponent.tsx",
    "content": "import {SessionComponent} from \"./SessionComponent\";\nimport * as React from \"react\";\nimport {SessionID} from \"../shell/Session\";\n\ntype Props = {\n    sessionIDs: SessionID[];\n    focusedSessionID: SessionID;\n    isFocused: boolean;\n    onSessionFocus: (id: SessionID) => void\n};\n\nexport class TabComponent extends React.Component<Props, {}> {\n    sessionComponents: SessionComponent[];\n    focusedSessionComponent: SessionComponent | undefined;\n\n    render() {\n        this.sessionComponents = [];\n        const sessionComponents = this.props.sessionIDs.map((id, index) => {\n            const isFocused = this.props.isFocused && id === this.props.focusedSessionID;\n\n            return (\n                <SessionComponent\n                    sessionID={id}\n                    key={id}\n                    ref={sessionComponent => {\n                        // Unmount.\n                        if (!sessionComponent) {\n                            return;\n                        }\n\n                        if (isFocused) {\n                            this.focusedSessionComponent = sessionComponent!;\n                        }\n                        this.sessionComponents[index] = sessionComponent!;\n                    }}\n                    isFocused={isFocused}\n                    focus={() => {\n                        this.props.onSessionFocus(id);\n                        this.forceUpdate();\n                    }}>\n                </SessionComponent>\n            );\n        });\n\n        return (\n            <div className=\"tab\" data-focused={this.props.isFocused}>\n                <div className=\"sessions\" data-side-by-side={this.props.sessionIDs.length === 2}>\n                    {sessionComponents}\n                </div>\n            </div>\n        );\n    }\n}\n"
  },
  {
    "path": "src/views/TabHeaderComponent.tsx",
    "content": "/* tslint:disable:no-unused-variable */\nimport * as React from \"react\";\nimport {fontAwesome} from \"./css/FontAwesome\";\n\nexport interface Props {\n    isFocused: boolean;\n    activate: () => void;\n    position: number;\n    closeHandler: React.EventHandler<React.MouseEvent<HTMLSpanElement>>;\n}\n\nexport class TabHeaderComponent extends React.Component<Props, {}> {\n    render() {\n        return (\n            <li className=\"tab-header\"\n                data-focused={this.props.isFocused}\n                onClick={this.props.activate}>\n\n                <span className=\"close-button\"\n                      onClick={this.props.closeHandler}>\n                    {fontAwesome.times}\n                </span>\n\n                <span>⌘{this.props.position}</span>\n            </li>\n        );\n    }\n}\n"
  },
  {
    "path": "src/views/ViewUtils.ts",
    "content": "import {KeyCode} from \"../Enums\";\n\nexport function isModifierKey(event: KeyboardEvent) {\n    return [KeyCode.Shift, KeyCode.Ctrl, KeyCode.Meta, KeyCode.Alt, KeyCode.CapsLock, KeyCode.AltGraph].includes(event.keyCode);\n}\n\nexport function setCaretPosition(node: Node, position: number) {\n    const selection = window.getSelection();\n    const range = document.createRange();\n\n    if (node.childNodes.length) {\n        range.setStart(node.childNodes[0], position);\n    } else {\n        range.setStart(node, 0);\n    }\n    range.collapse(true);\n    selection.removeAllRanges();\n    selection.addRange(range);\n}\n\n/**\n * @link http://stackoverflow.com/questions/4811822/get-a-ranges-start-and-end-offsets-relative-to-its-parent-container/4812022#4812022\n */\nexport function getCaretPosition(element: Node): number {\n    const selection = element.ownerDocument.defaultView.getSelection();\n\n    if (selection.rangeCount > 0) {\n        const range = selection.getRangeAt(0);\n        const preCaretRange = range.cloneRange();\n        preCaretRange.selectNodeContents(element);\n\n        return preCaretRange.toString().length;\n    } else {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/views/css/FontAwesome.ts",
    "content": "export const fontAwesome = {\n    adjust: \"\\uf042\",\n    adn: \"\\uf170\",\n    alignCenter: \"\\uf037\",\n    alignJustify: \"\\uf039\",\n    alignLeft: \"\\uf036\",\n    alignRight: \"\\uf038\",\n    amazon: \"\\uf270\",\n    ambulance: \"\\uf0f9\",\n    americanSignLanguageInterpreting: \"\\uf2a3\",\n    anchor: \"\\uf13d\",\n    android: \"\\uf17b\",\n    angellist: \"\\uf209\",\n    angleDoubleDown: \"\\uf103\",\n    angleDoubleLeft: \"\\uf100\",\n    angleDoubleRight: \"\\uf101\",\n    angleDoubleUp: \"\\uf102\",\n    angleDown: \"\\uf107\",\n    angleLeft: \"\\uf104\",\n    angleRight: \"\\uf105\",\n    angleUp: \"\\uf106\",\n    apple: \"\\uf179\",\n    archive: \"\\uf187\",\n    areaChart: \"\\uf1fe\",\n    arrowCircleDown: \"\\uf0ab\",\n    arrowCircleLeft: \"\\uf0a8\",\n    arrowCircleODown: \"\\uf01a\",\n    arrowCircleOLeft: \"\\uf190\",\n    arrowCircleORight: \"\\uf18e\",\n    arrowCircleOUp: \"\\uf01b\",\n    arrowCircleRight: \"\\uf0a9\",\n    arrowCircleUp: \"\\uf0aa\",\n    arrowDown: \"\\uf063\",\n    arrowLeft: \"\\uf060\",\n    arrowRight: \"\\uf061\",\n    arrowUp: \"\\uf062\",\n    arrows: \"\\uf047\",\n    arrowsAlt: \"\\uf0b2\",\n    arrowsH: \"\\uf07e\",\n    arrowsV: \"\\uf07d\",\n    aslInterpreting: \"\\uf2a3\",\n    assistiveListeningSystems: \"\\uf2a2\",\n    asterisk: \"\\uf069\",\n    at: \"\\uf1fa\",\n    audioDescription: \"\\uf29e\",\n    automobile: \"\\uf1b9\",\n    backward: \"\\uf04a\",\n    balanceScale: \"\\uf24e\",\n    ban: \"\\uf05e\",\n    bank: \"\\uf19c\",\n    barChart: \"\\uf080\",\n    barChartO: \"\\uf080\",\n    barcode: \"\\uf02a\",\n    bars: \"\\uf0c9\",\n    battery0: \"\\uf244\",\n    battery1: \"\\uf243\",\n    battery2: \"\\uf242\",\n    battery3: \"\\uf241\",\n    battery4: \"\\uf240\",\n    batteryEmpty: \"\\uf244\",\n    batteryFull: \"\\uf240\",\n    batteryHalf: \"\\uf242\",\n    batteryQuarter: \"\\uf243\",\n    batteryThreeQuarters: \"\\uf241\",\n    bed: \"\\uf236\",\n    beer: \"\\uf0fc\",\n    behance: \"\\uf1b4\",\n    behanceSquare: \"\\uf1b5\",\n    bell: \"\\uf0f3\",\n    bellO: \"\\uf0a2\",\n    bellSlash: \"\\uf1f6\",\n    bellSlashO: \"\\uf1f7\",\n    bicycle: \"\\uf206\",\n    binoculars: \"\\uf1e5\",\n    birthdayCake: \"\\uf1fd\",\n    bitbucket: \"\\uf171\",\n    bitbucketSquare: \"\\uf172\",\n    bitcoin: \"\\uf15a\",\n    blackTie: \"\\uf27e\",\n    blind: \"\\uf29d\",\n    bluetooth: \"\\uf293\",\n    bluetoothB: \"\\uf294\",\n    bold: \"\\uf032\",\n    bolt: \"\\uf0e7\",\n    bomb: \"\\uf1e2\",\n    book: \"\\uf02d\",\n    bookmark: \"\\uf02e\",\n    bookmarkO: \"\\uf097\",\n    braille: \"\\uf2a1\",\n    briefcase: \"\\uf0b1\",\n    btc: \"\\uf15a\",\n    bug: \"\\uf188\",\n    building: \"\\uf1ad\",\n    buildingO: \"\\uf0f7\",\n    bullhorn: \"\\uf0a1\",\n    bullseye: \"\\uf140\",\n    bus: \"\\uf207\",\n    buysellads: \"\\uf20d\",\n    cab: \"\\uf1ba\",\n    calculator: \"\\uf1ec\",\n    calendar: \"\\uf073\",\n    calendarCheckO: \"\\uf274\",\n    calendarMinusO: \"\\uf272\",\n    calendarO: \"\\uf133\",\n    calendarPlusO: \"\\uf271\",\n    calendarTimesO: \"\\uf273\",\n    camera: \"\\uf030\",\n    cameraRetro: \"\\uf083\",\n    car: \"\\uf1b9\",\n    caretDown: \"\\uf0d7\",\n    caretLeft: \"\\uf0d9\",\n    caretRight: \"\\uf0da\",\n    caretSquareODown: \"\\uf150\",\n    caretSquareOLeft: \"\\uf191\",\n    caretSquareORight: \"\\uf152\",\n    caretSquareOUp: \"\\uf151\",\n    caretUp: \"\\uf0d8\",\n    cartArrowDown: \"\\uf218\",\n    cartPlus: \"\\uf217\",\n    cc: \"\\uf20a\",\n    ccAmex: \"\\uf1f3\",\n    ccDinersClub: \"\\uf24c\",\n    ccDiscover: \"\\uf1f2\",\n    ccJcb: \"\\uf24b\",\n    ccMastercard: \"\\uf1f1\",\n    ccPaypal: \"\\uf1f4\",\n    ccStripe: \"\\uf1f5\",\n    ccVisa: \"\\uf1f0\",\n    certificate: \"\\uf0a3\",\n    chain: \"\\uf0c1\",\n    chainBroken: \"\\uf127\",\n    check: \"\\uf00c\",\n    checkCircle: \"\\uf058\",\n    checkCircleO: \"\\uf05d\",\n    checkSquare: \"\\uf14a\",\n    checkSquareO: \"\\uf046\",\n    chevronCircleDown: \"\\uf13a\",\n    chevronCircleLeft: \"\\uf137\",\n    chevronCircleRight: \"\\uf138\",\n    chevronCircleUp: \"\\uf139\",\n    chevronDown: \"\\uf078\",\n    chevronLeft: \"\\uf053\",\n    chevronRight: \"\\uf054\",\n    chevronUp: \"\\uf077\",\n    child: \"\\uf1ae\",\n    chrome: \"\\uf268\",\n    circle: \"\\uf111\",\n    circleO: \"\\uf10c\",\n    circleONotch: \"\\uf1ce\",\n    circleThin: \"\\uf1db\",\n    clipboard: \"\\uf0ea\",\n    clockO: \"\\uf017\",\n    clone: \"\\uf24d\",\n    close: \"\\uf00d\",\n    cloud: \"\\uf0c2\",\n    cloudDownload: \"\\uf0ed\",\n    cloudUpload: \"\\uf0ee\",\n    cny: \"\\uf157\",\n    code: \"\\uf121\",\n    codeFork: \"\\uf126\",\n    codepen: \"\\uf1cb\",\n    codiepie: \"\\uf284\",\n    coffee: \"\\uf0f4\",\n    cog: \"\\uf013\",\n    cogs: \"\\uf085\",\n    columns: \"\\uf0db\",\n    comment: \"\\uf075\",\n    commentO: \"\\uf0e5\",\n    commenting: \"\\uf27a\",\n    commentingO: \"\\uf27b\",\n    comments: \"\\uf086\",\n    commentsO: \"\\uf0e6\",\n    compass: \"\\uf14e\",\n    compress: \"\\uf066\",\n    connectdevelop: \"\\uf20e\",\n    contao: \"\\uf26d\",\n    copy: \"\\uf0c5\",\n    copyright: \"\\uf1f9\",\n    creativeCommons: \"\\uf25e\",\n    creditCard: \"\\uf09d\",\n    creditCardAlt: \"\\uf283\",\n    crop: \"\\uf125\",\n    crosshairs: \"\\uf05b\",\n    css3: \"\\uf13c\",\n    cube: \"\\uf1b2\",\n    cubes: \"\\uf1b3\",\n    cut: \"\\uf0c4\",\n    cutlery: \"\\uf0f5\",\n    dashboard: \"\\uf0e4\",\n    dashcube: \"\\uf210\",\n    database: \"\\uf1c0\",\n    deaf: \"\\uf2a4\",\n    deafness: \"\\uf2a4\",\n    dedent: \"\\uf03b\",\n    delicious: \"\\uf1a5\",\n    desktop: \"\\uf108\",\n    deviantart: \"\\uf1bd\",\n    diamond: \"\\uf219\",\n    digg: \"\\uf1a6\",\n    dollar: \"\\uf155\",\n    dotCircleO: \"\\uf192\",\n    download: \"\\uf019\",\n    dribbble: \"\\uf17d\",\n    dropbox: \"\\uf16b\",\n    drupal: \"\\uf1a9\",\n    edge: \"\\uf282\",\n    edit: \"\\uf044\",\n    eject: \"\\uf052\",\n    ellipsisH: \"\\uf141\",\n    ellipsisV: \"\\uf142\",\n    empire: \"\\uf1d1\",\n    envelope: \"\\uf0e0\",\n    envelopeO: \"\\uf003\",\n    envelopeSquare: \"\\uf199\",\n    envira: \"\\uf299\",\n    eraser: \"\\uf12d\",\n    eur: \"\\uf153\",\n    euro: \"\\uf153\",\n    exchange: \"\\uf0ec\",\n    exclamation: \"\\uf12a\",\n    exclamationCircle: \"\\uf06a\",\n    exclamationTriangle: \"\\uf071\",\n    expand: \"\\uf065\",\n    expeditedssl: \"\\uf23e\",\n    externalLink: \"\\uf08e\",\n    externalLinkSquare: \"\\uf14c\",\n    eye: \"\\uf06e\",\n    eyeSlash: \"\\uf070\",\n    eyedropper: \"\\uf1fb\",\n    fa: \"\\uf2b4\",\n    facebook: \"\\uf09a\",\n    facebookF: \"\\uf09a\",\n    facebookOfficial: \"\\uf230\",\n    facebookSquare: \"\\uf082\",\n    fastBackward: \"\\uf049\",\n    fastForward: \"\\uf050\",\n    fax: \"\\uf1ac\",\n    feed: \"\\uf09e\",\n    female: \"\\uf182\",\n    fighterJet: \"\\uf0fb\",\n    file: \"\\uf15b\",\n    fileArchiveO: \"\\uf1c6\",\n    fileAudioO: \"\\uf1c7\",\n    fileCodeO: \"\\uf1c9\",\n    fileExcelO: \"\\uf1c3\",\n    fileImageO: \"\\uf1c5\",\n    fileMovieO: \"\\uf1c8\",\n    fileO: \"\\uf016\",\n    filePdfO: \"\\uf1c1\",\n    filePhotoO: \"\\uf1c5\",\n    filePictureO: \"\\uf1c5\",\n    filePowerpointO: \"\\uf1c4\",\n    fileSoundO: \"\\uf1c7\",\n    fileText: \"\\uf15c\",\n    fileTextO: \"\\uf0f6\",\n    fileVideoO: \"\\uf1c8\",\n    fileWordO: \"\\uf1c2\",\n    fileZipO: \"\\uf1c6\",\n    filesO: \"\\uf0c5\",\n    film: \"\\uf008\",\n    filter: \"\\uf0b0\",\n    fire: \"\\uf06d\",\n    fireExtinguisher: \"\\uf134\",\n    firefox: \"\\uf269\",\n    firstOrder: \"\\uf2b0\",\n    flag: \"\\uf024\",\n    flagCheckered: \"\\uf11e\",\n    flagO: \"\\uf11d\",\n    flash: \"\\uf0e7\",\n    flask: \"\\uf0c3\",\n    flickr: \"\\uf16e\",\n    floppyO: \"\\uf0c7\",\n    folder: \"\\uf07b\",\n    folderO: \"\\uf114\",\n    folderOpen: \"\\uf07c\",\n    folderOpenO: \"\\uf115\",\n    font: \"\\uf031\",\n    fontAwesome: \"\\uf2b4\",\n    fonticons: \"\\uf280\",\n    fortAwesome: \"\\uf286\",\n    forumbee: \"\\uf211\",\n    forward: \"\\uf04e\",\n    foursquare: \"\\uf180\",\n    frownO: \"\\uf119\",\n    futbolO: \"\\uf1e3\",\n    gamepad: \"\\uf11b\",\n    gavel: \"\\uf0e3\",\n    gbp: \"\\uf154\",\n    ge: \"\\uf1d1\",\n    gear: \"\\uf013\",\n    gears: \"\\uf085\",\n    genderless: \"\\uf22d\",\n    getPocket: \"\\uf265\",\n    gg: \"\\uf260\",\n    ggCircle: \"\\uf261\",\n    gift: \"\\uf06b\",\n    git: \"\\uf1d3\",\n    gitSquare: \"\\uf1d2\",\n    github: \"\\uf09b\",\n    githubAlt: \"\\uf113\",\n    githubSquare: \"\\uf092\",\n    gitlab: \"\\uf296\",\n    gittip: \"\\uf184\",\n    glass: \"\\uf000\",\n    glide: \"\\uf2a5\",\n    glideG: \"\\uf2a6\",\n    globe: \"\\uf0ac\",\n    google: \"\\uf1a0\",\n    googlePlus: \"\\uf0d5\",\n    googlePlusCircle: \"\\uf2b3\",\n    googlePlusOfficial: \"\\uf2b3\",\n    googlePlusSquare: \"\\uf0d4\",\n    googleWallet: \"\\uf1ee\",\n    graduationCap: \"\\uf19d\",\n    gratipay: \"\\uf184\",\n    group: \"\\uf0c0\",\n    hSquare: \"\\uf0fd\",\n    hackerNews: \"\\uf1d4\",\n    handGrabO: \"\\uf255\",\n    handLizardO: \"\\uf258\",\n    handODown: \"\\uf0a7\",\n    handOLeft: \"\\uf0a5\",\n    handORight: \"\\uf0a4\",\n    handOUp: \"\\uf0a6\",\n    handPaperO: \"\\uf256\",\n    handPeaceO: \"\\uf25b\",\n    handPointerO: \"\\uf25a\",\n    handRockO: \"\\uf255\",\n    handScissorsO: \"\\uf257\",\n    handSpockO: \"\\uf259\",\n    handStopO: \"\\uf256\",\n    hardOfHearing: \"\\uf2a4\",\n    hashtag: \"\\uf292\",\n    hddO: \"\\uf0a0\",\n    header: \"\\uf1dc\",\n    headphones: \"\\uf025\",\n    heart: \"\\uf004\",\n    heartO: \"\\uf08a\",\n    heartbeat: \"\\uf21e\",\n    history: \"\\uf1da\",\n    home: \"\\uf015\",\n    hospitalO: \"\\uf0f8\",\n    hotel: \"\\uf236\",\n    hourglass: \"\\uf254\",\n    hourglass1: \"\\uf251\",\n    hourglass2: \"\\uf252\",\n    hourglass3: \"\\uf253\",\n    hourglassEnd: \"\\uf253\",\n    hourglassHalf: \"\\uf252\",\n    hourglassO: \"\\uf250\",\n    hourglassStart: \"\\uf251\",\n    houzz: \"\\uf27c\",\n    html5: \"\\uf13b\",\n    iCursor: \"\\uf246\",\n    ils: \"\\uf20b\",\n    image: \"\\uf03e\",\n    inbox: \"\\uf01c\",\n    indent: \"\\uf03c\",\n    industry: \"\\uf275\",\n    info: \"\\uf129\",\n    infoCircle: \"\\uf05a\",\n    inr: \"\\uf156\",\n    instagram: \"\\uf16d\",\n    institution: \"\\uf19c\",\n    internetExplorer: \"\\uf26b\",\n    intersex: \"\\uf224\",\n    ioxhost: \"\\uf208\",\n    italic: \"\\uf033\",\n    joomla: \"\\uf1aa\",\n    jpy: \"\\uf157\",\n    jsfiddle: \"\\uf1cc\",\n    key: \"\\uf084\",\n    keyboardO: \"\\uf11c\",\n    krw: \"\\uf159\",\n    language: \"\\uf1ab\",\n    laptop: \"\\uf109\",\n    lastfm: \"\\uf202\",\n    lastfmSquare: \"\\uf203\",\n    leaf: \"\\uf06c\",\n    leanpub: \"\\uf212\",\n    legal: \"\\uf0e3\",\n    lemonO: \"\\uf094\",\n    levelDown: \"\\uf149\",\n    levelUp: \"\\uf148\",\n    lifeBouy: \"\\uf1cd\",\n    lifeBuoy: \"\\uf1cd\",\n    lifeRing: \"\\uf1cd\",\n    lifeSaver: \"\\uf1cd\",\n    lightbulbO: \"\\uf0eb\",\n    lineChart: \"\\uf201\",\n    link: \"\\uf0c1\",\n    linkedin: \"\\uf0e1\",\n    linkedinSquare: \"\\uf08c\",\n    linux: \"\\uf17c\",\n    list: \"\\uf03a\",\n    listAlt: \"\\uf022\",\n    listOl: \"\\uf0cb\",\n    listUl: \"\\uf0ca\",\n    locationArrow: \"\\uf124\",\n    lock: \"\\uf023\",\n    longArrowDown: \"\\uf175\",\n    longArrowLeft: \"\\uf177\",\n    longArrowRight: \"\\uf178\",\n    longArrowUp: \"\\uf176\",\n    lowVision: \"\\uf2a8\",\n    magic: \"\\uf0d0\",\n    magnet: \"\\uf076\",\n    mailForward: \"\\uf064\",\n    mailReply: \"\\uf112\",\n    mailReplyAll: \"\\uf122\",\n    male: \"\\uf183\",\n    map: \"\\uf279\",\n    mapMarker: \"\\uf041\",\n    mapO: \"\\uf278\",\n    mapPin: \"\\uf276\",\n    mapSigns: \"\\uf277\",\n    mars: \"\\uf222\",\n    marsDouble: \"\\uf227\",\n    marsStroke: \"\\uf229\",\n    marsStrokeH: \"\\uf22b\",\n    marsStrokeV: \"\\uf22a\",\n    maxcdn: \"\\uf136\",\n    meanpath: \"\\uf20c\",\n    medium: \"\\uf23a\",\n    medkit: \"\\uf0fa\",\n    mehO: \"\\uf11a\",\n    mercury: \"\\uf223\",\n    microphone: \"\\uf130\",\n    microphoneSlash: \"\\uf131\",\n    minus: \"\\uf068\",\n    minusCircle: \"\\uf056\",\n    minusSquare: \"\\uf146\",\n    minusSquareO: \"\\uf147\",\n    mixcloud: \"\\uf289\",\n    mobile: \"\\uf10b\",\n    mobilePhone: \"\\uf10b\",\n    modx: \"\\uf285\",\n    money: \"\\uf0d6\",\n    moonO: \"\\uf186\",\n    mortarBoard: \"\\uf19d\",\n    motorcycle: \"\\uf21c\",\n    mousePointer: \"\\uf245\",\n    music: \"\\uf001\",\n    navicon: \"\\uf0c9\",\n    neuter: \"\\uf22c\",\n    newspaperO: \"\\uf1ea\",\n    objectGroup: \"\\uf247\",\n    objectUngroup: \"\\uf248\",\n    odnoklassniki: \"\\uf263\",\n    odnoklassnikiSquare: \"\\uf264\",\n    opencart: \"\\uf23d\",\n    openid: \"\\uf19b\",\n    opera: \"\\uf26a\",\n    optinMonster: \"\\uf23c\",\n    outdent: \"\\uf03b\",\n    pagelines: \"\\uf18c\",\n    paintBrush: \"\\uf1fc\",\n    paperPlane: \"\\uf1d8\",\n    paperPlaneO: \"\\uf1d9\",\n    paperclip: \"\\uf0c6\",\n    paragraph: \"\\uf1dd\",\n    paste: \"\\uf0ea\",\n    pause: \"\\uf04c\",\n    pauseCircle: \"\\uf28b\",\n    pauseCircleO: \"\\uf28c\",\n    paw: \"\\uf1b0\",\n    paypal: \"\\uf1ed\",\n    pencil: \"\\uf040\",\n    pencilSquare: \"\\uf14b\",\n    pencilSquareO: \"\\uf044\",\n    percent: \"\\uf295\",\n    phone: \"\\uf095\",\n    phoneSquare: \"\\uf098\",\n    photo: \"\\uf03e\",\n    pictureO: \"\\uf03e\",\n    pieChart: \"\\uf200\",\n    piedPiper: \"\\uf2ae\",\n    piedPiperAlt: \"\\uf1a8\",\n    piedPiperPp: \"\\uf1a7\",\n    pinterest: \"\\uf0d2\",\n    pinterestP: \"\\uf231\",\n    pinterestSquare: \"\\uf0d3\",\n    plane: \"\\uf072\",\n    play: \"\\uf04b\",\n    playCircle: \"\\uf144\",\n    playCircleO: \"\\uf01d\",\n    plug: \"\\uf1e6\",\n    plus: \"\\uf067\",\n    plusCircle: \"\\uf055\",\n    plusSquare: \"\\uf0fe\",\n    plusSquareO: \"\\uf196\",\n    powerOff: \"\\uf011\",\n    print: \"\\uf02f\",\n    productHunt: \"\\uf288\",\n    puzzlePiece: \"\\uf12e\",\n    qq: \"\\uf1d6\",\n    qrcode: \"\\uf029\",\n    question: \"\\uf128\",\n    questionCircle: \"\\uf059\",\n    questionCircleO: \"\\uf29c\",\n    quoteLeft: \"\\uf10d\",\n    quoteRight: \"\\uf10e\",\n    ra: \"\\uf1d0\",\n    random: \"\\uf074\",\n    rebel: \"\\uf1d0\",\n    recycle: \"\\uf1b8\",\n    reddit: \"\\uf1a1\",\n    redditAlien: \"\\uf281\",\n    redditSquare: \"\\uf1a2\",\n    refresh: \"\\uf021\",\n    registered: \"\\uf25d\",\n    remove: \"\\uf00d\",\n    renren: \"\\uf18b\",\n    reorder: \"\\uf0c9\",\n    repeat: \"\\uf01e\",\n    reply: \"\\uf112\",\n    replyAll: \"\\uf122\",\n    resistance: \"\\uf1d0\",\n    retweet: \"\\uf079\",\n    rmb: \"\\uf157\",\n    road: \"\\uf018\",\n    rocket: \"\\uf135\",\n    rotateLeft: \"\\uf0e2\",\n    rotateRight: \"\\uf01e\",\n    rouble: \"\\uf158\",\n    rss: \"\\uf09e\",\n    rssSquare: \"\\uf143\",\n    rub: \"\\uf158\",\n    ruble: \"\\uf158\",\n    rupee: \"\\uf156\",\n    safari: \"\\uf267\",\n    save: \"\\uf0c7\",\n    scissors: \"\\uf0c4\",\n    scribd: \"\\uf28a\",\n    search: \"\\uf002\",\n    searchMinus: \"\\uf010\",\n    searchPlus: \"\\uf00e\",\n    sellsy: \"\\uf213\",\n    send: \"\\uf1d8\",\n    sendO: \"\\uf1d9\",\n    server: \"\\uf233\",\n    share: \"\\uf064\",\n    shareAlt: \"\\uf1e0\",\n    shareAltSquare: \"\\uf1e1\",\n    shareSquare: \"\\uf14d\",\n    shareSquareO: \"\\uf045\",\n    shekel: \"\\uf20b\",\n    sheqel: \"\\uf20b\",\n    shield: \"\\uf132\",\n    ship: \"\\uf21a\",\n    shirtsinbulk: \"\\uf214\",\n    shoppingBag: \"\\uf290\",\n    shoppingBasket: \"\\uf291\",\n    shoppingCart: \"\\uf07a\",\n    signIn: \"\\uf090\",\n    signLanguage: \"\\uf2a7\",\n    signOut: \"\\uf08b\",\n    signal: \"\\uf012\",\n    signing: \"\\uf2a7\",\n    simplybuilt: \"\\uf215\",\n    sitemap: \"\\uf0e8\",\n    skyatlas: \"\\uf216\",\n    skype: \"\\uf17e\",\n    slack: \"\\uf198\",\n    sliders: \"\\uf1de\",\n    slideshare: \"\\uf1e7\",\n    smileO: \"\\uf118\",\n    snapchat: \"\\uf2ab\",\n    snapchatGhost: \"\\uf2ac\",\n    snapchatSquare: \"\\uf2ad\",\n    soccerBallO: \"\\uf1e3\",\n    sort: \"\\uf0dc\",\n    sortAlphaAsc: \"\\uf15d\",\n    sortAlphaDesc: \"\\uf15e\",\n    sortAmountAsc: \"\\uf160\",\n    sortAmountDesc: \"\\uf161\",\n    sortAsc: \"\\uf0de\",\n    sortDesc: \"\\uf0dd\",\n    sortDown: \"\\uf0dd\",\n    sortNumericAsc: \"\\uf162\",\n    sortNumericDesc: \"\\uf163\",\n    sortUp: \"\\uf0de\",\n    soundcloud: \"\\uf1be\",\n    spaceShuttle: \"\\uf197\",\n    spinner: \"\\uf110\",\n    spoon: \"\\uf1b1\",\n    spotify: \"\\uf1bc\",\n    square: \"\\uf0c8\",\n    squareO: \"\\uf096\",\n    stackExchange: \"\\uf18d\",\n    stackOverflow: \"\\uf16c\",\n    star: \"\\uf005\",\n    starHalf: \"\\uf089\",\n    starHalfEmpty: \"\\uf123\",\n    starHalfFull: \"\\uf123\",\n    starHalfO: \"\\uf123\",\n    starO: \"\\uf006\",\n    steam: \"\\uf1b6\",\n    steamSquare: \"\\uf1b7\",\n    stepBackward: \"\\uf048\",\n    stepForward: \"\\uf051\",\n    stethoscope: \"\\uf0f1\",\n    stickyNote: \"\\uf249\",\n    stickyNoteO: \"\\uf24a\",\n    stop: \"\\uf04d\",\n    stopCircle: \"\\uf28d\",\n    stopCircleO: \"\\uf28e\",\n    streetView: \"\\uf21d\",\n    strikethrough: \"\\uf0cc\",\n    stumbleupon: \"\\uf1a4\",\n    stumbleuponCircle: \"\\uf1a3\",\n    subscript: \"\\uf12c\",\n    subway: \"\\uf239\",\n    suitcase: \"\\uf0f2\",\n    sunO: \"\\uf185\",\n    superscript: \"\\uf12b\",\n    support: \"\\uf1cd\",\n    table: \"\\uf0ce\",\n    tablet: \"\\uf10a\",\n    tachometer: \"\\uf0e4\",\n    tag: \"\\uf02b\",\n    tags: \"\\uf02c\",\n    tasks: \"\\uf0ae\",\n    taxi: \"\\uf1ba\",\n    television: \"\\uf26c\",\n    tencentWeibo: \"\\uf1d5\",\n    terminal: \"\\uf120\",\n    textHeight: \"\\uf034\",\n    textWidth: \"\\uf035\",\n    th: \"\\uf00a\",\n    thLarge: \"\\uf009\",\n    thList: \"\\uf00b\",\n    themeisle: \"\\uf2b2\",\n    thumbTack: \"\\uf08d\",\n    thumbsDown: \"\\uf165\",\n    thumbsODown: \"\\uf088\",\n    thumbsOUp: \"\\uf087\",\n    thumbsUp: \"\\uf164\",\n    ticket: \"\\uf145\",\n    times: \"\\uf00d\",\n    timesCircle: \"\\uf057\",\n    timesCircleO: \"\\uf05c\",\n    tint: \"\\uf043\",\n    toggleDown: \"\\uf150\",\n    toggleLeft: \"\\uf191\",\n    toggleOff: \"\\uf204\",\n    toggleOn: \"\\uf205\",\n    toggleRight: \"\\uf152\",\n    toggleUp: \"\\uf151\",\n    trademark: \"\\uf25c\",\n    train: \"\\uf238\",\n    transgender: \"\\uf224\",\n    transgenderAlt: \"\\uf225\",\n    trash: \"\\uf1f8\",\n    trashO: \"\\uf014\",\n    tree: \"\\uf1bb\",\n    trello: \"\\uf181\",\n    tripadvisor: \"\\uf262\",\n    trophy: \"\\uf091\",\n    truck: \"\\uf0d1\",\n    try: \"\\uf195\",\n    tty: \"\\uf1e4\",\n    tumblr: \"\\uf173\",\n    tumblrSquare: \"\\uf174\",\n    turkishLira: \"\\uf195\",\n    tv: \"\\uf26c\",\n    twitch: \"\\uf1e8\",\n    twitter: \"\\uf099\",\n    twitterSquare: \"\\uf081\",\n    umbrella: \"\\uf0e9\",\n    underline: \"\\uf0cd\",\n    undo: \"\\uf0e2\",\n    universalAccess: \"\\uf29a\",\n    university: \"\\uf19c\",\n    unlink: \"\\uf127\",\n    unlock: \"\\uf09c\",\n    unlockAlt: \"\\uf13e\",\n    unsorted: \"\\uf0dc\",\n    upload: \"\\uf093\",\n    usb: \"\\uf287\",\n    usd: \"\\uf155\",\n    user: \"\\uf007\",\n    userMd: \"\\uf0f0\",\n    userPlus: \"\\uf234\",\n    userSecret: \"\\uf21b\",\n    userTimes: \"\\uf235\",\n    users: \"\\uf0c0\",\n    venus: \"\\uf221\",\n    venusDouble: \"\\uf226\",\n    venusMars: \"\\uf228\",\n    viacoin: \"\\uf237\",\n    viadeo: \"\\uf2a9\",\n    viadeoSquare: \"\\uf2aa\",\n    videoCamera: \"\\uf03d\",\n    vimeo: \"\\uf27d\",\n    vimeoSquare: \"\\uf194\",\n    vine: \"\\uf1ca\",\n    vk: \"\\uf189\",\n    volumeControlPhone: \"\\uf2a0\",\n    volumeDown: \"\\uf027\",\n    volumeOff: \"\\uf026\",\n    volumeUp: \"\\uf028\",\n    warning: \"\\uf071\",\n    wechat: \"\\uf1d7\",\n    weibo: \"\\uf18a\",\n    weixin: \"\\uf1d7\",\n    whatsapp: \"\\uf232\",\n    wheelchair: \"\\uf193\",\n    wheelchairAlt: \"\\uf29b\",\n    wifi: \"\\uf1eb\",\n    wikipediaW: \"\\uf266\",\n    windows: \"\\uf17a\",\n    won: \"\\uf159\",\n    wordpress: \"\\uf19a\",\n    wpbeginner: \"\\uf297\",\n    wpforms: \"\\uf298\",\n    wrench: \"\\uf0ad\",\n    xing: \"\\uf168\",\n    xingSquare: \"\\uf169\",\n    yCombinator: \"\\uf23b\",\n    yCombinatorSquare: \"\\uf1d4\",\n    yahoo: \"\\uf19e\",\n    yc: \"\\uf23b\",\n    ycSquare: \"\\uf1d4\",\n    yelp: \"\\uf1e9\",\n    yen: \"\\uf157\",\n    yoast: \"\\uf2b1\",\n    youtube: \"\\uf167\",\n    youtubePlay: \"\\uf16a\",\n    youtubeSquare: \"\\uf166\",\n};\n"
  },
  {
    "path": "src/views/css/colors.ts",
    "content": "import * as _ from \"lodash\";\nimport {ColorCode} from \"../../Interfaces\";\nimport {darken} from \"./functions\";\n\nexport const colors = {\n    black: \"#333\",\n    red: \"#BF6E7C\",\n    white: \"#999\",\n    green: \"#88B379\",\n    yellow: \"#D9BD86\",\n    blue: \"#66A5DF\",\n    magenta: \"#C699C5\",\n    cyan: \"#6EC6C6\",\n\n    brightBlack: \"#484c54\",\n    brightRed: \"#dd8494\",\n    brightWhite: \"#bccce8\",\n    brightGreen: \"#9dcc8c\",\n    brightYellow: \"#e9cc92\",\n    brightBlue: \"#6cb2f0\",\n    brightMagenta: \"#e8b6e7\",\n    brightCyan: \"#7adada\",\n};\n\nconst colorIndex = [\n    colors.black,\n    colors.red,\n    colors.green,\n    colors.yellow,\n    colors.blue,\n    colors.magenta,\n    colors.cyan,\n    colors.white,\n\n    colors.brightBlack,\n    colors.brightRed,\n    colors.brightGreen,\n    colors.brightYellow,\n    colors.brightBlue,\n    colors.brightMagenta,\n    colors.brightCyan,\n    colors.brightWhite,\n\n    ...generateIndexedColors(),\n    ...generateGreyScaleColors(),\n];\n\nfunction toRgb(colorComponent: number) {\n    if (colorComponent === 0) {\n        return 0;\n    }\n\n    return 55 + colorComponent * 40;\n}\n\nfunction generateIndexedColors() {\n    return _.range(0, 216).map(index => {\n        const red = Math.floor(index / 36);\n        const green = Math.floor((index % 36) / 6);\n        const blue = Math.floor(index % 6);\n\n        return `rgb(${toRgb(red)}, ${toRgb(green)}, ${toRgb(blue)})`;\n    });\n}\n\nfunction generateGreyScaleColors() {\n    return _.range(0, 24).map(index => {\n        const color = index * 10 + 8;\n        return `rgb(${color}, ${color}, ${color})`;\n    });\n}\n\nexport function colorValue(color: ColorCode, options = {isBright: false}) {\n    if (Array.isArray(color)) {\n        return `rgb(${color.join(\", \")})`;\n    } else {\n        if (options.isBright && color < 8) {\n            return colorIndex[color + 8];\n        } else {\n            return  colorIndex[color];\n        }\n    }\n}\nexport const textColor = \"#EEEEEE\";\nexport const backgroundColor = darken(colors.black, 4);\n\n"
  },
  {
    "path": "src/views/css/functions.ts",
    "content": "const tinyColor: any = require(\"tinycolor2\");\n\nexport function lighten(color: string, percent: number) {\n    return tinyColor(color).lighten(percent).toHexString();\n}\n\nexport function darken(color: string, percent: number) {\n    return tinyColor(color).darken(percent).toHexString();\n}\n\nexport function failurize(color: string) {\n    return tinyColor(color).spin(140).saturate(25).toHexString();\n}\n"
  },
  {
    "path": "src/views/css/styles.ts",
    "content": "import {Weight, Brightness, Color} from \"../../Enums\";\nimport {backgroundColor, colors, colorValue, textColor} from \"./colors\";\nimport {failurize, lighten} from \"./functions\";\nimport {Attributes} from \"../../Interfaces\";\nimport {services} from \"../../services/index\";\n\nconst jobBackgroundColor = colors.black;\nexport const contentPadding = 10;\n\nexport const application = () => ({\n    \"--font-size\": `${services.font.size}px`,\n    \"--font-family\": services.font.family,\n    \"--letter-width\": `${services.font.letterWidth}px`,\n    \"--letter-height\": `${services.font.letterHeight}px`,\n    \"--content-padding\": `${contentPadding}px`,\n    \"--search-input-color\": lighten(backgroundColor, 15),\n    \"--background-color\": backgroundColor,\n    \"--job-background-color\": jobBackgroundColor,\n    \"--failed-job-background-color\": failurize(jobBackgroundColor),\n    \"--text-color\": textColor,\n\n    \"--black-color\": colors.black,\n    \"--red-color\": colors.red,\n    \"--white-color\": colors.white,\n    \"--green-color\": colors.green,\n    \"--yellow-color\": colors.yellow,\n    \"--blue-color\": colors.blue,\n    \"--magenta-color\": colors.magenta,\n    \"--cyan-color\": colors.cyan,\n});\n\nexport const charGroup = (attributes: Attributes) => {\n    const styles: any = {\n        color: colorValue(attributes.color, {isBright: attributes.brightness === Brightness.Bright}),\n        backgroundColor: colorValue(attributes.backgroundColor, {isBright: false}),\n    };\n\n    if (attributes.inverse) {\n        const color = styles.color;\n\n        styles.color = styles.backgroundColor;\n        styles.backgroundColor = color;\n    }\n\n    if (attributes.underline) {\n        styles.textDecoration = \"underline\";\n    }\n\n    if (attributes.weight === Weight.Bold) {\n        styles.fontWeight = \"bold\";\n    }\n\n    // Remove default colors to allow CSS override for failed commands and reverse mode.\n    if (attributes.color === Color.White && !attributes.inverse) {\n        delete styles.color;\n    }\n    if (attributes.backgroundColor === Color.Black && !attributes.inverse) {\n        delete styles.backgroundColor;\n    }\n\n    return styles;\n};\n"
  },
  {
    "path": "src/views/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n    <head>\n        <meta charset=\"utf-8\" />\n        <title>Upterm</title>\n        <style>\n            @import url('https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,400i,700');\n            ::selection {\n                background: rgba(255, 255, 125, 0.99);\n                color: #032764;\n            }\n\n            * ::selection {\n                background: rgba(255, 255, 125, 0.99);\n                color: #032764;\n            }\n\n            * {\n                box-sizing: border-box;\n            }\n\n            ::-webkit-scrollbar {\n                display: none;\n            }\n\n            html {\n                -webkit-backface-visibility: hidden;\n                backface-visibility: hidden;\n            }\n\n            body {\n                margin: 0;\n            }\n\n            @font-face {\n                font-family: \"FontAwesome\";\n                src: url(../../../node_modules/font-awesome/fonts/fontawesome-webfont.ttf) format(\"truetype\");\n            }\n\n            @keyframes progress-bar-stripes {\n                from {\n                    background-position: 0 0;\n                }\n                to {\n                    background-position: 30px 0;\n                }\n            }\n\n            .application {\n                display: grid;\n                grid-template-areas: \"title-bar\" \"tab\";\n                font-size: var(--font-size);\n                font-family: var(--font-family);\n                background-color: var(--background-color);\n                color: var(--text-color);\n\n                --title-bar-height: 28px;\n                --footer-height: calc(var(--font-size) * 2);\n                --prompt-height: calc(var(--font-size) * 3);\n                --session-height: calc(100vh - var(--title-bar-height));\n                --prompt-font-size: calc(var(--font-size) + 2px);\n            }\n\n            .title-bar {\n                border-bottom: 1px solid #191C22;\n                display: flex;\n                grid-area: title-bar;\n                height: var(--title-bar-height);\n                -webkit-app-region: drag;\n                background: #242A31;\n            }\n\n            .title-bar .search {\n                margin-top: 3px;\n            }\n\n            .title-bar.reversed .search {\n                order: 3;\n            }\n\n            .title-bar .search .search-icon {\n                position: relative;\n                left: var(--font-size);\n                top: -1px;\n                font-size: calc(var(--font-size) - 4px);\n                font-family: FontAwesome;\n            }\n\n            .title-bar .search .search-input {\n                --search-input-height: calc(var(--title-bar-height) - 6px);\n                -webkit-app-region: no-drag;\n                font-size: var(--font-size);\n                background-color: var(--search-input-color);\n                border: 0;\n                border-radius: 3px;\n                -webkit-appearance: none;\n                outline: none;\n                height: var(--search-input-height);\n                width: 10em;\n                padding-left: var(--font-size);\n                color: var(--white-color);\n            }\n\n            .title-bar .tabs {\n                justify-content: center;\n                display: flex;\n                flex: 1;\n                -webkit-margin-before: 0;\n                -webkit-margin-after: 0;\n                -webkit-padding-start: 0;\n                -webkit-user-select: none;\n                user-select: none;\n                list-style: none;\n                padding-left: 68px;\n                padding-right: 129px;\n            }\n\n            .title-bar.reversed .tabs {\n                order: 2;\n            }\n\n            .tab-header {\n                opacity: 0.3;\n                position: relative;\n                max-width: 200px;\n                flex-grow: 1;\n                display: flex;\n                text-align: center;\n                border: 1px solid #FFFFFF20;\n                font-size: 0.852em;\n                border-top: 0;\n                align-items: center;\n                justify-content: center;\n                border-radius: 0 0 5px 5px;\n            }\n\n            .tab-header[data-focused=true] {\n                opacity: 1;\n\n                background-color: #FFFFFF10;\n            }\n\n            .tab-header:hover {\n                background-color: var(--black-color);\n                opacity: 1;\n            }\n\n            .tab-header .close-button {\n                color: transparent;\n\n                --margin: calc(var(--title-bar-height) - var(--font-size));\n\n                font-family: FontAwesome;\n                position: absolute;\n                left: var(--margin);\n                top: calc(var(--margin) / 2);\n                cursor: pointer;\n            }\n\n            .tab-header:hover .close-button {\n                color: var(--white-color);\n            }\n\n            .tab-header:hover .close-button:hover {\n                color: var(--red-color);\n            }\n\n            .tab {\n                grid-area: tab;\n            }\n\n            .tab[data-focused=false] {\n                display: none;\n            }\n\n            .sessions {\n                grid-area: tab;\n                display: grid;\n                background: #212731;\n                grid-template-rows: 100%;\n                height: var(--session-height);\n                border-left: var(--background-color);\n            }\n\n            .view-line {\n                padding-top: 5px;\n            }\n\n            .session .prompt:before {\n                z-index: 2;\n                grid-area: prompt-decoration;\n                content: \"\";\n                position: absolute;\n                top: 2px;\n                opacity: 0.2;\n                left: -27px;\n                width: 41px;\n                height: 50px;\n            }\n\n            .view-lines {\n                background-color: #20242B;\n            }\n\n            .monaco-editor .margin {\n                background-color: #20242B;\n            }\n\n            .sessions[data-side-by-side=true] {\n                grid-template-columns: 1fr 1fr;\n            }\n\n            .session {\n                contain: layout;\n                position: relative;\n                height: var(--session-height);\n            }\n\n            .shutter {\n                z-index: 5;\n                pointer-events: none;\n                background-color: var(--white-color);\n                opacity: 0.3;\n                position: absolute;\n                left: 0;\n                right: 0;\n                top: 0;\n                bottom: 0;\n            }\n\n            .session .jobs {\n                overflow-y: scroll;\n                will-change: transform;\n                contain: strict;\n                display: flex;\n                flex-direction: column-reverse;\n                height: calc(calc(var(--session-height) - var(--prompt-height)) - var(--footer-height));\n            }\n\n            .session[data-status=in-progress] .jobs {\n                height: calc(var(--session-height) - var(--footer-height));\n            }\n\n            .output-cut {\n                --height: calc(var(--font-size) * 2.6);\n                position: relative;\n                left: calc(0px - var(--content-padding));\n                width: calc(100% + var(--content-padding));\n                height: var(--height);\n                padding-top: calc(calc(var(--height) - var(--font-size)) / 3);\n                text-align: center;\n                cursor: pointer;\n            }\n\n            .job {\n                padding: var(--content-padding);\n                background-color: #2D323B;\n                margin-top: var(--font-size);\n            }\n\n            .job[data-status=failed] {\n                --job-background-color: var(--failed-job-background-color);\n            }\n\n            .job-header {\n                color: var(--job-background-color);\n                filter: brightness(260%);\n                font-size: var(--prompt-font-size);\n                padding-bottom: 3px;\n            }\n\n            .job-header .job-actions {\n                margin-right: 15px;\n                text-align: right;\n            }\n\n            .job-header .job-actions .prettify-toggle {\n                text-align: center;\n                width: 1em;\n                display: inline-block;\n                margin: 0 3px;\n                cursor: pointer;\n                font-family: FontAwesome;\n                color: var(--white-color);\n            }\n\n            .job-header .job-actions .prettify-toggle[data-enabled=true] {\n                color: var(--green-color);\n            }\n\n            .job-header div {\n                white-space: pre;\n            }\n\n            .output {\n                white-space: pre-wrap;\n                position: relative;\n            }\n\n            .output[data-buffer-type=normal] {\n                contain: paint;\n            }\n\n            .output[data-screen-mode=light] {\n                background-color: var(--white-color);\n                color: var(--black-color);\n            }\n\n            .job[data-status=in-progress] .output[data-buffer-type=alternate] {\n                background-color: var(--job-background-color);\n                position: absolute;\n                top: 0;\n                bottom: 0;\n                left: var(--content-padding);\n                right: var(--content-padding);\n                z-index: 4;\n            }\n\n            .job[data-status=success] .output[data-buffer-type=alternate],\n            .job[data-status=failed] .output[data-buffer-type=alternate] {\n                zoom: 0.5;\n            }\n\n            .output .cursor {\n                --scrollback-offset: calc(var(--scrollback-size) * var(--letter-height));\n                --row-offset: calc(var(--row-index) * var(--letter-height));\n                --column-offset: calc(var(--column-index) * var(--letter-width));\n\n                position: absolute;\n                background-color: var(--white-color);\n                opacity: 0.6;\n                top: calc(var(--scrollback-offset) + var(--row-offset));\n                left: var(--column-offset);\n                height: var(--letter-height);\n                width: var(--letter-width);\n            }\n\n            .output .row {\n                height: var(--letter-height);\n                width: 100%;\n            }\n\n            .output .row .char-group {\n                display: inline-block;\n                height: var(--letter-height);\n            }\n\n            .session .prompt {\n                --prompt-padding: calc(calc(var(--prompt-height) - var(--prompt-font-size)) / 2.5);\n                position: absolute;\n                contain: layout;\n                bottom: var(--footer-height);\n                height: var(--prompt-height);\n                width: 100%;\n                padding-top: var(--prompt-padding);\n                line-height: 1.3;\n                display: grid;\n                grid-template-areas: \"prompt-decoration prompt-content\";\n                grid-template-columns: calc(var(--prompt-font-size) * 2) auto;\n            }\n\n            .session .prompt[data-mode=history-search] {\n                grid-template-columns: calc(var(--prompt-font-size) * 8) auto;\n            }\n\n            .session[data-status=in-progress] .prompt {\n                opacity: 0;\n                pointer-events: none;\n            }\n\n            .session .prompt:before {\n                z-index: 2;\n                /* Without this left pane prompt overlaps right pane prompt. */\n                grid-area: prompt-decoration;\n                filter: brightness(50%);\n            }\n\n            .session .prompt[data-mode=normal]:before {\n                content: url(\"../../../images/prompt-decoration.svg\");\n            }\n\n            .session .prompt[data-mode=history-search]:before {\n                content: url(\"../../../images/prompt-history-mode-decoration.svg\");\n            }\n\n            .session .prompt-content {\n                grid-area: prompt-content;\n                font-size: var(--prompt-font-size);\n                white-space: pre-wrap;\n                -webkit-appearance: none;\n                outline: none;\n                z-index: 2;\n                margin-left: -12px;\n                margin-top: -4px;\n            }\n            .margin-view-overlays {\n    background: #1d1f23;\n}\n\n            .footer {\n                position: absolute;\n                z-index: 2;\n                bottom: 0;\n                height: var(--footer-height);\n                width: 100%;\n                opacity: 0.7;\n                padding: calc(calc(var(--footer-height) - var(--font-size)) / 2) var(--content-padding);\n                background-color: #111;\n            }\n\n            .footer span {\n                margin-right: 1.5em;\n            }\n\n            .footer .present-directory:before {\n                font-family: \"FontAwesome\";\n                padding-right: 5px;\n                content: \"\\f114\";\n            }\n\n            .footer .present-directory {\n                padding-right: 10px;\n            }\n\n            .footer .vcs-data:before {\n                padding-right: 5px;\n                font-family: \"FontAwesome\";\n                content: \"\\f126\";\n            }\n\n            .footer .vcs-data {\n                padding-right: 8px;\n            }\n\n            .footer .release-component-link {\n                cursor: pointer;\n                color: var(--green-color);\n                float: right;\n            }\n\n            /* inline styles can't really handle pseudo elements, so they still need classes */\n\n            .jsonTreeParentNode:before {\n                content: \"\\0025B8 \\0000a0\";\n                margin-left: -1.2em;\n            }\n\n            .jsonTreeParentNode.expanded:before {\n                content: \"\\0025BE \\0000a0\";\n            }\n\n            .underlineOnHover:hover {\n                text-decoration: underline;\n            }\n\n            .session .prompt[data-mode=history-search] .suggest-widget {\n                width: 600px !important;\n            }\n\n            /* A workaround for https://github.com/Microsoft/monaco-editor/issues/434 */\n\n            .monaco-editor .suggest-widget.no-icons .icon {\n                display: none !important;\n            }\n\n            .monaco-editor .suggest-widget .monaco-list .monaco-list-row {\n                padding-left: 5px;\n            }\n\n            /* Display detail for all suggestions, not only for the focused one. */\n\n            .monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.type-label {\n                display: inline !important;\n                opacity: .3 !important;\n                margin-left: 2em !important;\n            }\n\n            /* Change info icon color from blue to gray. */\n\n            .monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore {\n                background-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggMWMtMy44NjUgMC03IDMuMTM1LTcgN3MzLjEzNSA3IDcgNyA3LTMuMTM1IDctNy0zLjEzNS03LTctN3ptMSAxMmgtMnYtN2gydjd6bTAtOGgtMnYtMmgydjJ6IiBmaWxsPSIjRkZGRkZGMzAiLz48cGF0aCBkPSJNNyA2aDJ2N2gtMnYtN3ptMC0xaDJ2LTJoLTJ2MnoiIGZpbGw9IiNGRkZGRkYxMCIvPjwvc3ZnPg==\") !important;\n            }\n        </style>\n    </head>\n\n    <body>\n        <div id=\"react-entry-point\"></div>\n    </body>\n\n</html>\n\n<script>\n    require(\"../../src/monaco/Loader.js\").requireMonaco(() => require(\"../../src/views/Main.js\"));\n</script>\n\n</html>\n"
  },
  {
    "path": "src/views/keyevents/Keybindings.ts",
    "content": "import {KeyCode, KeyboardAction, Status} from \"../../Enums\";\nimport {error} from \"../../utils/Common\";\nimport {SearchComponent} from \"../SearchComponent\";\nimport {UserEvent} from \"../../Interfaces\";\nimport {isModifierKey} from \"../ViewUtils\";\nimport {services} from \"../../services/index\";\nimport {ApplicationComponent} from \"../ApplicationComponent\";\n\nexport type KeybindingType = {\n    action: KeyboardAction,\n    keybinding: (e: KeyboardEvent) => boolean,\n};\n\nfunction isCtrlOrCmd(e: KeyboardEvent): boolean {\n    /**\n     * Decides if a keyboard event contains the meta key for all platforms\n     * Linux does not support the metaKey so it can be manually changed here\n     * Windows/OSX is simply e.metaKey\n     */\n    if (e.metaKey) {\n        return true;\n    } else if (process.platform === \"linux\") {\n        return e.ctrlKey;\n    }\n    return false;\n}\n\nexport const KeybindingsForActions: KeybindingType[] = [\n    // CLI commands\n    {\n        action: KeyboardAction.cliClearJobs,\n        keybinding: (e: KeyboardEvent) => e.ctrlKey && e.keyCode === KeyCode.L,\n    },\n    {\n        action: KeyboardAction.cliClearText,\n        // Need to include !shiftKey otherwise it will clear instead of copying\n        keybinding: (e: KeyboardEvent) => e.ctrlKey && e.keyCode === KeyCode.C && !e.shiftKey,\n    },\n    {\n        action: KeyboardAction.cliAppendLastArgumentOfPreviousCommand,\n        keybinding: (e: KeyboardEvent) => e.altKey && e.keyCode === KeyCode.Period,\n    },\n    {\n        action: KeyboardAction.cliHistoryPrevious,\n        keybinding: (e: KeyboardEvent) => {\n            return (e.ctrlKey && e.keyCode === KeyCode.P) || (e.keyCode === KeyCode.Up);\n        },\n    },\n    {\n        action: KeyboardAction.cliHistoryNext,\n        keybinding: (e: KeyboardEvent) => {\n            return (e.ctrlKey && e.keyCode === KeyCode.N) || (e.keyCode === KeyCode.Down);\n        },\n    },\n    // autocomplete commands\n    {\n        action: KeyboardAction.autocompleteInsertCompletion,\n        keybinding: (e: KeyboardEvent) => e.keyCode === KeyCode.Tab,\n    },\n    {\n        action: KeyboardAction.autocompletePreviousSuggestion,\n        keybinding: (e: KeyboardEvent) => {\n            return (e.ctrlKey && e.keyCode === KeyCode.P) || (e.keyCode === KeyCode.Up);\n        },\n    },\n    {\n        action: KeyboardAction.autocompleteNextSuggestion,\n        keybinding: (e: KeyboardEvent) => {\n            return (e.ctrlKey && e.keyCode === KeyCode.N) || (e.keyCode === KeyCode.Down);\n        },\n    },\n    // tab commands\n    {\n        action: KeyboardAction.tabFocus,\n        keybinding: (e: KeyboardEvent) => {\n            return ((e.ctrlKey || isCtrlOrCmd(e)) && e.keyCode >= KeyCode.One && e.keyCode <= KeyCode.Nine);\n        },\n    },\n    // search commands\n    {\n        action: KeyboardAction.editFindClose,\n        keybinding: (e: KeyboardEvent) => e.keyCode === KeyCode.Escape,\n    },\n];\n\nexport function isKeybindingForEvent(event: KeyboardEvent, action: KeyboardAction): boolean {\n    /**\n     * Finds the keybinding for the given action and returns the result of the keybinding function\n     */\n    let matchingKeyboardAction = KeybindingsForActions.find((keybinding) => {\n        return keybinding.action === action;\n    });\n    if (!matchingKeyboardAction) {\n        error(\"No matching keybinding for action: \" + KeyboardAction[action]);\n        return false;\n    }\n    return matchingKeyboardAction.keybinding(event);\n}\n\n// Menu Stuff\nexport type KeybindingMenuType = {\n    action: KeyboardAction,\n    accelerator: string,\n};\n\nconst CmdOrCtrl = process.platform === \"darwin\" ? \"Cmd\" : \"Ctrl\";\n\nexport const KeybindingsForMenu: KeybindingMenuType[] = [\n    {\n        action: KeyboardAction.tabNew,\n        accelerator: `${CmdOrCtrl}+T`,\n    },\n    {\n        action: KeyboardAction.tabPrevious,\n        accelerator: `${CmdOrCtrl}+[`,\n    },\n    {\n        action: KeyboardAction.tabNext,\n        accelerator: `${CmdOrCtrl}+]`,\n    },\n    {\n        action: KeyboardAction.sessionClose,\n        accelerator: `${CmdOrCtrl}+W`,\n    },\n    // edit/clipboard commands\n    {\n        action: KeyboardAction.clipboardCopy,\n        accelerator: process.platform === \"darwin\" ? \"Cmd+C\" : \"Ctrl+Shift+C\",\n    },\n    {\n        action: KeyboardAction.clipboardPaste,\n        accelerator: process.platform === \"darwin\" ? \"Cmd+V\" : \"Ctrl+Shift+V\",\n    },\n    {\n        action: KeyboardAction.editFind,\n        accelerator: `${CmdOrCtrl}+F`,\n    },\n    {\n        action: KeyboardAction.editFindClose,\n        accelerator: \"Esc\",\n    },\n    {\n        action: KeyboardAction.increaseFontSize,\n        accelerator: `${CmdOrCtrl}+Plus`,\n    },\n    {\n        action: KeyboardAction.decreaseFontSize,\n        accelerator: `${CmdOrCtrl}+-`,\n    },\n    {\n        action: KeyboardAction.resetFontSize,\n        accelerator: `${CmdOrCtrl}+0`,\n    },\n    // view commands\n    {\n        action: KeyboardAction.otherSession,\n        accelerator: `${CmdOrCtrl}+\\\\`,\n    },\n    {\n        action: KeyboardAction.viewToggleFullScreen,\n        accelerator: \"Ctrl+Shift+F\",\n    },\n    {\n        action: KeyboardAction.toggleDeveloperTools,\n        accelerator: `${CmdOrCtrl}+Alt+I`,\n    },\n    // Upterm commands\n    {\n        action: KeyboardAction.uptermQuit,\n        accelerator: `${CmdOrCtrl}+Q`,\n    },\n];\n\n\nexport function getAcceleratorForAction(action: KeyboardAction): string {\n    /**\n     * Returns the accelerator for a given keyboard action\n     */\n    // Find the matching menu item by keyboardAction (should only ever return one item)\n    let matchingMenuItem = KeybindingsForMenu.filter((menuAction) => {\n        return menuAction.action === action;\n    })[0];\n    return matchingMenuItem.accelerator;\n}\n\nexport function isMenuShortcut(event: KeyboardEvent): boolean {\n    const accelerator = toAccelerator(event);\n    return !!KeybindingsForMenu.find(action => action.accelerator === accelerator);\n}\n\nfunction toAccelerator(event: KeyboardEvent): string {\n    let parts: string[] = [];\n\n    if (event.ctrlKey) {\n        parts.push(\"Ctrl\");\n    }\n\n    if (event.shiftKey) {\n        parts.push(\"Shift\");\n    }\n\n    if (event.metaKey) {\n        parts.push(\"Cmd\");\n    }\n\n    if (event.altKey) {\n        parts.push(\"Alt\");\n    }\n\n    // Cmd+Alt+I generates event.key Dead, but its code is KeyI.\n    const key = event.key === \"Dead\" ? event.code.slice(3) : event.key.toUpperCase();\n\n    parts.push(key);\n\n    return parts.join(\"+\");\n}\n\nexport function handleUserEvent(application: ApplicationComponent, search: SearchComponent, event: UserEvent) {\n    const sessionComponent = application.focusedTabComponent.focusedSessionComponent;\n    if (!sessionComponent) {\n        return;\n    }\n\n    const isJobRunning = sessionComponent.status === Status.InProgress;\n    const promptComponent = sessionComponent.promptComponent;\n\n    // Pasted data\n    if (event instanceof ClipboardEvent) {\n        if (search.isFocused) {\n            return;\n        }\n\n        if (isJobRunning) {\n            application.focusedSession.lastJob!.write(event.clipboardData.getData(\"text/plain\"));\n\n            event.stopPropagation();\n            event.preventDefault();\n        } else {\n            promptComponent.focus();\n        }\n\n        return;\n    }\n\n    if (isModifierKey(event) || isMenuShortcut(event)) {\n        return;\n    }\n\n    // Change focused tab\n    if (isKeybindingForEvent(event, KeyboardAction.tabFocus)) {\n        const position = parseInt(event.key, 10);\n        application.focusTab(position - 1);\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    // Console clear\n    if (!isJobRunning && isKeybindingForEvent(event, KeyboardAction.cliClearJobs)) {\n        application.focusedSession.clearJobs();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    if (search.isFocused) {\n        // Search close\n        if (isKeybindingForEvent(event, KeyboardAction.editFindClose)) {\n            search.clearSelection();\n            search.blur();\n\n            event.stopPropagation();\n            event.preventDefault();\n            return;\n        }\n\n        return;\n    }\n\n    if (isJobRunning && application.focusedSession.lastJob!.isRunningPty()) {\n        application.focusedSession.lastJob!.write(event);\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    if (isJobRunning) {\n        return;\n    }\n\n    promptComponent.focus();\n\n    // CLI execute command\n    if (event.keyCode === KeyCode.CarriageReturn) {\n        promptComponent.onReturnKeyPress();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    // Append last argument to prompt\n    if (isKeybindingForEvent(event, KeyboardAction.cliAppendLastArgumentOfPreviousCommand)) {\n        promptComponent.appendLastLArgumentOfPreviousCommand();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    // CLI clear\n    if (isKeybindingForEvent(event, KeyboardAction.cliClearText)) {\n        promptComponent.clear();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    if (event.ctrlKey && event.keyCode === KeyCode.R && !promptComponent.isInHistorySearchMode) {\n        promptComponent.setHistorySearchMode();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    if (event.keyCode === KeyCode.Tab) {\n        promptComponent.acceptSelectedSuggestion();\n\n        event.stopPropagation();\n        event.preventDefault();\n        return;\n    }\n\n    if (event.keyCode === KeyCode.Escape && promptComponent.isInHistorySearchMode) {\n        promptComponent.setNormalMode();\n        return;\n    }\n}\n"
  },
  {
    "path": "src/views/menu/Menu.ts",
    "content": "import {KeyboardAction} from \"../../Enums\";\nimport {remote} from \"electron\";\nimport {getAcceleratorForAction} from \"../keyevents/Keybindings\";\nimport {ApplicationComponent} from \"../ApplicationComponent\";\nimport {services} from \"../../services\";\n\nexport function buildMenuTemplate(\n    app: Electron.App,\n    browserWindow: Electron.BrowserWindow,\n    application: ApplicationComponent,\n): Electron.MenuItemConstructorOptions[] {\n    return [\n        {\n            label: \"Upterm\",\n            submenu: [\n                { role: \"about\" },\n                { type: \"separator\" },\n                { role: \"hide\" },\n                { role: \"hideothers\" },\n                { role: \"unhide\" },\n                { type: \"separator\" },\n                {\n                    label: \"Quit\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.uptermQuit),\n                    click: () => {\n                        app.quit();\n                    },\n                },\n            ],\n        },\n        {\n            label: \"Edit\",\n            submenu: [\n                {\n                    label: \"Copy\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.clipboardCopy),\n                    role: \"copy\",\n                },\n                {\n                    label: \"Paste\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.clipboardPaste),\n                    role: \"paste\",\n                },\n                {\n                    label: \"Find\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.editFind),\n                    click: () => {\n                        (document.querySelector(\"input[type=search]\") as HTMLInputElement).select();\n                    },\n                },\n                {\n                    type: \"separator\",\n                },\n                {\n                    label: \"Increase Font Size\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.increaseFontSize),\n                    click: () => {\n                        services.font.increaseSize();\n                    },\n                },\n                {\n                    label: \"Decrease Font Size\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.decreaseFontSize),\n                    click: () => {\n                        services.font.decreaseSize();\n                    },\n                },\n                {\n                    label: \"Reset Font Size\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.resetFontSize),\n                    click: () => {\n                        services.font.resetSize();\n                    },\n                },\n            ],\n        },\n        {\n            label: \"View\",\n            submenu: [\n                {\n                    label: \"Toggle Full Screen\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.viewToggleFullScreen),\n                    click: () => {\n                        browserWindow.setFullScreen(!browserWindow.isFullScreen());\n                    },\n                },\n                {\n                    label: \"Toggle Developer Tools\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.toggleDeveloperTools),\n                    click: () => {\n                        browserWindow.webContents.toggleDevTools();\n                    },\n                },\n            ],\n        },\n        {\n            label: \"Session\",\n            submenu: [\n                {\n                    label: \"Other Session\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.otherSession),\n                    click: () => {\n                        application.otherSession();\n                    },\n                },\n                {\n                    label: \"Close Current Session\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.sessionClose),\n                    click: () => {\n                        application.closeFocusedSession();\n                    },\n                },\n            ],\n        },\n        {\n            label: \"Tab\",\n            submenu: [\n                {\n                    label: \"New Tab\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.tabNew),\n                    click: () => {\n                        application.addTab();\n                    },\n                },\n                {\n                    type: \"separator\",\n                },\n                {\n                    label: \"Previous Tab\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.tabPrevious),\n                    click: () => {\n                        application.focusPreviousTab();\n                    },\n                },\n                {\n                    label: \"Next Tab\",\n                    accelerator: getAcceleratorForAction(KeyboardAction.tabNext),\n                    click: () => {\n                        application.focusNextTab();\n                    },\n                },\n                {\n                    type: \"separator\",\n                },\n                {\n                    label: \"Close Current Tab\",\n                    click: () => {\n                        application.closeFocusedTab();\n                    },\n                },\n            ],\n        },\n        {\n            role: \"window\",\n            submenu: [\n                { role: \"minimize\" },\n                { role: \"close\" },\n            ],\n        },\n        {\n            label: \"Help\",\n            submenu: [\n                {\n                    label: \"GitHub Repository\",\n                    click: () => {\n                        /* tslint:disable:no-unused-expression */\n                        remote.shell.openExternal(\"http://l.rw.rw/upterm_repository\");\n                    },\n                },\n                {\n                    label: \"Leave Feedback\",\n                    click: () => {\n                        /* tslint:disable:no-unused-expression */\n                        remote.shell.openExternal(\"http://l.rw.rw/upterm_leave_feedback\");\n                    },\n                },\n            ],\n        },\n    ];\n}\n"
  },
  {
    "path": "src/views/mouseevents/MouseEvents.ts",
    "content": "import {ApplicationComponent} from \"../ApplicationComponent\";\nimport {MouseEvent} from \"../../Interfaces\";\nimport * as fs from \"fs\";\nimport {userFriendlyPath, escapeFilePath, normalizeDirectory} from \"../../utils/Common\";\nimport {Status} from \"../../Enums\";\n\nfunction isDirectory(path: string): boolean {\n    return fs.lstatSync(path).isDirectory();\n}\n\nexport function handleMouseEvent(application: ApplicationComponent, event: MouseEvent) {\n    const sessionComponent = application.focusedTabComponent.focusedSessionComponent;\n    if (!sessionComponent) {\n        return;\n    }\n\n    const isJobRunning = sessionComponent.status === Status.InProgress;\n    const promptComponent = sessionComponent.promptComponent;\n\n    if (event instanceof DragEvent) {\n        const path = event.dataTransfer.files[0].path;\n        let formattedPath = userFriendlyPath(escapeFilePath(path));\n\n        if (isDirectory(path)) {\n            formattedPath = normalizeDirectory(formattedPath);\n        }\n\n        if (!isJobRunning) {\n            promptComponent.insertValueInPlace(formattedPath);\n        }\n\n        event.preventDefault();\n        return;\n    }\n}\n"
  },
  {
    "path": "test/e2e.ts",
    "content": "import {Application, SpectronClient} from \"spectron\";\nimport {expect} from \"chai\";\nimport {join} from \"path\";\nimport {userFriendlyPath} from \"../src/utils/Common\";\n\nconst timeout = 50000;\n\nclass Page {\n    private promptSelector = \".monaco-editor\";\n\n    constructor(private client: SpectronClient) {}\n\n    waitTillLoaded() {\n        return this.client.waitForExist(this.promptSelector, timeout);\n    }\n\n    executeCommand(command: string) {\n        return new Promise(resolve => {\n            this.client.keys(command + \"\\n\");\n            setTimeout(() => resolve(), 500);\n        });\n    }\n\n    get prompt() {\n        return this.client.element(this.promptSelector);\n    }\n\n    get job() {\n        return new Job(this.client, this.client.element(\".job\"));\n    }\n\n    get footer() {\n        return new Footer(this.client, this.client.element(\".footer\"));\n    }\n}\n\nabstract class Block {\n    constructor(\n        protected client: SpectronClient,\n        protected selector: WebdriverIO.Client<WebdriverIO.RawResult<WebdriverIO.Element>> & WebdriverIO.RawResult<WebdriverIO.Element>,\n    ) {}\n}\n\nclass Job extends Block {\n    get output() {\n        return this.selector.element(\".output\");\n    }\n}\n\nclass Footer extends Block {\n    get presentDirectory() {\n        return this.selector.element(\".present-directory\");\n    }\n}\n\ndescribe(\"application launch\", function () {\n    this.timeout(timeout);\n\n    let app: Application;\n    let page: Page;\n\n    before(async () => {\n        app = new Application({path: \"node_modules/.bin/electron\", args: [\".\"]});\n    });\n\n    beforeEach(async () => {\n        if (app.isRunning()) {\n            await app.restart();\n        } else {\n            await app.start();\n        }\n\n        await app.client.waitUntilWindowLoaded();\n        page = new Page(app.client);\n        return page.waitTillLoaded();\n    });\n\n    after(() => {\n        if (app.isRunning()) {\n            return app.stop();\n        }\n    });\n\n    it(\"can execute a command\", async () => {\n        await page.executeCommand(\"echo expected-text\");\n        const output = await page.job.output.getText();\n\n        expect(output).to.contain(\"expected-text\");\n    });\n\n    describe(\"status bar\", () => {\n        it(\"changes working directory on cd\", async () => {\n            const oldDirectory = userFriendlyPath(__dirname + \"/\");\n            const newDirectory = userFriendlyPath(join(oldDirectory, \"utils\") + \"/\");\n\n            await page.executeCommand(`cd ${oldDirectory}`);\n            expect(await page.footer.presentDirectory.getText()).to.eql(oldDirectory);\n\n            await page.executeCommand(`cd ${newDirectory}`);\n            expect(await page.footer.presentDirectory.getText()).to.eql(newDirectory);\n        });\n    });\n});\n"
  },
  {
    "path": "test/environment_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\nimport {Environment, preprocessEnv} from \"../src/shell/Environment\";\n\ndescribe(\"EnvironmentPath\", () => {\n    describe(\"input method\", () => {\n        it(\"prepend\", async() => {\n            const environment = new Environment({});\n\n            environment.path.prepend(\"/usr/bin\");\n            environment.path.prepend(\"/usr/local/bin\");\n\n            expect(environment.toObject()).to.eql({\n                PATH: \"/usr/local/bin:/usr/bin\",\n            });\n        });\n    });\n\n    describe(\"environment preprocessor\", () => {\n        it(\"preprocesses bash functions\", () => {\n            expect(preprocessEnv([\n                \"BASH_FUNC_foo%%=() if 0; then\",\n                \" x\",\n                \" else\",\n                \" y\",\n                \" fi\",\n                \"}\",\n                \"var=val\",\n            ])).to.eql([\n                \"BASH_FUNC_foo%%=() if 0; then\\n x\\n else\\n y\\n fi\\n}\",\n                \"var=val\",\n            ]);\n        });\n    });\n});\n"
  },
  {
    "path": "test/output_spec.ts",
    "content": "/// <reference path=\"../typings/Interfaces.d.ts\" />\n\nimport \"mocha\";\nimport {expect} from \"chai\";\nimport {Output} from \"../src/Output\";\nimport {TerminalLikeDevice} from \"../src/Interfaces\";\nimport {readFileSync} from \"fs\";\n\nclass DummyTerminal implements TerminalLikeDevice {\n    output: Output;\n    written = \"\";\n    write = (input: string) => this.written += input;\n\n    constructor(dimensions: Dimensions = {columns: 80, rows: 80}) {\n        this.output = new Output(this, dimensions);\n    }\n}\n\ntype CSIFinalCharacter = \"A\" | \"B\" | \"C\" | \"D\" | \"E\" | \"F\" | \"R\" | \"m\" | \"n\";\n\nconst esc = `\\x1b`;\nconst ri = `${esc}M`;\nconst ich = `${esc}[1@`;\nconst dl = (rows: number) => `${esc}[${rows}M`;\nconst dch = (n: number) => `${esc}[${n}P`;\nconst cup = (row: number, column: number) => `${esc}[${row};${column}H`;\nconst decsel = (param: number) => `${esc}[${param}K`;\nconst decstbm = (topMargin: number, bottomMargin: number) => `${esc}[${topMargin};${bottomMargin}r`;\nconst csi = (params: number[], final: CSIFinalCharacter) => {\n    return `${esc}[${params.join(\";\")}${final}`;\n};\n\nconst sgr = (params: number[]) => {\n    return csi(params, \"m\");\n};\n\ndescribe(\"Output\", () => {\n    it(\"contains the first row even if there was no input (to show cursor on)\", () => {\n        const terminal = new DummyTerminal({columns: 5, rows: 5});\n\n        expect(terminal.output.toLines()).to.eql([\n            \"     \",\n        ]);\n    });\n\n    it(\"wraps long strings\", () => {\n        const terminal = new DummyTerminal({columns: 5, rows: 5});\n        terminal.output.write(\"0123456789\");\n\n        expect(terminal.output.toLines()).to.eql([\n            \"01234\",\n            \"56789\",\n        ]);\n    });\n\n    describe(\"movements\", () => {\n        it(\"can move down\", () => {\n            const terminal = new DummyTerminal({columns: 11, rows: 2});\n            terminal.output.write(`first${csi([1], \"B\")}second`);\n\n            expect(terminal.output.toLines()).to.eql([\n                \"first      \",\n                \"     second\",\n            ]);\n        });\n\n        it(\"stays at the same line after writing last column character\", () => {\n            const terminal = new DummyTerminal({columns: 10, rows: 5});\n            terminal.output.write(`${esc}[1;10H*${esc}[5D*`);\n\n            expect(terminal.output.toString()).to.eql(\"    *    *\");\n            expect(terminal.output.toString()).to.eql(\"    *    *\");\n        });\n\n        it(\"doesn't move outside of the current page\", () => {\n            const terminal = new DummyTerminal({columns: 10, rows: 5});\n            terminal.output.write(`1\\r\\n2\\r\\n3\\r\\n4\\r\\n5\\r\\n6\\r\\n7${esc}[1;1H42`);\n\n            expect(terminal.output.toLines()).to.eql([\n                \"1         \",\n                \"2         \",\n                \"42        \",\n                \"4         \",\n                \"5         \",\n                \"6         \",\n                \"7         \",\n            ]);\n        });\n\n        describe(\"Reverse Index\", () => {\n            it(\"scrolls down when cursor is at the beginning of page\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                terminal.output.write(`1\\r\\n2\\r\\n3\\r\\n4\\r\\n5\\r\\n6\\r\\n7${cup(1, 1)}${ri}`);\n\n                expect(terminal.output.toLines()).to.eql([\n                    \"1         \",\n                    \"2         \",\n                    \"          \",\n                    \"3         \",\n                    \"4         \",\n                    \"5         \",\n                    \"6         \",\n                ]);\n            });\n\n            it(\"scrolls down scrolling region\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                terminal.output.write(`1\\r\\n2\\r\\n3\\r\\n4\\r\\n5\\r\\n6\\r\\n7${decstbm(1, 3)}${cup(1, 1)}${ri}`);\n\n                expect(terminal.output.toLines()).to.eql([\n                    \"1         \",\n                    \"2         \",\n                    \"          \",\n                    \"3         \",\n                    \"4         \",\n                    \"6         \",\n                    \"7         \",\n                ]);\n            });\n        });\n    });\n\n    it(\"can parse an ASCII string\", () => {\n        const terminal = new DummyTerminal();\n        terminal.output.write(\"something\");\n\n        expect(terminal.output.toString().trim()).to.eql(\"something\");\n    });\n\n    it(\"ICH\", () => {\n        const terminal = new DummyTerminal();\n        terminal.output.write(`123${cup(1, 1)}${ich}0`);\n\n        expect(terminal.output.toString().trim()).to.eql(\"0123\");\n    });\n\n    describe(\"true color\", () => {\n        it(\"sets the correct foreground color\", () => {\n            const terminal = new DummyTerminal();\n            terminal.output.write(`${sgr([38, 2, 255, 100, 0])}A${sgr([0])}`);\n\n            const firstChar = terminal.output.activeBuffer.at({rowIndex: 0, columnIndex: 0});\n            expect(firstChar.attributes.color).to.eql([255, 100, 0]);\n        });\n    });\n\n    it(\"uses default attributes to fill in new lines\", () => {\n        const terminal = new DummyTerminal();\n        terminal.output.write(`${sgr([46])}${cup(2, 1)}`);\n\n        const firstChar = terminal.output.activeBuffer.at({rowIndex: 1, columnIndex: 0});\n        expect(firstChar.attributes.backgroundColor).to.eql(0);\n    });\n\n    describe(\"CSI\", () => {\n        describe(\"Device Status Report (DSR)\", () => {\n            describe(\"Report Cursor Position (CPR)\", () => {\n                it(\"report cursor position\", () => {\n                    const terminal = new DummyTerminal();\n                    terminal.output.write(`some text${csi([6], \"n\")}`);\n\n                    expect(terminal.written).to.eql(`${csi([1, 10], \"R\")}`);\n                });\n            });\n        });\n\n        describe(\"DL\", () => {\n            // A temporary test. Can be removed when we have a test for the real behavior.\n            it(\"doesn't fail\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                const input = `1\\r\\n2\\r\\n3${cup(3, 1)}${dl(1)}`;\n                terminal.output.write(input);\n\n                terminal.output.toLines();\n            });\n        });\n\n        describe(\"DCH\", () => {\n            it(\"Removes chars from cursor position\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                const input = `1234567890${cup(1, 2)}${dch(2)}`;\n                terminal.output.write(input);\n\n                expect(terminal.output.toLines()).to.deep.equal([\n                    \"14567890  \",\n                ]);\n            });\n        });\n\n        describe(\"DECSEL\", () => {\n            it(\"Erases line to right\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                const input = `1234567890${cup(1, 5)}${decsel(0)}`;\n                terminal.output.write(input);\n\n                expect(terminal.output.toLines()).to.deep.equal([\n                    \"1234      \",\n                ]);\n            });\n\n            it(\"Correctly erases line to right from beginning\", () => {\n                const terminal = new DummyTerminal({columns: 10, rows: 5});\n                const input = `1234567890${cup(1, 1)}${decsel(0)}`;\n                terminal.output.write(input);\n\n                expect(terminal.output.toLines()).to.deep.equal([\n                    \"          \",\n                ]);\n            });\n        });\n    });\n\n    describe(\"vttest\", () => {\n        function vttest(fileName: string, expectedOutput: string[]) {\n            const input = readFileSync(`${__dirname}/test_files/vttest/${fileName}`).toString();\n\n            return it(fileName, () => {\n                const terminal = new DummyTerminal();\n                terminal.output.write(input);\n                const actualOutput = terminal.output.toLines();\n\n                expect(actualOutput).to.deep.equal(expectedOutput);\n            });\n        }\n\n        describe(\"cursor movements\", () => {\n            vttest(\"1-1\", [\n                \"********************************************************************************\",\n                \"*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+        EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE        +*\",\n                \"*+        E                                                          E        +*\",\n                \"*+        E The screen should be cleared,  and have an unbroken bor- E        +*\",\n                \"*+        E der of *'s and +'s around the edge,   and exactly in the E        +*\",\n                \"*+        E middle  there should be a frame of E's around this  text E        +*\",\n                \"*+        E with  one (1) free position around it.    Push <RETURN>  E        +*\",\n                \"*+        E                                                          E        +*\",\n                \"*+        EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE        +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*+                                                                            +*\",\n                \"*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\",\n                \"********************************************************************************\",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"1-2\", [\n                \"************************************************************************************************************************************\",\n                \"*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                  EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE                                  +*\",\n                \"*+                                  E                                                          E                                  +*\",\n                \"*+                                  E The screen should be cleared,  and have an unbroken bor- E                                  +*\",\n                \"*+                                  E der of *'s and +'s around the edge,   and exactly in the E                                  +*\",\n                \"*+                                  E middle  there should be a frame of E's around this  text E                                  +*\",\n                \"*+                                  E with  one (1) free position around it.    Push <RETURN>  E                                  +*\",\n                \"*+                                  E                                                          E                                  +*\",\n                \"*+                                  EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE                                  +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*+                                                                                                                                +*\",\n                \"*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\",\n                \"************************************************************************************************************************************\",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n                \"                                                                                                                                    \",\n            ]);\n\n            vttest(\"1-3\", [\n                \"Test of autowrap, mixing control and print characters.                          \",\n                \"The left/right margins should have letters in order:                            \",\n                \"I                                                                              i\",\n                \"J                                                                              j\",\n                \"K                                                                              k\",\n                \"L                                                                              l\",\n                \"M                                                                              m\",\n                \"N                                                                              n\",\n                \"O                                                                              o\",\n                \"P                                                                              p\",\n                \"Q                                                                              q\",\n                \"R                                                                              r\",\n                \"S                                                                              s\",\n                \"T                                                                              t\",\n                \"U                                                                              u\",\n                \"V                                                                              v\",\n                \"W                                                                              w\",\n                \"X                                                                              x\",\n                \"Y                                                                              y\",\n                \"Z                                                                              z\",\n                \"                                                                                \",\n                \"Push <RETURN>                                                                   \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"1-4\", [\n                \"Test of autowrap, mixing control and print characters.                                                                              \",\n                \"The left/right margins should have letters in order:                                                                                \",\n                \"I                                                                                                                                  i\",\n                \"J                                                                                                                                  j\",\n                \"K                                                                                                                                  k\",\n                \"L                                                                                                                                  l\",\n                \"M                                                                                                                                  m\",\n                \"N                                                                                                                                  n\",\n                \"O                                                                                                                                  o\",\n                \"P                                                                                                                                  p\",\n                \"Q                                                                                                                                  q\",\n                \"R                                                                                                                                  r\",\n                \"S                                                                                                                                  s\",\n                \"T                                                                                                                                  t\",\n                \"U                                                                                                                                  u\",\n                \"V                                                                                                                                  v\",\n                \"W                                                                                                                                  w\",\n                \"X                                                                                                                                  x\",\n                \"Y                                                                                                                                  y\",\n                \"Z                                                                                                                                  z\",\n                \"                                                                                                                                    \",\n                \"Push <RETURN>                                                                                                                       \",\n                \"                                                                                                                                    \",\n            ]);\n\n            vttest(\"1-5\", [\n                \"Test of cursor-control characters inside ESC sequences.                         \",\n                \"Below should be four identical lines:                                           \",\n                \"                                                                                \",\n                \"A B C D E F G H I                                                               \",\n                \"A B C D E F G H I                                                               \",\n                \"A B C D E F G H I                                                               \",\n                \"A B C D E F G H I                                                               \",\n                \"                                                                                \",\n                \"Push <RETURN>                                                                   \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"1-6\", [\n                \"Test of leading zeros in ESC sequences.                                         \",\n                'Two lines below you should see the sentence \"This is a correct sentence\".       ',\n                \"                                                                                \",\n                \"This is a correct sentence                                                      \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"Push <RETURN>                                                                   \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"2-1\", [\n                \"********************************************************************************\",\n                \"********************************************************************************\",\n                \"********************************************************************************\",\n                \"                                                                                \",\n                \"This should be three identical lines of *'s completely filling                  \",\n                \"the top of the screen without any empty lines between.                          \",\n                \"(Test of WRAP AROUND mode setting.)                                             \",\n                \"Push <RETURN>                                                                   \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"2-2\", [\n                \"      *     *     *     *     *     *     *     *     *     *     *     *     * \",\n                \"      *     *     *     *     *     *     *     *     *     *     *     *     * \",\n                \"                                                                                \",\n                \"Test of TAB setting/resetting. These two lines                                  \",\n                \"should look the same. Push <RETURN>                                             \",\n                \"                                                                                \",\n            ]);\n\n            vttest(\"2-15\", [\n                \"AAAAA                                                                           \",\n                \"AAAAA                                                                           \",\n                \"AAAAA                                                                           \",\n                \"AAAAA                                                                           \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"           normal      bold        underscored blinking    reversed             \",\n                \"                                                                                \",\n                \"stars:     **********  **********  **********  **********  **********           \",\n                \"                                                                                \",\n                \"line:      ──────────  ──────────  ──────────  ──────────  ──────────           \",\n                \"                                                                                \",\n                \"x'es:      xxxxxxxxxx  xxxxxxxxxx  xxxxxxxxxx  xxxxxxxxxx  xxxxxxxxxx           \",\n                \"                                                                                \",\n                \"diamonds:  ◆◆◆◆◆◆◆◆◆◆  ◆◆◆◆◆◆◆◆◆◆  ◆◆◆◆◆◆◆◆◆◆  ◆◆◆◆◆◆◆◆◆◆  ◆◆◆◆◆◆◆◆◆◆           \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"                                                                                \",\n                \"Test of the SAVE/RESTORE CURSOR feature. There should                           \",\n                \"be ten characters of each flavour, and a rectangle                              \",\n                \"of 5 x 4 A's filling the top left of the screen.                                \",\n                \"Push <RETURN>                                                                   \",\n                \"                                                                                \",\n            ]);\n        });\n    });\n});\n"
  },
  {
    "path": "test/pty_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\nimport {PTY} from \"../src/PTY\";\nimport {scan} from \"../src/shell/Scanner\";\nimport ProcessEnv = NodeJS.ProcessEnv;\n\ndescribe(\"PTY\", () => {\n    it(\"doesn't interpolate expressions inside single quotes\", (done) => {\n        let output = \"\";\n        const tokens = scan(\"echo '$('\");\n        const env = <ProcessEnv & {PWD: string}>process.env;\n\n        return new PTY(\n            tokens.map(token => token.escapedValue),\n            env,\n            {columns: 80, rows: 30},\n            (data: string) => output += data,\n            (exitCode: number) => {\n                expect(exitCode).to.eq(0);\n                done();\n            },\n        );\n    });\n});\n"
  },
  {
    "path": "test/references.d.ts",
    "content": "/// <reference path=\"../typings/Interfaces.d.ts\" />\n"
  },
  {
    "path": "test/shell/scanner_spec.ts",
    "content": "import {expect} from \"chai\";\nimport {\n    scan, Word, DoubleQuotedStringLiteral, SingleQuotedStringLiteral, CompositeStringLiteral,\n    Pipe, OutputRedirectionSymbol, AppendingOutputRedirectionSymbol, InputRedirectionSymbol, Invalid, Semicolon,\n} from \"../../src/shell/Scanner\";\n\ndescribe(\"scan\", () => {\n    it(\"returns no tokens on empty input\", () => {\n        const tokens = scan(\"\");\n\n        expect(tokens.length).to.eq(0);\n    });\n\n    it(\"returns an invalid token on input that consists only of spaces\", () => {\n        const tokens = scan(\"  \");\n\n        expect(tokens.length).to.eq(1);\n        expect(tokens[0]).to.be.an.instanceof(Invalid);\n    });\n\n    it(\"splits on a space\", () => {\n        const tokens = scan(\"some words\");\n\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"some\", \"words\"]);\n    });\n\n    it(\"doesn't split inside double quotes\", () => {\n        const tokens = scan('prefix \"inside quotes\"');\n\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(DoubleQuotedStringLiteral);\n\n        expect(tokens.map(token => token.value)).to.eql([\"prefix\", \"inside quotes\"]);\n    });\n\n    it(\"doesn't split inside single quotes\", () => {\n        const tokens = scan(\"prefix 'inside quotes'\");\n\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(SingleQuotedStringLiteral);\n\n        expect(tokens.map(token => token.value)).to.eql([\"prefix\", \"inside quotes\"]);\n    });\n\n    it(\"doesn't split string literals with no spaces between them\", () => {\n        const tokens = scan(\"echo a'b'\\\"c\\\" d\");\n\n        expect(tokens.length).to.eq(3);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(CompositeStringLiteral);\n        expect(tokens[2]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"echo\", \"abc\", \"d\"]);\n    });\n\n    it(\"doesn't split on an escaped space\", () => {\n        const tokens = scan(\"prefix single\\\\ token\");\n\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"prefix\", \"single token\"]);\n    });\n\n    it(\"doesn't split on a colon\", () => {\n        const tokens = scan(\"curl http://www.example.com\");\n\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"curl\", \"http://www.example.com\"]);\n    });\n\n    it(\"can handle special characters\", () => {\n        const tokens = scan(\"ls --color=tty -lh\");\n\n        expect(tokens.length).to.eq(3);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n        expect(tokens[2]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"ls\", \"--color=tty\", \"-lh\"]);\n    });\n\n    it(\"recognizes a pipe\", () => {\n        const tokens = scan(\"cat file | grep word\");\n\n        expect(tokens.length).to.eq(5);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n        expect(tokens[2]).to.be.an.instanceof(Pipe);\n        expect(tokens[3]).to.be.an.instanceof(Word);\n        expect(tokens[4]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"cat\", \"file\", \"|\", \"grep\", \"word\"]);\n    });\n\n    it(\"recognizes a semicolon\", () => {\n        const tokens = scan(\"cd directory; rm file\");\n\n        expect(tokens.length).to.eq(5);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n        expect(tokens[2]).to.be.an.instanceof(Semicolon);\n        expect(tokens[3]).to.be.an.instanceof(Word);\n        expect(tokens[4]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"cd\", \"directory\", \";\", \"rm\", \"file\"]);\n    });\n\n    it(\"recognizes input redirection\", () => {\n        const tokens = scan(\"cat < file\");\n\n        expect(tokens.length).to.eq(3);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(InputRedirectionSymbol);\n        expect(tokens[2]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"cat\", \"<\", \"file\"]);\n    });\n\n    it(\"recognizes output redirection\", () => {\n        const tokens = scan(\"cat file > another_file\");\n\n        expect(tokens.length).to.eq(4);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n        expect(tokens[2]).to.be.an.instanceof(OutputRedirectionSymbol);\n        expect(tokens[3]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"cat\", \"file\", \">\", \"another_file\"]);\n    });\n\n    it(\"recognizes appending output redirection\", () => {\n        const tokens = scan(\"cat file >> another_file\");\n\n        expect(tokens.length).to.eq(4);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Word);\n        expect(tokens[2]).to.be.an.instanceof(AppendingOutputRedirectionSymbol);\n        expect(tokens[3]).to.be.an.instanceof(Word);\n\n        expect(tokens.map(token => token.value)).to.eql([\"cat\", \"file\", \">>\", \"another_file\"]);\n    });\n\n    it(\"can handle unicode é\", () => {\n        const tokens = scan(\"cd é/\");\n        expect(tokens.map(token => token.value)).to.eql([\"cd\", \"é/\"]);\n    });\n\n    it(\"can handle 'x+' (regression test for #753)\", () => {\n        const tokens = scan(\"cd x+\");\n        expect(tokens.map(token => token.value)).to.eql([\"cd\", \"x+\"]);\n    });\n\n    it(\"includes spaces at end in final token\", () => {\n        const tokens = scan(\"test space \");\n        expect(tokens.map(token => token.value)).to.eql([\"test\", \"space\", \"\"]);\n    });\n\n    it(\"handles escaped brackets in words\", () => {\n        const tokens = scan(\"file\\\\ with\\\\ brackets\\\\(\\\\)\");\n        expect(tokens.map(token => token.value)).to.eql([\"file with brackets\\\\(\\\\)\"]);\n    });\n\n    it(\"adds an invalid token on invalid input\", () => {\n        const tokens = scan(\"cd '\");\n        expect(tokens.length).to.eq(2);\n        expect(tokens[0]).to.be.an.instanceof(Word);\n        expect(tokens[1]).to.be.an.instanceof(Invalid);\n        expect(tokens.map(token => token.value)).to.eql([\"cd\", \"'\"]);\n    });\n\n    it(\"handles file descriptor redirection\", () => {\n        const tokens = scan(\"find / -name x 2>/dev/null\");\n        expect(tokens.map(token => token.value)).to.eql([\"find\", \"/\", \"-name\", \"x\", \"2>\", \"/dev/null\"]);\n    });\n});\n"
  },
  {
    "path": "test/test_files/file_names_test/file with brackets()",
    "content": ""
  },
  {
    "path": "test/test_files/vttest/1-1",
    "content": "\u0007\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 1\r\n\u001b[2J\u001b[?3l\u001b#8\u001b[9;10H\u001b[1J\u001b[18;60H\u001b[0J\u001b[1K\u001b[9;71H\u001b[0K\u001b[10;10H\u001b[1K\u001b[10;71H\u001b[0K\u001b[11;10H\u001b[1K\u001b[11;71H\u001b[0K\u001b[12;10H\u001b[1K\u001b[12;71H\u001b[0K\u001b[13;10H\u001b[1K\u001b[13;71H\u001b[0K\u001b[14;10H\u001b[1K\u001b[14;71H\u001b[0K\u001b[15;10H\u001b[1K\u001b[15;71H\u001b[0K\u001b[16;10H\u001b[1K\u001b[16;71H\u001b[0K\u001b[17;30H\u001b[2K\u001b[24;1f*\u001b[1;1f*\u001b[24;2f*\u001b[1;2f*\u001b[24;3f*\u001b[1;3f*\u001b[24;4f*\u001b[1;4f*\u001b[24;5f*\u001b[1;5f*\u001b[24;6f*\u001b[1;6f*\u001b[24;7f*\u001b[1;7f*\u001b[24;8f*\u001b[1;8f*\u001b[24;9f*\u001b[1;9f*\u001b[24;10f*\u001b[1;10f*\u001b[24;11f*\u001b[1;11f*\u001b[24;12f*\u001b[1;12f*\u001b[24;13f*\u001b[1;13f*\u001b[24;14f*\u001b[1;14f*\u001b[24;15f*\u001b[1;15f*\u001b[24;16f*\u001b[1;16f*\u001b[24;17f*\u001b[1;17f*\u001b[24;18f*\u001b[1;18f*\u001b[24;19f*\u001b[1;19f*\u001b[24;20f*\u001b[1;20f*\u001b[24;21f*\u001b[1;21f*\u001b[24;22f*\u001b[1;22f*\u001b[24;23f*\u001b[1;23f*\u001b[24;24f*\u001b[1;24f*\u001b[24;25f*\u001b[1;25f*\u001b[24;26f*\u001b[1;26f*\u001b[24;27f*\u001b[1;27f*\u001b[24;28f*\u001b[1;28f*\u001b[24;29f*\u001b[1;29f*\u001b[24;30f*\u001b[1;30f*\u001b[24;31f*\u001b[1;31f*\u001b[24;32f*\u001b[1;32f*\u001b[24;33f*\u001b[1;33f*\u001b[24;34f*\u001b[1;34f*\u001b[24;35f*\u001b[1;35f*\u001b[24;36f*\u001b[1;36f*\u001b[24;37f*\u001b[1;37f*\u001b[24;38f*\u001b[1;38f*\u001b[24;39f*\u001b[1;39f*\u001b[24;40f*\u001b[1;40f*\u001b[24;41f*\u001b[1;41f*\u001b[24;42f*\u001b[1;42f*\u001b[24;43f*\u001b[1;43f*\u001b[24;44f*\u001b[1;44f*\u001b[24;45f*\u001b[1;45f*\u001b[24;46f*\u001b[1;46f*\u001b[24;47f*\u001b[1;47f*\u001b[24;48f*\u001b[1;48f*\u001b[24;49f*\u001b[1;49f*\u001b[24;50f*\u001b[1;50f*\u001b[24;51f*\u001b[1;51f*\u001b[24;52f*\u001b[1;52f*\u001b[24;53f*\u001b[1;53f*\u001b[24;54f*\u001b[1;54f*\u001b[24;55f*\u001b[1;55f*\u001b[24;56f*\u001b[1;56f*\u001b[24;57f*\u001b[1;57f*\u001b[24;58f*\u001b[1;58f*\u001b[24;59f*\u001b[1;59f*\u001b[24;60f*\u001b[1;60f*\u001b[24;61f*\u001b[1;61f*\u001b[24;62f*\u001b[1;62f*\u001b[24;63f*\u001b[1;63f*\u001b[24;64f*\u001b[1;64f*\u001b[24;65f*\u001b[1;65f*\u001b[24;66f*\u001b[1;66f*\u001b[24;67f*\u001b[1;67f*\u001b[24;68f*\u001b[1;68f*\u001b[24;69f*\u001b[1;69f*\u001b[24;70f*\u001b[1;70f*\u001b[24;71f*\u001b[1;71f*\u001b[24;72f*\u001b[1;72f*\u001b[24;73f*\u001b[1;73f*\u001b[24;74f*\u001b[1;74f*\u001b[24;75f*\u001b[1;75f*\u001b[24;76f*\u001b[1;76f*\u001b[24;77f*\u001b[1;77f*\u001b[24;78f*\u001b[1;78f*\u001b[24;79f*\u001b[1;79f*\u001b[24;80f*\u001b[1;80f*\u001b[2;2H+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD\u001b[23;79H+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM\u001b[2;1H*\u001b[2;80H*\u001b[10D\u001bE*\u001b[3;80H*\u001b[10D\u001bE*\u001b[4;80H*\u001b[10D\u001bE*\u001b[5;80H*\u001b[10D\u001bE*\u001b[6;80H*\u001b[10D\u001bE*\u001b[7;80H*\u001b[10D\u001bE*\u001b[8;80H*\u001b[10D\u001bE*\u001b[9;80H*\u001b[10D\u001bE*\u001b[10;80H*\u001b[10D\r\n*\u001b[11;80H*\u001b[10D\r\n*\u001b[12;80H*\u001b[10D\r\n*\u001b[13;80H*\u001b[10D\r\n*\u001b[14;80H*\u001b[10D\r\n*\u001b[15;80H*\u001b[10D\r\n*\u001b[16;80H*\u001b[10D\r\n*\u001b[17;80H*\u001b[10D\r\n*\u001b[18;80H*\u001b[10D\r\n*\u001b[19;80H*\u001b[10D\r\n*\u001b[20;80H*\u001b[10D\r\n*\u001b[21;80H*\u001b[10D\r\n*\u001b[22;80H*\u001b[10D\r\n*\u001b[23;80H*\u001b[10D\r\n\u001b[2;10H\u001b[42D\u001b[2C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C\u001b[23;70H\u001b[42C\u001b[2D+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b\u001b[1;1H\u001b[10A\u001b[1A\u001b[0A\u001b[24;80H\u001b[10B\u001b[1B\u001b[0B\u001b[10;12H                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D\u001b[5A\u001b[1CThe screen should be cleared,  and have an unbroken bor-\u001b[12;13Hder of *'s and +'s around the edge,   and exactly in the\u001b[13;13Hmiddle  there should be a frame of E's around this  text\u001b[14;13Hwith  one (1) free position around it.    Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/1-2",
    "content": "\u0007\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 1\r\n\u001b[2J\u001b[?3l\u001b#8\u001b[9;10H\u001b[1J\u001b[18;60H\u001b[0J\u001b[1K\u001b[9;71H\u001b[0K\u001b[10;10H\u001b[1K\u001b[10;71H\u001b[0K\u001b[11;10H\u001b[1K\u001b[11;71H\u001b[0K\u001b[12;10H\u001b[1K\u001b[12;71H\u001b[0K\u001b[13;10H\u001b[1K\u001b[13;71H\u001b[0K\u001b[14;10H\u001b[1K\u001b[14;71H\u001b[0K\u001b[15;10H\u001b[1K\u001b[15;71H\u001b[0K\u001b[16;10H\u001b[1K\u001b[16;71H\u001b[0K\u001b[17;30H\u001b[2K\u001b[24;1f*\u001b[1;1f*\u001b[24;2f*\u001b[1;2f*\u001b[24;3f*\u001b[1;3f*\u001b[24;4f*\u001b[1;4f*\u001b[24;5f*\u001b[1;5f*\u001b[24;6f*\u001b[1;6f*\u001b[24;7f*\u001b[1;7f*\u001b[24;8f*\u001b[1;8f*\u001b[24;9f*\u001b[1;9f*\u001b[24;10f*\u001b[1;10f*\u001b[24;11f*\u001b[1;11f*\u001b[24;12f*\u001b[1;12f*\u001b[24;13f*\u001b[1;13f*\u001b[24;14f*\u001b[1;14f*\u001b[24;15f*\u001b[1;15f*\u001b[24;16f*\u001b[1;16f*\u001b[24;17f*\u001b[1;17f*\u001b[24;18f*\u001b[1;18f*\u001b[24;19f*\u001b[1;19f*\u001b[24;20f*\u001b[1;20f*\u001b[24;21f*\u001b[1;21f*\u001b[24;22f*\u001b[1;22f*\u001b[24;23f*\u001b[1;23f*\u001b[24;24f*\u001b[1;24f*\u001b[24;25f*\u001b[1;25f*\u001b[24;26f*\u001b[1;26f*\u001b[24;27f*\u001b[1;27f*\u001b[24;28f*\u001b[1;28f*\u001b[24;29f*\u001b[1;29f*\u001b[24;30f*\u001b[1;30f*\u001b[24;31f*\u001b[1;31f*\u001b[24;32f*\u001b[1;32f*\u001b[24;33f*\u001b[1;33f*\u001b[24;34f*\u001b[1;34f*\u001b[24;35f*\u001b[1;35f*\u001b[24;36f*\u001b[1;36f*\u001b[24;37f*\u001b[1;37f*\u001b[24;38f*\u001b[1;38f*\u001b[24;39f*\u001b[1;39f*\u001b[24;40f*\u001b[1;40f*\u001b[24;41f*\u001b[1;41f*\u001b[24;42f*\u001b[1;42f*\u001b[24;43f*\u001b[1;43f*\u001b[24;44f*\u001b[1;44f*\u001b[24;45f*\u001b[1;45f*\u001b[24;46f*\u001b[1;46f*\u001b[24;47f*\u001b[1;47f*\u001b[24;48f*\u001b[1;48f*\u001b[24;49f*\u001b[1;49f*\u001b[24;50f*\u001b[1;50f*\u001b[24;51f*\u001b[1;51f*\u001b[24;52f*\u001b[1;52f*\u001b[24;53f*\u001b[1;53f*\u001b[24;54f*\u001b[1;54f*\u001b[24;55f*\u001b[1;55f*\u001b[24;56f*\u001b[1;56f*\u001b[24;57f*\u001b[1;57f*\u001b[24;58f*\u001b[1;58f*\u001b[24;59f*\u001b[1;59f*\u001b[24;60f*\u001b[1;60f*\u001b[24;61f*\u001b[1;61f*\u001b[24;62f*\u001b[1;62f*\u001b[24;63f*\u001b[1;63f*\u001b[24;64f*\u001b[1;64f*\u001b[24;65f*\u001b[1;65f*\u001b[24;66f*\u001b[1;66f*\u001b[24;67f*\u001b[1;67f*\u001b[24;68f*\u001b[1;68f*\u001b[24;69f*\u001b[1;69f*\u001b[24;70f*\u001b[1;70f*\u001b[24;71f*\u001b[1;71f*\u001b[24;72f*\u001b[1;72f*\u001b[24;73f*\u001b[1;73f*\u001b[24;74f*\u001b[1;74f*\u001b[24;75f*\u001b[1;75f*\u001b[24;76f*\u001b[1;76f*\u001b[24;77f*\u001b[1;77f*\u001b[24;78f*\u001b[1;78f*\u001b[24;79f*\u001b[1;79f*\u001b[24;80f*\u001b[1;80f*\u001b[2;2H+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD\u001b[23;79H+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM\u001b[2;1H*\u001b[2;80H*\u001b[10D\u001bE*\u001b[3;80H*\u001b[10D\u001bE*\u001b[4;80H*\u001b[10D\u001bE*\u001b[5;80H*\u001b[10D\u001bE*\u001b[6;80H*\u001b[10D\u001bE*\u001b[7;80H*\u001b[10D\u001bE*\u001b[8;80H*\u001b[10D\u001bE*\u001b[9;80H*\u001b[10D\u001bE*\u001b[10;80H*\u001b[10D\r\n*\u001b[11;80H*\u001b[10D\r\n*\u001b[12;80H*\u001b[10D\r\n*\u001b[13;80H*\u001b[10D\r\n*\u001b[14;80H*\u001b[10D\r\n*\u001b[15;80H*\u001b[10D\r\n*\u001b[16;80H*\u001b[10D\r\n*\u001b[17;80H*\u001b[10D\r\n*\u001b[18;80H*\u001b[10D\r\n*\u001b[19;80H*\u001b[10D\r\n*\u001b[20;80H*\u001b[10D\r\n*\u001b[21;80H*\u001b[10D\r\n*\u001b[22;80H*\u001b[10D\r\n*\u001b[23;80H*\u001b[10D\r\n\u001b[2;10H\u001b[42D\u001b[2C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C\u001b[23;70H\u001b[42C\u001b[2D+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b\u001b[1;1H\u001b[10A\u001b[1A\u001b[0A\u001b[24;80H\u001b[10B\u001b[1B\u001b[0B\u001b[10;12H                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D\u001b[5A\u001b[1CThe screen should be cleared,  and have an unbroken bor-\u001b[12;13Hder of *'s and +'s around the edge,   and exactly in the\u001b[13;13Hmiddle  there should be a frame of E's around this  text\u001b[14;13Hwith  one (1) free position around it.    Push <RETURN>\u001b[?3h\u001b#8\u001b[9;36H\u001b[1J\u001b[18;86H\u001b[0J\u001b[1K\u001b[9;97H\u001b[0K\u001b[10;36H\u001b[1K\u001b[10;97H\u001b[0K\u001b[11;36H\u001b[1K\u001b[11;97H\u001b[0K\u001b[12;36H\u001b[1K\u001b[12;97H\u001b[0K\u001b[13;36H\u001b[1K\u001b[13;97H\u001b[0K\u001b[14;36H\u001b[1K\u001b[14;97H\u001b[0K\u001b[15;36H\u001b[1K\u001b[15;97H\u001b[0K\u001b[16;36H\u001b[1K\u001b[16;97H\u001b[0K\u001b[17;30H\u001b[2K\u001b[24;1f*\u001b[1;1f*\u001b[24;2f*\u001b[1;2f*\u001b[24;3f*\u001b[1;3f*\u001b[24;4f*\u001b[1;4f*\u001b[24;5f*\u001b[1;5f*\u001b[24;6f*\u001b[1;6f*\u001b[24;7f*\u001b[1;7f*\u001b[24;8f*\u001b[1;8f*\u001b[24;9f*\u001b[1;9f*\u001b[24;10f*\u001b[1;10f*\u001b[24;11f*\u001b[1;11f*\u001b[24;12f*\u001b[1;12f*\u001b[24;13f*\u001b[1;13f*\u001b[24;14f*\u001b[1;14f*\u001b[24;15f*\u001b[1;15f*\u001b[24;16f*\u001b[1;16f*\u001b[24;17f*\u001b[1;17f*\u001b[24;18f*\u001b[1;18f*\u001b[24;19f*\u001b[1;19f*\u001b[24;20f*\u001b[1;20f*\u001b[24;21f*\u001b[1;21f*\u001b[24;22f*\u001b[1;22f*\u001b[24;23f*\u001b[1;23f*\u001b[24;24f*\u001b[1;24f*\u001b[24;25f*\u001b[1;25f*\u001b[24;26f*\u001b[1;26f*\u001b[24;27f*\u001b[1;27f*\u001b[24;28f*\u001b[1;28f*\u001b[24;29f*\u001b[1;29f*\u001b[24;30f*\u001b[1;30f*\u001b[24;31f*\u001b[1;31f*\u001b[24;32f*\u001b[1;32f*\u001b[24;33f*\u001b[1;33f*\u001b[24;34f*\u001b[1;34f*\u001b[24;35f*\u001b[1;35f*\u001b[24;36f*\u001b[1;36f*\u001b[24;37f*\u001b[1;37f*\u001b[24;38f*\u001b[1;38f*\u001b[24;39f*\u001b[1;39f*\u001b[24;40f*\u001b[1;40f*\u001b[24;41f*\u001b[1;41f*\u001b[24;42f*\u001b[1;42f*\u001b[24;43f*\u001b[1;43f*\u001b[24;44f*\u001b[1;44f*\u001b[24;45f*\u001b[1;45f*\u001b[24;46f*\u001b[1;46f*\u001b[24;47f*\u001b[1;47f*\u001b[24;48f*\u001b[1;48f*\u001b[24;49f*\u001b[1;49f*\u001b[24;50f*\u001b[1;50f*\u001b[24;51f*\u001b[1;51f*\u001b[24;52f*\u001b[1;52f*\u001b[24;53f*\u001b[1;53f*\u001b[24;54f*\u001b[1;54f*\u001b[24;55f*\u001b[1;55f*\u001b[24;56f*\u001b[1;56f*\u001b[24;57f*\u001b[1;57f*\u001b[24;58f*\u001b[1;58f*\u001b[24;59f*\u001b[1;59f*\u001b[24;60f*\u001b[1;60f*\u001b[24;61f*\u001b[1;61f*\u001b[24;62f*\u001b[1;62f*\u001b[24;63f*\u001b[1;63f*\u001b[24;64f*\u001b[1;64f*\u001b[24;65f*\u001b[1;65f*\u001b[24;66f*\u001b[1;66f*\u001b[24;67f*\u001b[1;67f*\u001b[24;68f*\u001b[1;68f*\u001b[24;69f*\u001b[1;69f*\u001b[24;70f*\u001b[1;70f*\u001b[24;71f*\u001b[1;71f*\u001b[24;72f*\u001b[1;72f*\u001b[24;73f*\u001b[1;73f*\u001b[24;74f*\u001b[1;74f*\u001b[24;75f*\u001b[1;75f*\u001b[24;76f*\u001b[1;76f*\u001b[24;77f*\u001b[1;77f*\u001b[24;78f*\u001b[1;78f*\u001b[24;79f*\u001b[1;79f*\u001b[24;80f*\u001b[1;80f*\u001b[24;81f*\u001b[1;81f*\u001b[24;82f*\u001b[1;82f*\u001b[24;83f*\u001b[1;83f*\u001b[24;84f*\u001b[1;84f*\u001b[24;85f*\u001b[1;85f*\u001b[24;86f*\u001b[1;86f*\u001b[24;87f*\u001b[1;87f*\u001b[24;88f*\u001b[1;88f*\u001b[24;89f*\u001b[1;89f*\u001b[24;90f*\u001b[1;90f*\u001b[24;91f*\u001b[1;91f*\u001b[24;92f*\u001b[1;92f*\u001b[24;93f*\u001b[1;93f*\u001b[24;94f*\u001b[1;94f*\u001b[24;95f*\u001b[1;95f*\u001b[24;96f*\u001b[1;96f*\u001b[24;97f*\u001b[1;97f*\u001b[24;98f*\u001b[1;98f*\u001b[24;99f*\u001b[1;99f*\u001b[24;100f*\u001b[1;100f*\u001b[24;101f*\u001b[1;101f*\u001b[24;102f*\u001b[1;102f*\u001b[24;103f*\u001b[1;103f*\u001b[24;104f*\u001b[1;104f*\u001b[24;105f*\u001b[1;105f*\u001b[24;106f*\u001b[1;106f*\u001b[24;107f*\u001b[1;107f*\u001b[24;108f*\u001b[1;108f*\u001b[24;109f*\u001b[1;109f*\u001b[24;110f*\u001b[1;110f*\u001b[24;111f*\u001b[1;111f*\u001b[24;112f*\u001b[1;112f*\u001b[24;113f*\u001b[1;113f*\u001b[24;114f*\u001b[1;114f*\u001b[24;115f*\u001b[1;115f*\u001b[24;116f*\u001b[1;116f*\u001b[24;117f*\u001b[1;117f*\u001b[24;118f*\u001b[1;118f*\u001b[24;119f*\u001b[1;119f*\u001b[24;120f*\u001b[1;120f*\u001b[24;121f*\u001b[1;121f*\u001b[24;122f*\u001b[1;122f*\u001b[24;123f*\u001b[1;123f*\u001b[24;124f*\u001b[1;124f*\u001b[24;125f*\u001b[1;125f*\u001b[24;126f*\u001b[1;126f*\u001b[24;127f*\u001b[1;127f*\u001b[24;128f*\u001b[1;128f*\u001b[24;129f*\u001b[1;129f*\u001b[24;130f*\u001b[1;130f*\u001b[24;131f*\u001b[1;131f*\u001b[24;132f*\u001b[1;132f*\u001b[2;2H+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD+\u001b[1D\u001bD\u001b[23;131H+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM+\u001b[1D\u001bM\u001b[2;1H*\u001b[2;132H*\u001b[10D\u001bE*\u001b[3;132H*\u001b[10D\u001bE*\u001b[4;132H*\u001b[10D\u001bE*\u001b[5;132H*\u001b[10D\u001bE*\u001b[6;132H*\u001b[10D\u001bE*\u001b[7;132H*\u001b[10D\u001bE*\u001b[8;132H*\u001b[10D\u001bE*\u001b[9;132H*\u001b[10D\u001bE*\u001b[10;132H*\u001b[10D\r\n*\u001b[11;132H*\u001b[10D\r\n*\u001b[12;132H*\u001b[10D\r\n*\u001b[13;132H*\u001b[10D\r\n*\u001b[14;132H*\u001b[10D\r\n*\u001b[15;132H*\u001b[10D\r\n*\u001b[16;132H*\u001b[10D\r\n*\u001b[17;132H*\u001b[10D\r\n*\u001b[18;132H*\u001b[10D\r\n*\u001b[19;132H*\u001b[10D\r\n*\u001b[20;132H*\u001b[10D\r\n*\u001b[21;132H*\u001b[10D\r\n*\u001b[22;132H*\u001b[10D\r\n*\u001b[23;132H*\u001b[10D\r\n\u001b[2;10H\u001b[68D\u001b[2C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C+\u001b[0C\u001b[2D\u001b[1C\u001b[23;96H\u001b[68C\u001b[2D+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b+\u001b[1D\u001b[1C\u001b[0D\b\u001b[1;1H\u001b[10A\u001b[1A\u001b[0A\u001b[24;132H\u001b[10B\u001b[1B\u001b[0B\u001b[10;38H                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D                                                          \u001b[1B\u001b[58D\u001b[5A\u001b[1CThe screen should be cleared,  and have an unbroken bor-\u001b[12;39Hder of *'s and +'s around the edge,   and exactly in the\u001b[13;39Hmiddle  there should be a frame of E's around this  text\u001b[14;39Hwith  one (1) free position around it.    Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/1-3",
    "content": "\u001b[?3l\u001b[?3lTest of autowrap, mixing control and print characters.\r\r\nThe left/right margins should have letters in order:\r\r\n\u001b[3;21r\u001b[?6h\u001b[19;1HA\u001b[19;80Ha\r\n\u001b[18;80HaB\u001b[19;80HB\b b\r\n\u001b[19;80HC\b\b\t\tc\u001b[19;2H\bC\r\n\u001b[19;80H\r\n\u001b[18;1HD\u001b[18;80Hd\u001b[19;1HE\u001b[19;80He\r\n\u001b[18;80HeF\u001b[19;80HF\b f\r\n\u001b[19;80HG\b\b\t\tg\u001b[19;2H\bG\r\n\u001b[19;80H\r\n\u001b[18;1HH\u001b[18;80Hh\u001b[19;1HI\u001b[19;80Hi\r\n\u001b[18;80HiJ\u001b[19;80HJ\b j\r\n\u001b[19;80HK\b\b\t\tk\u001b[19;2H\bK\r\n\u001b[19;80H\r\n\u001b[18;1HL\u001b[18;80Hl\u001b[19;1HM\u001b[19;80Hm\r\n\u001b[18;80HmN\u001b[19;80HN\b n\r\n\u001b[19;80HO\b\b\t\to\u001b[19;2H\bO\r\n\u001b[19;80H\r\n\u001b[18;1HP\u001b[18;80Hp\u001b[19;1HQ\u001b[19;80Hq\r\n\u001b[18;80HqR\u001b[19;80HR\b r\r\n\u001b[19;80HS\b\b\t\ts\u001b[19;2H\bS\r\n\u001b[19;80H\r\n\u001b[18;1HT\u001b[18;80Ht\u001b[19;1HU\u001b[19;80Hu\r\n\u001b[18;80HuV\u001b[19;80HV\b v\r\n\u001b[19;80HW\b\b\t\tw\u001b[19;2H\bW\r\n\u001b[19;80H\r\n\u001b[18;1HX\u001b[18;80Hx\u001b[19;1HY\u001b[19;80Hy\r\n\u001b[18;80HyZ\u001b[19;80HZ\b z\r\n\u001b[?6l\u001b[r\u001b[22;1HPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/1-4",
    "content": "\u001b[?3hTest of autowrap, mixing control and print characters.\r\r\nThe left/right margins should have letters in order:\r\r\n\u001b[3;21r\u001b[?6h\u001b[19;1HA\u001b[19;132Ha\r\n\u001b[18;132HaB\u001b[19;132HB\b b\r\n\u001b[19;132HC\b\b\t\tc\u001b[19;2H\bC\r\n\u001b[19;132H\r\n\u001b[18;1HD\u001b[18;132Hd\u001b[19;1HE\u001b[19;132He\r\n\u001b[18;132HeF\u001b[19;132HF\b f\r\n\u001b[19;132HG\b\b\t\tg\u001b[19;2H\bG\r\n\u001b[19;132H\r\n\u001b[18;1HH\u001b[18;132Hh\u001b[19;1HI\u001b[19;132Hi\r\n\u001b[18;132HiJ\u001b[19;132HJ\b j\r\n\u001b[19;132HK\b\b\t\tk\u001b[19;2H\bK\r\n\u001b[19;132H\r\n\u001b[18;1HL\u001b[18;132Hl\u001b[19;1HM\u001b[19;132Hm\r\n\u001b[18;132HmN\u001b[19;132HN\b n\r\n\u001b[19;132HO\b\b\t\to\u001b[19;2H\bO\r\n\u001b[19;132H\r\n\u001b[18;1HP\u001b[18;132Hp\u001b[19;1HQ\u001b[19;132Hq\r\n\u001b[18;132HqR\u001b[19;132HR\b r\r\n\u001b[19;132HS\b\b\t\ts\u001b[19;2H\bS\r\n\u001b[19;132H\r\n\u001b[18;1HT\u001b[18;132Ht\u001b[19;1HU\u001b[19;132Hu\r\n\u001b[18;132HuV\u001b[19;132HV\b v\r\n\u001b[19;132HW\b\b\t\tw\u001b[19;2H\bW\r\n\u001b[19;132H\r\n\u001b[18;1HX\u001b[18;132Hx\u001b[19;1HY\u001b[19;132Hy\r\n\u001b[18;132HyZ\u001b[19;132HZ\b z\r\n\u001b[?6l\u001b[r\u001b[22;1HPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/1-5",
    "content": "\u001b[?3l\u001b[2J\u001b[1;1HTest of cursor-control characters inside ESC sequences.\r\r\nBelow should be four identical lines:\r\r\n\r\r\nA B C D E F G H I\r\r\nA\u001b[2\bCB\u001b[2\bCC\u001b[2\bCD\u001b[2\bCE\u001b[2\bCF\u001b[2\bCG\u001b[2\bCH\u001b[2\bCI\u001b[2\bC\r\r\nA \u001b[\r2CB\u001b[\r4CC\u001b[\r6CD\u001b[\r8CE\u001b[\r10CF\u001b[\r12CG\u001b[\r14CH\u001b[\r16CI\r\r\n\u001b[20lA \u001b[1\u000bAB \u001b[1\u000bAC \u001b[1\u000bAD \u001b[1\u000bAE \u001b[1\u000bAF \u001b[1\u000bAG \u001b[1\u000bAH \u001b[1\u000bAI\u001b[1\u000bA\r\r\n\r\r\nPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/1-6",
    "content": "\u001b[2J\u001b[1;1HTest of leading zeros in ESC sequences.\r\r\nTwo lines below you should see the sentence \"This is a correct sentence\".\u001b[00000000004;000000001HT\u001b[00000000004;000000002Hh\u001b[00000000004;000000003Hi\u001b[00000000004;000000004Hs\u001b[00000000004;000000005H \u001b[00000000004;000000006Hi\u001b[00000000004;000000007Hs\u001b[00000000004;000000008H \u001b[00000000004;000000009Ha\u001b[00000000004;0000000010H \u001b[00000000004;0000000011Hc\u001b[00000000004;0000000012Ho\u001b[00000000004;0000000013Hr\u001b[00000000004;0000000014Hr\u001b[00000000004;0000000015He\u001b[00000000004;0000000016Hc\u001b[00000000004;0000000017Ht\u001b[00000000004;0000000018H \u001b[00000000004;0000000019Hs\u001b[00000000004;0000000020He\u001b[00000000004;0000000021Hn\u001b[00000000004;0000000022Ht\u001b[00000000004;0000000023He\u001b[00000000004;0000000024Hn\u001b[00000000004;0000000025Hc\u001b[00000000004;0000000026He\u001b[20;1HPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/2-1",
    "content": "\u001b[?3l\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/2-10",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BJump scroll up region [1..24] size 24 Line 1\r\nJump scroll up region [1..24] size 24 Line 2\r\nJump scroll up region [1..24] size 24 Line 3\r\nJump scroll up region [1..24] size 24 Line 4\r\nJump scroll up region [1..24] size 24 Line 5\r\nJump scroll up region [1..24] size 24 Line 6\r\nJump scroll up region [1..24] size 24 Line 7\r\nJump scroll up region [1..24] size 24 Line 8\r\nJump scroll up region [1..24] size 24 Line 9\r\nJump scroll up region [1..24] size 24 Line 10\r\nJump scroll up region [1..24] size 24 Line 11\r\nJump scroll up region [1..24] size 24 Line 12\r\nJump scroll up region [1..24] size 24 Line 13\r\nJump scroll up region [1..24] size 24 Line 14\r\nJump scroll up region [1..24] size 24 Line 15\r\nJump scroll up region [1..24] size 24 Line 16\r\nJump scroll up region [1..24] size 24 Line 17\r\nJump scroll up region [1..24] size 24 Line 18\r\nJump scroll up region [1..24] size 24 Line 19\r\nJump scroll up region [1..24] size 24 Line 20\r\nJump scroll up region [1..24] size 24 Line 21\r\nJump scroll up region [1..24] size 24 Line 22\r\nJump scroll up region [1..24] size 24 Line 23\r\nJump scroll up region [1..24] size 24 Line 24\r\nJump scroll up region [1..24] size 24 Line 25\r\nJump scroll up region [1..24] size 24 Line 26\r\nJump scroll up region [1..24] size 24 Line 27\r\nJump scroll up region [1..24] size 24 Line 28\r\nJump scroll up region [1..24] size 24 Line 29\r\n\u001b[24AJump scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-11",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BJump scroll up region [1..24] size 24 Line 1\r\nJump scroll up region [1..24] size 24 Line 2\r\nJump scroll up region [1..24] size 24 Line 3\r\nJump scroll up region [1..24] size 24 Line 4\r\nJump scroll up region [1..24] size 24 Line 5\r\nJump scroll up region [1..24] size 24 Line 6\r\nJump scroll up region [1..24] size 24 Line 7\r\nJump scroll up region [1..24] size 24 Line 8\r\nJump scroll up region [1..24] size 24 Line 9\r\nJump scroll up region [1..24] size 24 Line 10\r\nJump scroll up region [1..24] size 24 Line 11\r\nJump scroll up region [1..24] size 24 Line 12\r\nJump scroll up region [1..24] size 24 Line 13\r\nJump scroll up region [1..24] size 24 Line 14\r\nJump scroll up region [1..24] size 24 Line 15\r\nJump scroll up region [1..24] size 24 Line 16\r\nJump scroll up region [1..24] size 24 Line 17\r\nJump scroll up region [1..24] size 24 Line 18\r\nJump scroll up region [1..24] size 24 Line 19\r\nJump scroll up region [1..24] size 24 Line 20\r\nJump scroll up region [1..24] size 24 Line 21\r\nJump scroll up region [1..24] size 24 Line 22\r\nJump scroll up region [1..24] size 24 Line 23\r\nJump scroll up region [1..24] size 24 Line 24\r\nJump scroll up region [1..24] size 24 Line 25\r\nJump scroll up region [1..24] size 24 Line 26\r\nJump scroll up region [1..24] size 24 Line 27\r\nJump scroll up region [1..24] size 24 Line 28\r\nJump scroll up region [1..24] size 24 Line 29\r\n\u001b[24AJump scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[2J\u001b[23;24r\r\nOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be the one above the bottom of the screen. Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-12",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BJump scroll up region [1..24] size 24 Line 1\r\nJump scroll up region [1..24] size 24 Line 2\r\nJump scroll up region [1..24] size 24 Line 3\r\nJump scroll up region [1..24] size 24 Line 4\r\nJump scroll up region [1..24] size 24 Line 5\r\nJump scroll up region [1..24] size 24 Line 6\r\nJump scroll up region [1..24] size 24 Line 7\r\nJump scroll up region [1..24] size 24 Line 8\r\nJump scroll up region [1..24] size 24 Line 9\r\nJump scroll up region [1..24] size 24 Line 10\r\nJump scroll up region [1..24] size 24 Line 11\r\nJump scroll up region [1..24] size 24 Line 12\r\nJump scroll up region [1..24] size 24 Line 13\r\nJump scroll up region [1..24] size 24 Line 14\r\nJump scroll up region [1..24] size 24 Line 15\r\nJump scroll up region [1..24] size 24 Line 16\r\nJump scroll up region [1..24] size 24 Line 17\r\nJump scroll up region [1..24] size 24 Line 18\r\nJump scroll up region [1..24] size 24 Line 19\r\nJump scroll up region [1..24] size 24 Line 20\r\nJump scroll up region [1..24] size 24 Line 21\r\nJump scroll up region [1..24] size 24 Line 22\r\nJump scroll up region [1..24] size 24 Line 23\r\nJump scroll up region [1..24] size 24 Line 24\r\nJump scroll up region [1..24] size 24 Line 25\r\nJump scroll up region [1..24] size 24 Line 26\r\nJump scroll up region [1..24] size 24 Line 27\r\nJump scroll up region [1..24] size 24 Line 28\r\nJump scroll up region [1..24] size 24 Line 29\r\n\u001b[24AJump scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[2J\u001b[23;24r\r\nOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be the one above the bottom of the screen. Push <RETURN>\u001b[2J\u001b[?6l\u001b[24;1HOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be at the top of the screen. Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-13",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BJump scroll up region [1..24] size 24 Line 1\r\nJump scroll up region [1..24] size 24 Line 2\r\nJump scroll up region [1..24] size 24 Line 3\r\nJump scroll up region [1..24] size 24 Line 4\r\nJump scroll up region [1..24] size 24 Line 5\r\nJump scroll up region [1..24] size 24 Line 6\r\nJump scroll up region [1..24] size 24 Line 7\r\nJump scroll up region [1..24] size 24 Line 8\r\nJump scroll up region [1..24] size 24 Line 9\r\nJump scroll up region [1..24] size 24 Line 10\r\nJump scroll up region [1..24] size 24 Line 11\r\nJump scroll up region [1..24] size 24 Line 12\r\nJump scroll up region [1..24] size 24 Line 13\r\nJump scroll up region [1..24] size 24 Line 14\r\nJump scroll up region [1..24] size 24 Line 15\r\nJump scroll up region [1..24] size 24 Line 16\r\nJump scroll up region [1..24] size 24 Line 17\r\nJump scroll up region [1..24] size 24 Line 18\r\nJump scroll up region [1..24] size 24 Line 19\r\nJump scroll up region [1..24] size 24 Line 20\r\nJump scroll up region [1..24] size 24 Line 21\r\nJump scroll up region [1..24] size 24 Line 22\r\nJump scroll up region [1..24] size 24 Line 23\r\nJump scroll up region [1..24] size 24 Line 24\r\nJump scroll up region [1..24] size 24 Line 25\r\nJump scroll up region [1..24] size 24 Line 26\r\nJump scroll up region [1..24] size 24 Line 27\r\nJump scroll up region [1..24] size 24 Line 28\r\nJump scroll up region [1..24] size 24 Line 29\r\n\u001b[24AJump scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[2J\u001b[23;24r\r\nOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be the one above the bottom of the screen. Push <RETURN>\u001b[2J\u001b[?6l\u001b[24;1HOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be at the top of the screen. Push <RETURN>\u001b[1;24r\u001b[2J\u001b[1;20HGraphic rendition test pattern:\u001b[4;1H\u001b[0mvanilla\u001b[4;40H\u001b[0;1mbold\u001b[6;6H\u001b[;4munderline\u001b[6;45H\u001b[;1m\u001b[4mbold underline\u001b[8;1H\u001b[0;5mblink\u001b[8;40H\u001b[0;5;1mbold blink\u001b[10;6H\u001b[0;4;5munderline blink\u001b[10;45H\u001b[0;1;4;5mbold underline blink\u001b[12;1H\u001b[1;4;5;0;7mnegative\u001b[12;40H\u001b[0;1;7mbold negative\u001b[14;6H\u001b[0;4;7munderline negative\u001b[14;45H\u001b[0;1;4;7mbold underline negative\u001b[16;1H\u001b[1;4;;5;7mblink negative\u001b[16;40H\u001b[0;1;5;7mbold blink negative\u001b[18;6H\u001b[0;4;5;7munderline blink negative\u001b[18;45H\u001b[0;1;4;5;7mbold underline blink negative\u001b[m\u001b[?5l\u001b[23;1H\u001b[0KDark background. Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-14",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BJump scroll up region [1..24] size 24 Line 1\r\nJump scroll up region [1..24] size 24 Line 2\r\nJump scroll up region [1..24] size 24 Line 3\r\nJump scroll up region [1..24] size 24 Line 4\r\nJump scroll up region [1..24] size 24 Line 5\r\nJump scroll up region [1..24] size 24 Line 6\r\nJump scroll up region [1..24] size 24 Line 7\r\nJump scroll up region [1..24] size 24 Line 8\r\nJump scroll up region [1..24] size 24 Line 9\r\nJump scroll up region [1..24] size 24 Line 10\r\nJump scroll up region [1..24] size 24 Line 11\r\nJump scroll up region [1..24] size 24 Line 12\r\nJump scroll up region [1..24] size 24 Line 13\r\nJump scroll up region [1..24] size 24 Line 14\r\nJump scroll up region [1..24] size 24 Line 15\r\nJump scroll up region [1..24] size 24 Line 16\r\nJump scroll up region [1..24] size 24 Line 17\r\nJump scroll up region [1..24] size 24 Line 18\r\nJump scroll up region [1..24] size 24 Line 19\r\nJump scroll up region [1..24] size 24 Line 20\r\nJump scroll up region [1..24] size 24 Line 21\r\nJump scroll up region [1..24] size 24 Line 22\r\nJump scroll up region [1..24] size 24 Line 23\r\nJump scroll up region [1..24] size 24 Line 24\r\nJump scroll up region [1..24] size 24 Line 25\r\nJump scroll up region [1..24] size 24 Line 26\r\nJump scroll up region [1..24] size 24 Line 27\r\nJump scroll up region [1..24] size 24 Line 28\r\nJump scroll up region [1..24] size 24 Line 29\r\n\u001b[24AJump scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMJump scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[2J\u001b[23;24r\r\nOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be the one above the bottom of the screen. Push <RETURN>\u001b[2J\u001b[?6l\u001b[24;1HOrigin mode test. This line should be at the bottom of the screen.\u001b[1;1HThis line should be at the top of the screen. Push <RETURN>\u001b[1;24r\u001b[2J\u001b[1;20HGraphic rendition test pattern:\u001b[4;1H\u001b[0mvanilla\u001b[4;40H\u001b[0;1mbold\u001b[6;6H\u001b[;4munderline\u001b[6;45H\u001b[;1m\u001b[4mbold underline\u001b[8;1H\u001b[0;5mblink\u001b[8;40H\u001b[0;5;1mbold blink\u001b[10;6H\u001b[0;4;5munderline blink\u001b[10;45H\u001b[0;1;4;5mbold underline blink\u001b[12;1H\u001b[1;4;5;0;7mnegative\u001b[12;40H\u001b[0;1;7mbold negative\u001b[14;6H\u001b[0;4;7munderline negative\u001b[14;45H\u001b[0;1;4;7mbold underline negative\u001b[16;1H\u001b[1;4;;5;7mblink negative\u001b[16;40H\u001b[0;1;5;7mbold blink negative\u001b[18;6H\u001b[0;4;5;7munderline blink negative\u001b[18;45H\u001b[0;1;4;5;7mbold underline blink negative\u001b[m\u001b[?5l\u001b[23;1H\u001b[0KDark background. Push <RETURN>\u001b[?5h\u001b[23;1H\u001b[0KLight background. Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-15",
    "content": "\u001b[?5l\u001b[2J\u001b[8;12Hnormal\u001b[8;24Hbold\u001b[8;36Hunderscored\u001b[8;48Hblinking\u001b[8;60Hreversed\u001b[10;1Hstars:\u001b[12;1Hline:\u001b[14;1Hx'es:\u001b[16;1Hdiamonds:\u001b[10;12H\u001b[;0m\u001b(B\u001b)B\u000f*****\u001b7\u001b[1;1H\u001b[m\u001b(B\u001b)B\u000fA\u001b8*****\u001b[10;24H\u001b[;1m\u001b(B\u001b)B\u000f*****\u001b7\u001b[1;2H\u001b[m\u001b(B\u001b)B\u000fA\u001b8*****\u001b[10;36H\u001b[;4m\u001b(B\u001b)B\u000f*****\u001b7\u001b[1;3H\u001b[m\u001b(B\u001b)B\u000fA\u001b8*****\u001b[10;48H\u001b[;5m\u001b(B\u001b)B\u000f*****\u001b7\u001b[1;4H\u001b[m\u001b(B\u001b)B\u000fA\u001b8*****\u001b[10;60H\u001b[;7m\u001b(B\u001b)B\u000f*****\u001b7\u001b[1;5H\u001b[m\u001b(B\u001b)B\u000fA\u001b8*****\u001b[12;12H\u001b[;0m\u001b(0\u001b)B\u000fqqqqq\u001b7\u001b[2;1H\u001b[m\u001b(B\u001b)B\u000fA\u001b8qqqqq\u001b[12;24H\u001b[;1m\u001b(0\u001b)B\u000fqqqqq\u001b7\u001b[2;2H\u001b[m\u001b(B\u001b)B\u000fA\u001b8qqqqq\u001b[12;36H\u001b[;4m\u001b(0\u001b)B\u000fqqqqq\u001b7\u001b[2;3H\u001b[m\u001b(B\u001b)B\u000fA\u001b8qqqqq\u001b[12;48H\u001b[;5m\u001b(0\u001b)B\u000fqqqqq\u001b7\u001b[2;4H\u001b[m\u001b(B\u001b)B\u000fA\u001b8qqqqq\u001b[12;60H\u001b[;7m\u001b(0\u001b)B\u000fqqqqq\u001b7\u001b[2;5H\u001b[m\u001b(B\u001b)B\u000fA\u001b8qqqqq\u001b[14;12H\u001b[;0m\u001b(B\u001b)B\u000fxxxxx\u001b7\u001b[3;1H\u001b[m\u001b(B\u001b)B\u000fA\u001b8xxxxx\u001b[14;24H\u001b[;1m\u001b(B\u001b)B\u000fxxxxx\u001b7\u001b[3;2H\u001b[m\u001b(B\u001b)B\u000fA\u001b8xxxxx\u001b[14;36H\u001b[;4m\u001b(B\u001b)B\u000fxxxxx\u001b7\u001b[3;3H\u001b[m\u001b(B\u001b)B\u000fA\u001b8xxxxx\u001b[14;48H\u001b[;5m\u001b(B\u001b)B\u000fxxxxx\u001b7\u001b[3;4H\u001b[m\u001b(B\u001b)B\u000fA\u001b8xxxxx\u001b[14;60H\u001b[;7m\u001b(B\u001b)B\u000fxxxxx\u001b7\u001b[3;5H\u001b[m\u001b(B\u001b)B\u000fA\u001b8xxxxx\u001b[16;12H\u001b[;0m\u001b(0\u001b)B\u000f`````\u001b7\u001b[4;1H\u001b[m\u001b(B\u001b)B\u000fA\u001b8`````\u001b[16;24H\u001b[;1m\u001b(0\u001b)B\u000f`````\u001b7\u001b[4;2H\u001b[m\u001b(B\u001b)B\u000fA\u001b8`````\u001b[16;36H\u001b[;4m\u001b(0\u001b)B\u000f`````\u001b7\u001b[4;3H\u001b[m\u001b(B\u001b)B\u000fA\u001b8`````\u001b[16;48H\u001b[;5m\u001b(0\u001b)B\u000f`````\u001b7\u001b[4;4H\u001b[m\u001b(B\u001b)B\u000fA\u001b8`````\u001b[16;60H\u001b[;7m\u001b(0\u001b)B\u000f`````\u001b7\u001b[4;5H\u001b[m\u001b(B\u001b)B\u000fA\u001b8`````\u001b[0m\u001b(B\u001b)B\u000f\u001b[21;1HTest of the SAVE/RESTORE CURSOR feature. There should\r\r\nbe ten characters of each flavour, and a rectangle\r\r\nof 5 x 4 A's filling the top left of the screen.\r\r\nPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/2-2",
    "content": "\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/2-3",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-4",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-5",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-6",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-7",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>"
  },
  {
    "path": "test/test_files/vttest/2-8",
    "content": "\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\r\n"
  },
  {
    "path": "test/test_files/vttest/2-9",
    "content": "\u001b[0c\u001b[?1l\u001b[?3l\u001b[?4l\u001b[?5l\u001b[?6l\u001b[?7h\u001b[?8l\u001b[?40h\u001b[?45l\u001b[r\u001b[0m\u001b[2J\u001b[3;10HVT100 test program, version 2.7 (20140305)\u001b[4;10HLine speed 38400bd \u001b[5;10HChoose test type:\r\r\n\u001b[6;1H\u001b[0J\r\r\n          0. Exit\r\n          1. Test of cursor movements\r\n          2. Test of screen features\r\n          3. Test of character sets\r\n          4. Test of double-sized characters\r\n          5. Test of keyboard\r\n          6. Test of terminal reports\r\n          7. Test of VT52 mode\r\n          8. Test of VT102 features (Insert/Delete Char/Line)\r\n          9. Test of known bugs\r\n          10. Test of reset and self-test\r\n          11. Test non-VT100 (e.g., VT220, XTERM) terminals\r\n          12. Modify test-parameters\r\n\r\n          Enter choice number (0 - 12): 2\r\n\u001b[2J\u001b[1;1H\u001b[?7h****************************************************************************************************************************************************************\u001b[?7l\u001b[3;1H****************************************************************************************************************************************************************\u001b[?7h\u001b[5;1HThis should be three identical lines of *'s completely filling\r\r\nthe top of the screen without any empty lines between.\r\r\n(Test of WRAP AROUND mode setting.)\r\r\nPush <RETURN>\u001b[2J\u001b[3g\u001b[1;1H\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[3C\u001bH\u001b[1;4H\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[0g\u001b[6C\u001b[1;7H\u001b[1g\u001b[2g\u001b[1;1H\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\t*\u001b[2;2H     *     *     *     *     *     *     *     *     *     *     *     *     *\u001b[4;1HTest of TAB setting/resetting. These two lines\r\r\nshould look the same. Push <RETURN>\u001b[?5h\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, light background.\u001b[4;4HThis is 132 column mode, light background.\u001b[5;5HThis is 132 column mode, light background.\u001b[6;6HThis is 132 column mode, light background.\u001b[7;7HThis is 132 column mode, light background.\u001b[8;8HThis is 132 column mode, light background.\u001b[9;9HThis is 132 column mode, light background.\u001b[10;10HThis is 132 column mode, light background.\u001b[11;11HThis is 132 column mode, light background.\u001b[12;12HThis is 132 column mode, light background.\u001b[13;13HThis is 132 column mode, light background.\u001b[14;14HThis is 132 column mode, light background.\u001b[15;15HThis is 132 column mode, light background.\u001b[16;16HThis is 132 column mode, light background.\u001b[17;17HThis is 132 column mode, light background.\u001b[18;18HThis is 132 column mode, light background.\u001b[19;19HThis is 132 column mode, light background.\u001b[20;20HThis is 132 column mode, light background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, light background.\u001b[4;4HThis is 80 column mode, light background.\u001b[5;5HThis is 80 column mode, light background.\u001b[6;6HThis is 80 column mode, light background.\u001b[7;7HThis is 80 column mode, light background.\u001b[8;8HThis is 80 column mode, light background.\u001b[9;9HThis is 80 column mode, light background.\u001b[10;10HThis is 80 column mode, light background.\u001b[11;11HThis is 80 column mode, light background.\u001b[12;12HThis is 80 column mode, light background.\u001b[13;13HThis is 80 column mode, light background.\u001b[14;14HThis is 80 column mode, light background.\u001b[15;15HThis is 80 column mode, light background.\u001b[16;16HThis is 80 column mode, light background.\u001b[17;17HThis is 80 column mode, light background.\u001b[18;18HThis is 80 column mode, light background.\u001b[19;19HThis is 80 column mode, light background.\u001b[20;20HThis is 80 column mode, light background.Push <RETURN>\u001b[?5l\u001b[?3h\u001b[2J\u001b[1;1H\u001b[3g\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[8C\u001bH\u001b[1;1H12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\u001b[3;3HThis is 132 column mode, dark background.\u001b[4;4HThis is 132 column mode, dark background.\u001b[5;5HThis is 132 column mode, dark background.\u001b[6;6HThis is 132 column mode, dark background.\u001b[7;7HThis is 132 column mode, dark background.\u001b[8;8HThis is 132 column mode, dark background.\u001b[9;9HThis is 132 column mode, dark background.\u001b[10;10HThis is 132 column mode, dark background.\u001b[11;11HThis is 132 column mode, dark background.\u001b[12;12HThis is 132 column mode, dark background.\u001b[13;13HThis is 132 column mode, dark background.\u001b[14;14HThis is 132 column mode, dark background.\u001b[15;15HThis is 132 column mode, dark background.\u001b[16;16HThis is 132 column mode, dark background.\u001b[17;17HThis is 132 column mode, dark background.\u001b[18;18HThis is 132 column mode, dark background.\u001b[19;19HThis is 132 column mode, dark background.\u001b[20;20HThis is 132 column mode, dark background.Push <RETURN>\u001b[?3l\u001b[2J\u001b[1;1H1234567890123456789012345678901234567890123456789012345678901234567890123456789\u001b[3;3HThis is 80 column mode, dark background.\u001b[4;4HThis is 80 column mode, dark background.\u001b[5;5HThis is 80 column mode, dark background.\u001b[6;6HThis is 80 column mode, dark background.\u001b[7;7HThis is 80 column mode, dark background.\u001b[8;8HThis is 80 column mode, dark background.\u001b[9;9HThis is 80 column mode, dark background.\u001b[10;10HThis is 80 column mode, dark background.\u001b[11;11HThis is 80 column mode, dark background.\u001b[12;12HThis is 80 column mode, dark background.\u001b[13;13HThis is 80 column mode, dark background.\u001b[14;14HThis is 80 column mode, dark background.\u001b[15;15HThis is 80 column mode, dark background.\u001b[16;16HThis is 80 column mode, dark background.\u001b[17;17HThis is 80 column mode, dark background.\u001b[18;18HThis is 80 column mode, dark background.\u001b[19;19HThis is 80 column mode, dark background.\u001b[20;20HThis is 80 column mode, dark background.Push <RETURN>\u001b[2J\u001b[?6h\u001b[?4h\u001b[12;13r\u001b[2J\u001b[24BSoft scroll up region [12..13] size 2 Line 1\r\nSoft scroll up region [12..13] size 2 Line 2\r\nSoft scroll up region [12..13] size 2 Line 3\r\nSoft scroll up region [12..13] size 2 Line 4\r\nSoft scroll up region [12..13] size 2 Line 5\r\nSoft scroll up region [12..13] size 2 Line 6\r\nSoft scroll up region [12..13] size 2 Line 7\r\nSoft scroll up region [12..13] size 2 Line 8\r\nSoft scroll up region [12..13] size 2 Line 9\r\nSoft scroll up region [12..13] size 2 Line 10\r\nSoft scroll up region [12..13] size 2 Line 11\r\nSoft scroll up region [12..13] size 2 Line 12\r\nSoft scroll up region [12..13] size 2 Line 13\r\nSoft scroll up region [12..13] size 2 Line 14\r\nSoft scroll up region [12..13] size 2 Line 15\r\nSoft scroll up region [12..13] size 2 Line 16\r\nSoft scroll up region [12..13] size 2 Line 17\r\nSoft scroll up region [12..13] size 2 Line 18\r\nSoft scroll up region [12..13] size 2 Line 19\r\nSoft scroll up region [12..13] size 2 Line 20\r\nSoft scroll up region [12..13] size 2 Line 21\r\nSoft scroll up region [12..13] size 2 Line 22\r\nSoft scroll up region [12..13] size 2 Line 23\r\nSoft scroll up region [12..13] size 2 Line 24\r\nSoft scroll up region [12..13] size 2 Line 25\r\nSoft scroll up region [12..13] size 2 Line 26\r\nSoft scroll up region [12..13] size 2 Line 27\r\nSoft scroll up region [12..13] size 2 Line 28\r\nSoft scroll up region [12..13] size 2 Line 29\r\n\u001b[24ASoft scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMSoft scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[1;24r\u001b[2J\u001b[24BSoft scroll up region [1..24] size 24 Line 1\r\nSoft scroll up region [1..24] size 24 Line 2\r\nSoft scroll up region [1..24] size 24 Line 3\r\nSoft scroll up region [1..24] size 24 Line 4\r\nSoft scroll up region [1..24] size 24 Line 5\r\nSoft scroll up region [1..24] size 24 Line 6\r\nSoft scroll up region [1..24] size 24 Line 7\r\nSoft scroll up region [1..24] size 24 Line 8\r\nSoft scroll up region [1..24] size 24 Line 9\r\nSoft scroll up region [1..24] size 24 Line 10\r\nSoft scroll up region [1..24] size 24 Line 11\r\nSoft scroll up region [1..24] size 24 Line 12\r\nSoft scroll up region [1..24] size 24 Line 13\r\nSoft scroll up region [1..24] size 24 Line 14\r\nSoft scroll up region [1..24] size 24 Line 15\r\nSoft scroll up region [1..24] size 24 Line 16\r\nSoft scroll up region [1..24] size 24 Line 17\r\nSoft scroll up region [1..24] size 24 Line 18\r\nSoft scroll up region [1..24] size 24 Line 19\r\nSoft scroll up region [1..24] size 24 Line 20\r\nSoft scroll up region [1..24] size 24 Line 21\r\nSoft scroll up region [1..24] size 24 Line 22\r\nSoft scroll up region [1..24] size 24 Line 23\r\nSoft scroll up region [1..24] size 24 Line 24\r\nSoft scroll up region [1..24] size 24 Line 25\r\nSoft scroll up region [1..24] size 24 Line 26\r\nSoft scroll up region [1..24] size 24 Line 27\r\nSoft scroll up region [1..24] size 24 Line 28\r\nSoft scroll up region [1..24] size 24 Line 29\r\n\u001b[24ASoft scroll down region [1..24] size 24 Line 1\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 2\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 3\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 4\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 5\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 6\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 7\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 8\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 9\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 10\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 11\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 12\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 13\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 14\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 15\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 16\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 17\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 18\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 19\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 20\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 21\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 22\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 23\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 24\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 25\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 26\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 27\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 28\r\n\u001bM\u001bMSoft scroll down region [1..24] size 24 Line 29\r\n\u001bM\u001bMPush <RETURN>\u001b[?4l\u001b[12;13r\u001b[2J\u001b[24BJump scroll up region [12..13] size 2 Line 1\r\nJump scroll up region [12..13] size 2 Line 2\r\nJump scroll up region [12..13] size 2 Line 3\r\nJump scroll up region [12..13] size 2 Line 4\r\nJump scroll up region [12..13] size 2 Line 5\r\nJump scroll up region [12..13] size 2 Line 6\r\nJump scroll up region [12..13] size 2 Line 7\r\nJump scroll up region [12..13] size 2 Line 8\r\nJump scroll up region [12..13] size 2 Line 9\r\nJump scroll up region [12..13] size 2 Line 10\r\nJump scroll up region [12..13] size 2 Line 11\r\nJump scroll up region [12..13] size 2 Line 12\r\nJump scroll up region [12..13] size 2 Line 13\r\nJump scroll up region [12..13] size 2 Line 14\r\nJump scroll up region [12..13] size 2 Line 15\r\nJump scroll up region [12..13] size 2 Line 16\r\nJump scroll up region [12..13] size 2 Line 17\r\nJump scroll up region [12..13] size 2 Line 18\r\nJump scroll up region [12..13] size 2 Line 19\r\nJump scroll up region [12..13] size 2 Line 20\r\nJump scroll up region [12..13] size 2 Line 21\r\nJump scroll up region [12..13] size 2 Line 22\r\nJump scroll up region [12..13] size 2 Line 23\r\nJump scroll up region [12..13] size 2 Line 24\r\nJump scroll up region [12..13] size 2 Line 25\r\nJump scroll up region [12..13] size 2 Line 26\r\nJump scroll up region [12..13] size 2 Line 27\r\nJump scroll up region [12..13] size 2 Line 28\r\nJump scroll up region [12..13] size 2 Line 29\r\n\u001b[24AJump scroll down region [12..13] size 2 Line 1\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 2\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 3\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 4\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 5\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 6\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 7\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 8\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 9\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 10\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 11\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 12\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 13\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 14\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 15\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 16\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 17\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 18\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 19\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 20\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 21\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 22\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 23\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 24\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 25\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 26\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 27\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 28\r\n\u001bM\u001bMJump scroll down region [12..13] size 2 Line 29\r\n\u001bM\u001bMPush <RETURN>"
  },
  {
    "path": "test/utils/ManPages_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\n\nimport {\n  combineManPageLines,\n  preprocessManPage,\n  extractManPageSections,\n  extractManPageSectionParagraphs,\n  suggestionFromFlagParagraph,\n} from \"../../src/utils/ManPageParsingUtils\";\n\ndescribe(\"man page line combiner\", () => {\n  it(\"combines lines with correct spacing\", () => {\n    expect(combineManPageLines([\n      \"   first line     \",\n      \"  second line       \",\n    ])).to.eql(\"first line second line\");\n  });\n\n  it(\"correctly handles words split across lines\", () => {\n    expect(combineManPageLines([\n      \"this is com-\",\n      \"bined\",\n    ])).to.eql(\"this is combined\");\n  });\n});\n\ndescribe(\"man page preprocessor\", () => {\n  it(\"strips whitespace and applies backspace literals\", () => {\n    expect(preprocessManPage(\"   ab\\x08   \")).to.eql(\"a\");\n  });\n});\n\ndescribe(\"man page section extractor\", () => {\n  it(\"extracts sections\", () => {\n    expect(extractManPageSections(\"DESCRIPTION\\n desc\\n\\nNAME\\n name\")).to.eql({\n      DESCRIPTION: [\" desc\", \"\"],\n      NAME: [\" name\"],\n    });\n  });\n});\n\ndescribe(\"man page paragraph extraction\", () => {\n  it(\"extracts paragraphs\", () => {\n    expect(extractManPageSectionParagraphs([\n      \"p1\",\n      \"p1\",\n      \"\",\n      \"p2\",\n      \"p2\",\n    ])).to.eql([\n      [\"p1\", \"p1\"],\n      [\"p2\", \"p2\"],\n    ]);\n  });\n\n  it(\"doesn't output empty paragraphs\", () => {\n    expect(extractManPageSectionParagraphs([\n      \"p1\",\n      \"p1\",\n      \"\",\n      \"\",\n      \"\",\n      \"\",\n      \"\",\n      \"p2\",\n      \"p2\",\n    ])).to.eql([\n      [\"p1\", \"p1\"],\n      [\"p2\", \"p2\"],\n    ]);\n  });\n\n  it(\"can handle flag descriptions that have blank lines in the middle\", () => {\n    expect(extractManPageSectionParagraphs([\n      \"     -f1   line one\",\n      \"           line two\",\n      \"\",\n      \"           line three\",\n      \"\",\n      \"     -f2   line one\",\n    ])).to.eql([\n      [\n        \"     -f1   line one\",\n        \"           line two\",\n        \"           line three\",\n      ],\n      [\"     -f2   line one\"],\n    ]);\n  });\n\n  it(\"can handle flag descriptions that have indentation like df's -T option\", () => {\n    expect(extractManPageSectionParagraphs([\n      \"     -f        line one\",\n      \"               line two\",\n      \"\",\n      \"                      indented\",\n      \"\",\n      \"               line three\",\n      \"\",\n      \"     -g        line one\",\n    ])).to.eql([\n      [\n        \"     -f        line one\",\n        \"               line two\",\n        \"                      indented\",\n        \"               line three\",\n      ],\n      [\"     -g        line one\"],\n    ]);\n  });\n});\n\ndescribe(\"suggestion parser\", () => {\n  it(\"can handle short flags without arguments\", () => {\n    expect(suggestionFromFlagParagraph([\n      \"   -f  flag with\",\n      \"       description\",\n    ])).to.eql({\n      label: \"-f\",\n      detail: \"flag with description\",\n    });\n  });\n\n  it(\"can handle short flags with arguments\", () => {\n    expect(suggestionFromFlagParagraph([\n      \"   -f arg\",\n      \"       flag with\",\n      \"       description\",\n    ])).to.eql({\n      label: \"-f\",\n      detail: \"flag with description\",\n    });\n  });\n\n  // DESCRIPTION section can contain things other than flags\n  it(\"returns undefined if attempting to parse paragraph with no flag\", () => {\n    expect(suggestionFromFlagParagraph([\n      \"        no flag\",\n    ])).to.eql(undefined);\n  });\n});\n"
  },
  {
    "path": "test/utils/common_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\nimport {commonPrefix, fuzzyMatch, normalizeProcessInput} from \"../../src/utils/Common\";\n\ninterface SimulatedKeyboardEvent {\n    ctrlKey?: boolean;\n    altKey?: boolean;\n    shiftKey?: boolean;\n    keyCode: number;\n    key: string;\n}\n\nfunction simulateKeyboardEvent(event: SimulatedKeyboardEvent) {\n    return event as KeyboardEvent;\n}\n\ndescribe(\"common utils\", () => {\n    describe(\"commonPrefix\", () => {\n        it(\"returns the whole string for the same strings\", () => {\n            expect(commonPrefix(\"abc\", \"abc\")).to.eql(\"abc\");\n        });\n    });\n\n    describe(\"fuzzyMatch\", () => {\n        it(\"matches beginning of string\", () => {\n            expect(fuzzyMatch(\"com\", \"commit\")).to.eql(true);\n        });\n\n        it(\"matches beginning of token\", () => {\n            expect(fuzzyMatch(\"nam\", \"file_name\")).to.eql(true);\n        });\n    });\n\n    describe(\"normalizeProcessInput\", () => {\n        it(\"handles Ctrl+[\", () => {\n            const event = simulateKeyboardEvent({ctrlKey: true, keyCode: 219, key: \"[\"});\n            const escape = String.fromCharCode(27);\n\n            expect(normalizeProcessInput(event, false)).to.eql(escape);\n        });\n\n        it(\"handles Ctrl+J\", () => {\n            const event = simulateKeyboardEvent({ctrlKey: true, keyCode: 219, key: \"[\"});\n            const escape = String.fromCharCode(27);\n\n            expect(normalizeProcessInput(event, false)).to.eql(escape);\n        });\n    });\n});\n"
  },
  {
    "path": "test/utils/history_trie_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\nimport {HistoryTrie} from \"../../src/utils/HistoryTrie\";\n\nfunction getSuggestions(history: string[], input: string): string[] {\n    const trie = new HistoryTrie();\n    history.forEach(string => trie.add(string));\n    return trie.getContinuationsFor(input).map(prefix => prefix.value + (prefix.space ? \" \" : \"\"));\n}\n\ndescribe(\"HistoryTrie\", () => {\n    it(\"finds next common prefixes\", () => {\n        const history = [\n            \"git commit\",\n            \"git checkout\",\n        ];\n        const input = \"git \";\n        const suggestions = [\n            \"commit\",\n            \"checkout\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"finds first word\", () => {\n        const history = [\n            \"git commit\",\n            \"git checkout\",\n        ];\n        const input = \"gi\";\n        const suggestions = [\n            \"git \",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"finds single continuation\", () => {\n        const history = [\n            \"git commit\",\n            \"git checkout\",\n        ];\n        const input = \"git co\";\n        const suggestions = [\n            \"commit\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"finds next word\", () => {\n        const history = [\n            \"git commit\",\n            \"git checkout master\",\n        ];\n        const input = \"git c\";\n        const suggestions = [\n            \"commit\",\n            \"checkout \",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"finds longest prefix\", () => {\n        const history = [\n            \"git commit\",\n            \"git checkout master --option\",\n        ];\n        const input = \"git ch\";\n        const suggestions = [\n            \"checkout \",\n            \"checkout master --option\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"is ordered by frequency\", () => {\n        const history = [\n            \"git status\",\n            \"git pull\",\n            \"git pull\",\n        ];\n        const input = \"git \";\n        const suggestions = [\n            \"pull\",\n            \"status\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"is fuzzy matches\", () => {\n        const history = [\n            \"git cherry-pick\",\n        ];\n        const input = \"git pi\";\n        const suggestions = [\n            \"cherry-pick\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"considers a string literal a single token\", () => {\n        const history = [\n            \"git commit -m 'first message'\",\n            \"git commit -m 'second message'\",\n        ];\n        const input = \"git commit -m \";\n        const suggestions = [\n            \"'first message'\",\n            \"'second message'\",\n        ];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n\n    it(\"gives no results for empty input\", () => {\n        const history = [\n            \"git log\",\n        ];\n        const input = \"\";\n        const suggestions: string[] = [];\n\n        expect(getSuggestions(history, input)).to.eql(suggestions);\n    });\n});\n"
  },
  {
    "path": "test/utils/ordered_set_spec.ts",
    "content": "import \"mocha\";\nimport {expect} from \"chai\";\nimport {OrderedSet} from \"../../src/utils/OrderedSet\";\n\ndescribe(\"ordered set\", () => {\n    describe(\"prepend\", () => {\n        it(\"doesn't keep two elements with the same values\", () => {\n            const set = new OrderedSet<string>();\n\n            set.prepend(\"foo\");\n            set.prepend(\"foo\");\n\n            expect(set.size).to.eq(1);\n        });\n\n        it(\"moves an element to the beginning if it already exists\", () => {\n            const set = new OrderedSet<string>();\n\n            set.prepend(\"foo\");\n            set.prepend(\"bar\");\n            set.prepend(\"foo\");\n\n            expect(set.at(0)).to.eq(\"foo\");\n        });\n    });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES6\",\n    \"module\": \"commonjs\",\n    \"removeComments\": true,\n    \"preserveConstEnums\": true,\n    \"moduleResolution\": \"node\",\n    \"experimentalDecorators\": true,\n    \"noImplicitAny\": true,\n    \"noEmitOnError\": true,\n    \"jsx\": \"react\",\n    \"outDir\": \"compiled/src\",\n    \"strictNullChecks\": true,\n    \"noImplicitThis\": true,\n    \"inlineSourceMap\": true,\n    \"lib\": [\"es2015\", \"es2017\", \"dom\"]\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"typings\",\n    \"test\"\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"rules\": {\n    \"align\": [\n      true,\n      \"parameters\",\n      \"arguments\",\n      \"statements\"\n    ],\n    \"ban\": [\n      true,\n      [\"Object\", \"assign\", \"Use spread syntax ({...a, ...b}) instead.\"]\n    ],\n    \"class-name\": true,\n    \"comment-format\": [\n      true,\n      \"check-space\"\n    ],\n    \"eofline\": true,\n    \"forin\": true,\n    \"indent\": [\n      true,\n      \"spaces\"\n    ],\n    \"interface-name\": false,\n    \"jsdoc-format\": true,\n    \"label-position\": true,\n    \"max-line-length\": [\n      true,\n      200\n    ],\n    \"member-access\": false,\n    \"member-ordering\": [\n      true,\n      \"public-before-private\",\n      \"static-before-instance\",\n      \"variables-before-functions\"\n    ],\n    \"no-any\": false,\n    \"no-arg\": true,\n    \"no-bitwise\": false,\n    \"no-conditional-assignment\": true,\n    \"no-consecutive-blank-lines\": false,\n    \"no-console\": [\n      true,\n      \"debug\",\n      \"info\",\n      \"time\",\n      \"timeEnd\",\n      \"trace\"\n    ],\n    \"no-construct\": true,\n    \"no-parameter-properties\": false,\n    \"no-debugger\": true,\n    \"no-duplicate-variable\": true,\n    \"no-empty\": true,\n    \"no-eval\": true,\n    \"no-inferrable-types\": false,\n    \"no-internal-module\": true,\n    \"no-null-keyword\": false,\n    \"no-require-imports\": false,\n    \"no-shadowed-variable\": false,\n    \"no-string-literal\": true,\n    \"no-switch-case-fall-through\": true,\n    \"no-trailing-whitespace\": true,\n    \"no-unused-expression\": true,\n    \"no-use-before-declare\": false,\n    \"no-var-keyword\": true,\n    \"no-var-requires\": false,\n    \"object-literal-sort-keys\": false,\n    \"one-line\": [\n      true,\n      \"check-open-brace\",\n      \"check-catch\",\n      \"check-else\",\n      \"check-whitespace\"\n    ],\n    \"quotemark\": [\n      true,\n      \"double\",\n      \"avoid-escape\"\n    ],\n    \"radix\": true,\n    \"semicolon\": true,\n    \"switch-default\": true,\n    \"trailing-comma\": [\n      true,\n      {\n        \"multiline\": \"always\",\n        \"singleline\": \"never\"\n      }\n    ],\n    \"triple-equals\": [\n      true,\n      \"allow-null-check\"\n    ],\n    \"typedef-whitespace\": [\n      true,\n      {\n        \"call-signature\": \"nospace\",\n        \"index-signature\": \"nospace\",\n        \"parameter\": \"nospace\",\n        \"property-declaration\": \"nospace\",\n        \"variable-declaration\": \"nospace\"\n      }\n    ],\n    \"variable-name\": false,\n    \"whitespace\": [\n      true,\n      \"check-branch\",\n      \"check-decl\",\n      \"check-operator\",\n      \"check-separator\",\n      \"check-type\"\n    ]\n  }\n}\n"
  },
  {
    "path": "typings/Interfaces.d.ts",
    "content": "interface Size {\n    height: number;\n    width: number;\n}\n\ninterface Dimensions {\n    columns: number;\n    rows: number;\n}\n\ninterface Advancement {\n    vertical?: number;\n    horizontal?: number;\n}\n\ninterface RowColumn {\n    columnIndex: number;\n    rowIndex: number;\n}\n\ntype GitState = {\n  kind: \"repository\",\n  branch: string,\n  status: \"dirty\" | \"clean\";\n} | { kind: \"not-repository\"; };\n\ninterface Margins {\n    top: number;\n    bottom?: number;\n    left: number;\n    right?: number;\n}\n\ninterface Dictionary<T> {\n    [index: string]: T;\n}\n\ninterface ProcessEnvironment extends Dictionary<string> {\n    PWD: string;\n}\n\ntype EscapedShellWord = string & {__isEscapedShellToken: any};\ntype FullPath = string & { __isFullPath: boolean };\ntype ExistingAlias = string & { __isExistingAlias: boolean };\ntype OneBasedPosition = number;\n"
  },
  {
    "path": "typings/Overrides.d.ts",
    "content": "interface IntersectionObserverEntry {\n    readonly time: number;\n    readonly rootBounds: ClientRect | DOMRect;\n    readonly boundingClientRect: ClientRect | DOMRect;\n    readonly intersectionRect: ClientRect | DOMRect;\n    readonly intersectionRatio: number;\n    readonly target: Element;\n}\n\ninterface IntersectionObserverInit {\n    // The root to use for intersection. If not provided, use the top-level document’s viewport.\n    root?: Element | null;\n    // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.  If an explicit\n    // root element is specified, components may be percentages of the root element size.  If no\n    // explicit root element is specified, using a percentage here is an error.\n    // \"5px\"\n    // \"10% 20%\"\n    // \"-10px 5px 5px\"\n    // \"-10px -10px 5px 5px\"\n    rootMargin?: string;\n    // Threshold(s) at which to trigger callback, specified as a ratio, or list of ratios,\n    // of (visible area / total area) of the observed element (hence all entries must be\n    // in the range [0, 1]).  Callback will be invoked when the visible ratio of the observed\n    // element crosses a threshold in the list.\n    threshold?: number | number[];\n}\n\ninterface Window {\n    DEBUG: boolean;\n    search: any;\n}\n\ndeclare class AnsiParser {\n    constructor(callbacks: Dictionary<Function>)\n\n    parse(data: string): any;\n}\n\ninterface Array<T> {\n    includes(value: T): boolean;\n}\n\ninterface NodeBuffer extends Uint8Array {\n    fill(value: number, offset?: number, end?: number): this;\n}\n\ninterface ObjectConstructor {\n    assign<A, B, C, D, E, F>(a: A, b: B, c: C, d: D, e: E, f: F): A & B & C & D & E & F;\n}\n"
  },
  {
    "path": "typings/child-process-promise.d.ts",
    "content": "declare module \"child-process-promise\" {\n  function execFile(file: string, args: string[], options: {}): Promise<{stdout: string, stderr: string}>\n}\n"
  },
  {
    "path": "typings/dirStat.d.ts",
    "content": "declare module \"dirStat\" {\n  function dirStat(path: string, cb: (err: any, results: any) => void): void\n}\n"
  },
  {
    "path": "typings/mode-to-permissions.d.ts",
    "content": "interface PermittedGroups {\n    owner: boolean;\n    group: boolean;\n    others: boolean;\n}\n\ninterface Permissions {\n    read: PermittedGroups;\n    write: PermittedGroups;\n    execute: PermittedGroups;\n}\n\ndeclare module \"mode-to-permissions\" {\n    function modeToPermissions(mode: number): Permissions;\n    namespace modeToPermissions {}\n    export = modeToPermissions;\n}\n"
  },
  {
    "path": "typings/uuid.d.ts",
    "content": "declare module \"uuid\" {\n  function v4(): string\n}\n"
  }
]