├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── images ├── icon.png ├── icon.rli └── preview.png ├── package-lock.json ├── package.json ├── sample ├── ExampleEditor.png ├── ExampleInline.png ├── ExampleResult.gif ├── ExampleResult.png ├── ExampleResultDarkTheme.gif ├── ExampleResultDarkTheme.png ├── ExampleResultLightTheme.gif ├── ExampleResultLightTheme.png ├── ExampleSource.png ├── generate_sample.py ├── generate_sample2.py ├── sample.vt100 ├── sample2.vt100 └── sample_inline.py ├── snippets └── vt100.json ├── src ├── configurationManager.ts ├── content │ ├── htmlContentProvider.ts │ └── textContentProvider.ts ├── decorationManager.ts ├── exportManager.ts ├── extension.ts ├── previewManager.ts ├── util.ts └── vt100Parser.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.ts] 2 | end_of_line = lf -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /**@type {import('eslint').Linter.Config} */ 2 | // eslint-disable-next-line no-undef 3 | module.exports = { 4 | root: true, 5 | parser: '@typescript-eslint/parser', 6 | plugins: [ 7 | '@typescript-eslint', 8 | ], 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:@typescript-eslint/recommended', 12 | ], 13 | rules: { 14 | 'semi': [2, "always"], 15 | '@typescript-eslint/no-unused-vars': 0, 16 | '@typescript-eslint/no-explicit-any': 0, 17 | '@typescript-eslint/explicit-module-boundary-types': 0, 18 | '@typescript-eslint/no-non-null-assertion': 0, 19 | "no-constant-condition": 0 20 | } 21 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ts text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "dbaeumer.vscode-eslint" 8 | ] 9 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [{ 8 | "name": "Run Extension", 9 | "type": "extensionHost", 10 | "request": "launch", 11 | "runtimeExecutable": "${execPath}", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}", 14 | "${workspaceFolder}/sample", 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Run Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": false 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | out/**/*.map 5 | src/** 6 | sample/** 7 | images/**/*.rli 8 | .gitignore 9 | tsconfig.json 10 | tslint.json -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.1.0 4 | 5 | - Added support for inline escape sequence highlighting (with includes and excludes) 6 | - Added support for opening files with custom editor (by default) 7 | - Improved handling of previews (side-by-side vs dedicated) 8 | - Fixed bug on text export where escape sequences were not removed 9 | 10 | ## 1.0.2 11 | 12 | - Updated dependencies 13 | - Fixed spacing issue with escape codes 14 | 15 | ## 1.0.1 16 | 17 | - Added badges to repository 18 | 19 | ## 1.0.0 20 | 21 | - First feature-complete release 22 | - Updated preview images in repository 23 | 24 | ## 0.0.16 25 | 26 | - Added native theme support 27 | 28 | ## 0.0.15 29 | 30 | - Added support for synchronous scrolling in preview 31 | 32 | ## 0.0.14 33 | 34 | - Updated color palette to match default Microsoft terminal colors 35 | - Fixed dark / light color handling for editor 36 | - Fixed bug where leading zeroes in escape code were not stripped 37 | 38 | ## 0.0.13 39 | 40 | - Fixed bug in parsing of ESC[m code 41 | - Added foreground text colors for light mode 42 | 43 | ## 0.0.12 44 | 45 | - Added additional filter to remove escape codes in preview panel 46 | - Added section 'How to open the preview' to the readme document 47 | 48 | ## 0.0.11 49 | 50 | - Added progress to export commands 51 | - Introduced abbreviated class names for HTML export to reduce memory consumption 52 | 53 | ## 0.0.10 54 | 55 | - Implemented dark and light support for editor 56 | - Implemented dark, light and high-contrast support for preview 57 | 58 | ## 0.0.9 59 | 60 | - Debounced editor input to increase performance during typing and editing 61 | 62 | ## 0.0.8 63 | 64 | - Added escape sequence snippets 65 | 66 | ## 0.0.7 67 | 68 | - Added Text and HTML export 69 | - Fixed bug where context menu options for a file would do nothing 70 | 71 | ## 0.0.6 72 | 73 | - Fixed extension activation 74 | 75 | ## 0.0.5 76 | 77 | - Added preview panel with rendered VT100 codes 78 | - Updated configuration settings 79 | - Added font configuration support with fallback to editor font 80 | - Added custom CSS support for preview panel 81 | 82 | ## 0.0.4 83 | 84 | - Rewrote most of the plugins achitecture. \ 85 | Plugin now uses decorators instead of semantic types and modifiers. 86 | - Added background color support. 87 | - Added support for inverted colors and hidden attribute. 88 | 89 | ## 0.0.3 90 | 91 | - Introduced changelog. 92 | 93 | ## 0.0.2 94 | 95 | - Added icon for the Marketplace. 96 | 97 | ## 0.0.1 98 | 99 | - First test release of this plugin. 100 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2021 Tobias Faller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Visual Studio Marketplace](https://flat.badgen.net/vs-marketplace/i/Tobias-Faller.vt100-syntax-highlighting?icon=visualstudio)](https://marketplace.visualstudio.com/items?itemName=Tobias-Faller.vt100-syntax-highlighting) 2 | [![License](https://flat.badgen.net/github/license/TobiasFaller/vscode-vt100-syntax-highlight?icon=github)](LICENSE.md) 3 | [![Last Release](https://flat.badgen.net/github/release/TobiasFaller/vscode-vt100-syntax-highlight?icon=github)](https://github.com/TobiasFaller/vscode-vt100-syntax-highlight/releases) 4 | [![Last Commit](https://flat.badgen.net/github/last-commit/TobiasFaller/vscode-vt100-syntax-highlight?icon=github)](https://github.com/TobiasFaller/vscode-vt100-syntax-highlight) 5 | [![Open Issues](https://flat.badgen.net/github/open-issues/TobiasFaller/vscode-vt100-syntax-highlight?icon=github)](https://github.com/TobiasFaller/vscode-vt100-syntax-highlight/issues) 6 | [![Closed Issues](https://flat.badgen.net/github/closed-issues/TobiasFaller/vscode-vt100-syntax-highlight?icon=github)](https://github.com/TobiasFaller/vscode-vt100-syntax-highlight/issues?q=is%3Aissue+is%3Aclosed) 7 | 8 | # Terminal Color and Style Highlighting for VS Code 9 | 10 | This extension provides highlighting for **terminal color** escape sequences in the editor. 11 | The colors and styles can be customized via the configuration and light / dark theming is supported. 12 | The included preview feature can be used to view the source file like it would be shown in a terminal. 13 | With the export features the source file can be converted to text or to HTML. 14 | 15 | ## Features 16 | 17 | - **Editor**: Color and styling support (full document and inline) 18 | - **Editor**: Snippets for escape sequences 19 | - **Preview**: Terminal-like preview of the file 20 | - **Export**: Text export with removed escape sequences 21 | - **Export**: HTML export with rendered escape sequences 22 | 23 | Below is a screenshot how the highlighted sample file might look by applying this plugin. 24 | Select the file type "VT100 Terminal" on the lower right corner to enable highlighting of a file. 25 | 26 | ![Example Result](sample/ExampleResult.gif) 27 | 28 | ![Example Result with Dark Theme](sample/ExampleResultDarkTheme.gif) 29 | 30 | ![Example Result with Light Theme](sample/ExampleResultLightTheme.gif) 31 | 32 | ## How to open the preview 33 | 34 | Make sure that the extension is currently active for the selected file. 35 | This is done by selecting the file type `VT100 Terminal` on the bottom right of the editor. 36 | You can also use the `Change Language Mode` command, with has the shortcut `Ctrl + K` and then `M` when using the default configuration. 37 | v 38 | On the top right of the editor a preview icon with a magnifying glass should appear. 39 | Click on this icon to open the preview. 40 | You can also use the `Open Preview to Side` command, which has the shortcut `Ctrl + K` and then `V` when using the default configuration. 41 | 42 | ![Set Language Mode and Open the Preview](images/preview.png) 43 | 44 | ## Inline Highlighting 45 | 46 | This extension supports highlighting inline escape sequences. 47 | Enable / disable this feature for specific languages via the `decorate-include` and `decorate-exclude` options. Below is an example enabling this feature for python and shell files. Further below is an example of what this could look like in the editor. 48 | 49 | ```json 50 | { 51 | "vt100.decorate-includes": "python|shellscript" 52 | } 53 | ``` 54 | 55 | ![Escape Sequence Highlighting in Python File](sample/ExampleInline.png) 56 | 57 | ## Open Preview Directly (or by Default) 58 | 59 | By default the extension is configured to show files via an editor view. 60 | For txt and log files the preview view can be activated in-place by running the command `View: Toggle Editor Type`. 61 | To always show txt and log files via the preview add the following to your configuration. 62 | To get back to the default text editor run the command `View: Toggle Editor Type` again. 63 | 64 | ```json 65 | { 66 | "workbench.editorAssociations": { 67 | "*.txt": "vt100.preview", 68 | "*.log": "vt100.preview" 69 | } 70 | } 71 | ``` 72 | 73 | ![Editor Preview Opened by Default](sample/ExampleEditor.png) 74 | 75 | ## Extended Extension Settings 76 | 77 | Edit the global or workspace configuration and apply your custom colors. 78 | The configuration values shown below are the default values set by the plugin. 79 | All `foreground-color`, `background-color` and `attribute` settings support defining two styling variants. 80 | The variant defined in the `editor` sub-configuration is used for the editor view, while the `preview` sub-configuration is used for the HTML preview. 81 | If no sub-configurations are defined the style is used for both views. 82 | 83 | All `color` and `background-color` options can be set to `native` to use the theme color when `vt100.use-native-theme` is set to `false`. 84 | This can be used to selectively override single colors. 85 | 86 | The native color theme currently does not work for HTML export (VS Code API limitation) and will use the colors for the preview option instead. 87 | 88 | ```jsonc 89 | { 90 | // Associate the VT100 extension with *.log files if you want to. 91 | // "files.associations": { 92 | // "*.log": "vt100" 93 | // }, 94 | 95 | // Enable synchronous scrolling for preview when scrolling in the editor 96 | "vt100.synchronous-scrolling": true, 97 | 98 | // Enable native color theme support (overrides the user-defined colors) 99 | "vt100.use-native-theme": true, 100 | 101 | // Default foreground color and inverted default foreground color 102 | "vt100.foreground-color-default": { 103 | "dark": { "color": "#CCCCCC" }, 104 | "light": { "color": "#333333"} 105 | }, 106 | "vt100.foreground-color-inverted": { 107 | "dark": { "color": "#000000" }, 108 | "light": { "color": "#FFFFFF"} 109 | }, 110 | 111 | // Standard foreground colors 112 | "vt100.foreground-color-black": { 113 | "dark": { "color": "#666666" }, 114 | "light": { "color": "#000000"} 115 | }, 116 | "vt100.foreground-color-red": { 117 | "dark": { "color": "#CD3131" }, 118 | "light": { "color": "#CD3131"} 119 | }, 120 | "vt100.foreground-color-green": { 121 | "dark": { "color": "#0DBC79" }, 122 | "light": { "color": "#00BC00"} 123 | }, 124 | "vt100.foreground-color-yellow": { 125 | "dark": { "color": "#E5E510" }, 126 | "light": { "color": "#949800"} 127 | }, 128 | "vt100.foreground-color-blue": { 129 | "dark": { "color": "#2472C8" }, 130 | "light": { "color": "#0451A5"} 131 | }, 132 | "vt100.foreground-color-magenta": { 133 | "dark": { "color": "#BC3FBC" }, 134 | "light": { "color": "#BC05BC"} 135 | }, 136 | "vt100.foreground-color-cyan": { 137 | "dark": { "color": "#11A8CD" }, 138 | "light": { "color": "#0598BC"} 139 | }, 140 | "vt100.foreground-color-light-gray": { 141 | "dark": { "color": "#A5A5A5" }, 142 | "light": { "color": "#777777"} 143 | }, 144 | "vt100.foreground-color-dark-gray": { 145 | "dark": { "color": "#888888" }, 146 | "light": { "color": "#555555"} 147 | }, 148 | "vt100.foreground-color-light-red": { 149 | "dark": { "color": "#F14C4C" }, 150 | "light": { "color": "#CD3131"} 151 | }, 152 | "vt100.foreground-color-light-green": { 153 | "dark": { "color": "#23D18B" }, 154 | "light": { "color": "#14CE14"} 155 | }, 156 | "vt100.foreground-color-light-yellow": { 157 | "dark": { "color": "#F5F543" }, 158 | "light": { "color": "#B5BA00"} 159 | }, 160 | "vt100.foreground-color-light-blue": { 161 | "dark": { "color": "#3B8EEA" }, 162 | "light": { "color": "#0451A5"} 163 | }, 164 | "vt100.foreground-color-light-magenta": { 165 | "dark": { "color": "#D670D6" }, 166 | "light": { "color": "#BC05BC"} 167 | }, 168 | "vt100.foreground-color-light-cyan": { 169 | "dark": { "color": "#29B8DB" }, 170 | "light": { "color": "#0598BC"} 171 | }, 172 | "vt100.foreground-color-white": { 173 | "dark": { "color": "#E5E5E5" }, 174 | "light": { "color": "#A5A5A5"} 175 | }, 176 | 177 | // Default background color and inverted default background color 178 | "vt100.background-color-default": { }, 179 | "vt100.background-color-inverted": { 180 | "dark": { "background-color": "#CCCCCC" }, 181 | "light": { "background-color": "#333333"} 182 | }, 183 | 184 | // Standard background colors 185 | "vt100.background-color-black": { 186 | "dark": { "background-color": "#000000" }, 187 | "light": { "background-color": "#000000"} 188 | }, 189 | "vt100.background-color-red": { 190 | "dark": { "background-color": "#CD3131" }, 191 | "light": { "background-color": "#CD3131"} 192 | }, 193 | "vt100.background-color-green": { 194 | "dark": { "background-color": "#0DBC79" }, 195 | "light": { "background-color": "#00BC00"} 196 | }, 197 | "vt100.background-color-yellow": { 198 | "dark": { "background-color": "#E5E510" }, 199 | "light": { "background-color": "#949800"} 200 | }, 201 | "vt100.background-color-blue": { 202 | "dark": { "background-color": "#2472C8" }, 203 | "light": { "background-color": "#0451A5"} 204 | }, 205 | "vt100.background-color-magenta": { 206 | "dark": { "background-color": "#BC3FBC" }, 207 | "light": { "background-color": "#BC05BC"} 208 | }, 209 | "vt100.background-color-cyan": { 210 | "dark": { "background-color": "#11A8CD" }, 211 | "light": { "background-color": "#0598BC"} 212 | }, 213 | "vt100.background-color-light-gray": { 214 | "dark": { "background-color": "#A5A5A5" }, 215 | "light": { "background-color": "#777777"} 216 | }, 217 | "vt100.background-color-dark-gray": { 218 | "dark": { "background-color": "#666666" }, 219 | "light": { "background-color": "#555555"} 220 | }, 221 | "vt100.background-color-light-red": { 222 | "dark": { "background-color": "#F14C4C" }, 223 | "light": { "background-color": "#CD3131"} 224 | }, 225 | "vt100.background-color-light-green": { 226 | "dark": { "background-color": "#23D18B" }, 227 | "light": { "background-color": "#14CE14"} 228 | }, 229 | "vt100.background-color-light-yellow": { 230 | "dark": { "background-color": "#F5F543" }, 231 | "light": { "background-color": "#B5BA00"} 232 | }, 233 | "vt100.background-color-light-blue": { 234 | "dark": { "background-color": "#3B8EEA" }, 235 | "light": { "background-color": "#0451A5"} 236 | }, 237 | "vt100.background-color-light-magenta": { 238 | "dark": { "background-color": "#D670D6" }, 239 | "light": { "background-color": "#BC05BC"} 240 | }, 241 | "vt100.background-color-light-cyan": { 242 | "dark": { "background-color": "#29B8DB" }, 243 | "light": { "background-color": "#0598BC"} 244 | }, 245 | "vt100.background-color-white": { 246 | "dark": { "background-color": "#E5E5E5" }, 247 | "light": { "background-color": "#A5A5A5" } 248 | }, 249 | 250 | // Separate style definitions for the editor and the preview panel are possible 251 | // "vt100.background-color-white": { 252 | // "editor": { 253 | // "dark": { "background-color": "#E5E5E5" }, 254 | // "light": { "background-color": "#A5A5A5" } 255 | // }, 256 | // "preview": { 257 | // "background-color": "#FFFFFF" 258 | // } 259 | // }, 260 | 261 | // VT100 attributes 262 | "vt100.attribute-bold": { "font-weight": "bold" }, 263 | "vt100.attribute-dim": { "opacity": "0.7", "font-weight": "lighter" }, 264 | "vt100.attribute-underlined": { "text-decoration": "underline solid" }, 265 | "vt100.attribute-blink": { 266 | // The configuration for the editor and the preview is different, 267 | // because the editor does not support all CSS options. 268 | "editor": { 269 | "border": "1px dotted #FFFFFF77" 270 | }, 271 | "preview": { 272 | // The animation is defined in the default custom CSS setting 273 | // for the preview. 274 | "animation": "blink-animation 1s step-start 0s infinite" 275 | } 276 | }, 277 | "vt100.attribute-inverted": { }, 278 | "vt100.attribute-hidden": { "opacity": "0.3" }, 279 | 280 | // VT100 escape sequences 281 | // Do not apply additional styling to escape sequences (default). 282 | "vt100.escape-sequence": { }, 283 | 284 | // Style for all text which is not an escape sequence. 285 | "vt100.text": { }, 286 | 287 | // Make escape sequences white and fully visible in the editor. 288 | // Escape sequences are never rendered in the preview. 289 | // "vt100.escape-sequence": { "color": "#FFFFFF !important", "opacity": "1.0 !important" }, 290 | 291 | // Make escape sequences invisible in the editor. 292 | // "vt100.escape-sequence": { "opacity": "0.0 !important" }, 293 | 294 | // Use the default editor font settings. 295 | "vt100.font-family": null, 296 | "vt100.font-size": null, 297 | "vt100.font-weight": null, 298 | 299 | // Use a different font for the preview. 300 | // Be sure to surround names with spaces in quotes. 301 | // "vt100.font-family": "'Lucida Console', monospace", 302 | 303 | // Additional custom CSS which is required for the preview. 304 | // The keyframes are required when animating the blinking style. 305 | // Use the classes .vscode-light, .vscode-dark and .vscode-high-contrast 306 | // to create theme dependent styles. 307 | "vt100.custom-css": { 308 | "*": { 309 | "padding": "0px", 310 | "margin": "0px" 311 | }, 312 | ".bg": { 313 | "display": "inline-block", 314 | "padding": "0.1em" 315 | }, 316 | "@keyframes blink-animation": { 317 | "50%": { 318 | "opacity": "0.0" 319 | } 320 | } 321 | // Example for dark theme only definition, which uses white text 322 | // when viewing in dark mode. 323 | // The same effect can be created by using the "dark" and "light" 324 | // settings of the other configuration options. 325 | //".vscode-dark .bg": { 326 | // "color": "white" 327 | //} 328 | } 329 | } 330 | ``` 331 | 332 | The HTML preview allows all [CSS properties](https://www.w3schools.com/cssref/default.asp) which can be set for a `span` element. 333 | The [common properties](https://code.visualstudio.com/api/references/vscode-api#DecorationRenderOptions) listed below are supported by the editor view. 334 | The HTML property names are automatically converted to the VS Code internal format which allows one value to be used for both configurations variants. 335 | Even though the color configuration settings are called `foreground-color-*` and `background-color-*`, all properties below can be specified in the editor, if desired. 336 | 337 | - **text-decoration** 338 | - **outline-width** 339 | - **outline-style** 340 | - **outline-color** 341 | - **outline** 342 | - **opacity** 343 | - **letter-spacing** 344 | - **font-weight** 345 | - **font-style** 346 | - **cursor** 347 | - **color** 348 | - **border-width** 349 | - **border-style** 350 | - **border-spacing** 351 | - **border-radius** 352 | - **border-color** 353 | - **border** 354 | - **before** 355 | - **background-color** 356 | - **after** 357 | 358 | The HTML preview uses the HTML class names in abbreviated form to reduce the memory consumption of the generated HTML code. 359 | Below is the list with all class names and the abbreviations which are used in the HTML code. 360 | 361 | - **foreground**: fg 362 | - **foreground-color-default**: fg-de 363 | - **foreground-color-inverted**: fg-in 364 | - **foreground-color-black**: fg-bl 365 | - **foreground-color-red**: fg-re 366 | - **foreground-color-green**: fg-gr 367 | - **foreground-color-yellow**: fg-yl 368 | - **foreground-color-blue**: fg-blu 369 | - **foreground-color-magenta**: fg-mg 370 | - **foreground-color-cyan**: fg-cy 371 | - **foreground-color-light-gray**: fg-lg 372 | - **foreground-color-dark-gray**: fg-dg 373 | - **foreground-color-light-red**: fg-lr 374 | - **foreground-color-light-green**: fg-lgr 375 | - **foreground-color-light-yellow**: fg-ly 376 | - **foreground-color-light-blue**: fg-lb 377 | - **foreground-color-light-magenta**: fg-lm 378 | - **foreground-color-light-cyan**: fg-lc 379 | - **foreground-color-white**: fg-wh 380 | - **background**: bg 381 | - **background-color-default**: bg-de 382 | - **background-color-inverted**: bg-in 383 | - **background-color-black**: bg-bl 384 | - **background-color-red**: bg-re 385 | - **background-color-green**: bg-gr 386 | - **background-color-yellow**: bg-yl 387 | - **background-color-blue**: bg-blu 388 | - **background-color-magenta**: bg-mg 389 | - **background-color-cyan**: bg-cy 390 | - **background-color-light-gray**: bg-lg 391 | - **background-color-dark-gray**: bg-dg 392 | - **background-color-light-red**: bg-lr 393 | - **background-color-light-green**: bg-lgr 394 | - **background-color-light-yellow**: bg-ly 395 | - **background-color-light-blue**: bg-lb 396 | - **background-color-light-magenta**: bg-lm 397 | - **background-color-light-cyan**: bg-lc 398 | - **background-color-white**: bg-wh 399 | - **attribute-bold**: at-bo 400 | - **attribute-dim**: at-di 401 | - **attribute-underlined**: at-ul 402 | - **attribute-blink**: at-bl 403 | - **attribute-inverted**: at-in 404 | - **attribute-hidden**: at-hi 405 | - **text**: te 406 | - **escape-sequence**: es 407 | 408 | ## Test the Extension with an Example 409 | 410 | Use the python program below to generate a sample file which contains VT100 escape sequences. 411 | 412 | ```python 413 | #!/bin/env python3 414 | 415 | FGCOLORS = [ 416 | 39, 417 | 30, 31, 32, 33, 34, 35, 36, 37, 418 | 90, 91, 92, 93, 94, 95, 96, 97 419 | ] 420 | BGCOLORS = [ 421 | 49, 422 | 40, 41, 42, 43, 44, 45, 46, 47, 423 | 100, 101, 102, 103, 104, 105, 106, 107 424 | ] 425 | ATTRIBUTES = [ 0, 1, 2, 4, 5, 7, 8 ] 426 | 427 | for bgColor in BGCOLORS: 428 | for fgColor in FGCOLORS: 429 | line = "" 430 | for attribute in ATTRIBUTES: 431 | style = f"{attribute};{fgColor};{bgColor}" 432 | line += f"\x1b[0;{style}m {style}" 433 | line += "\x1b[0m" 434 | print(line) 435 | print() 436 | ``` 437 | 438 | Run the program with `python3 generate_sample.py > sample.vt100`. 439 | The output looks similar to the image shown below on a terminal. 440 | 441 | ![Example Result on a Terminal](sample/ExampleSource.png) 442 | 443 | ## Requirements for Build 444 | 445 | Typescript for plugin development. 446 | This extension does not need any other dependencies. 447 | 448 | ```bash 449 | #!/bin/bash 450 | 451 | # Install dependencies 452 | npm install 453 | 454 | # Build plugin 455 | vsce package 456 | ``` 457 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/images/icon.png -------------------------------------------------------------------------------- /images/icon.rli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/images/icon.rli -------------------------------------------------------------------------------- /images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/images/preview.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vt100-syntax-highlighting", 3 | "displayName": "Terminal Color and Style Highlighting for the Editor", 4 | "description": "VT100 Color and Style Highlighting in the Text Editor", 5 | "keywords": [ 6 | "colors", 7 | "escape code", 8 | "terminal", 9 | "ansi", 10 | "console" 11 | ], 12 | "version": "1.0.2", 13 | "author": { 14 | "name": "Tobias Faller" 15 | }, 16 | "publisher": "Tobias-Faller", 17 | "license": "MIT", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/TobiasFaller/vscode-vt100-syntax-highlight" 21 | }, 22 | "engines": { 23 | "vscode": "^1.74.0" 24 | }, 25 | "categories": [ 26 | "Programming Languages", 27 | "Snippets", 28 | "Other" 29 | ], 30 | "icon": "images/icon.png", 31 | "galleryBanner": { 32 | "color": "#0055CC", 33 | "theme": "dark" 34 | }, 35 | "homepage": "https://github.com/TobiasFaller/vscode-vt100-syntax-highlight", 36 | "activationEvents": [ "*" ], 37 | "main": "./out/extension.js", 38 | "contributes": { 39 | "languages": [ 40 | { 41 | "id": "vt100", 42 | "aliases": [ 43 | "VT100 Terminal" 44 | ], 45 | "extensions": [ 46 | ".vt100", 47 | ".vt100.txt", 48 | ".vt100.log" 49 | ] 50 | } 51 | ], 52 | "configuration": [ 53 | { 54 | "title": "VT100 Syntax Highlighting Configuration", 55 | "properties": { 56 | "vt100.foreground-color-default": { 57 | "type": [ 58 | "object", 59 | "null" 60 | ], 61 | "title": "VT100 Text Style (Default color)", 62 | "default": { 63 | "dark": { 64 | "color": "#CCCCCC" 65 | }, 66 | "light": { 67 | "color": "#333333" 68 | } 69 | }, 70 | "description": "The VT100 default foreground color style" 71 | }, 72 | "vt100.foreground-color-inverted": { 73 | "type": [ 74 | "object", 75 | "null" 76 | ], 77 | "title": "VT100 Text Style (Inverted default color)", 78 | "default": { 79 | "dark": { 80 | "color": "#000000" 81 | }, 82 | "light": { 83 | "color": "#FFFFFF" 84 | } 85 | }, 86 | "description": "The VT100 default (inverted) foreground color style" 87 | }, 88 | "vt100.foreground-color-black": { 89 | "type": [ 90 | "object", 91 | "null" 92 | ], 93 | "title": "VT100 Text Style (Black color)", 94 | "default": { 95 | "dark": { 96 | "color": "#666666" 97 | }, 98 | "light": { 99 | "color": "#000000" 100 | } 101 | }, 102 | "description": "The VT100 black foreground color style" 103 | }, 104 | "vt100.foreground-color-red": { 105 | "type": [ 106 | "object", 107 | "null" 108 | ], 109 | "title": "VT100 Text Style (Red color)", 110 | "default": { 111 | "dark": { 112 | "color": "#CD3131" 113 | }, 114 | "light": { 115 | "color": "#CD3131" 116 | } 117 | }, 118 | "description": "The VT100 red foreground color style" 119 | }, 120 | "vt100.foreground-color-green": { 121 | "type": [ 122 | "object", 123 | "null" 124 | ], 125 | "title": "VT100 Text Style (Green color)", 126 | "default": { 127 | "dark": { 128 | "color": "#0DBC79" 129 | }, 130 | "light": { 131 | "color": "#00BC00" 132 | } 133 | }, 134 | "description": "The VT100 green foreground color style" 135 | }, 136 | "vt100.foreground-color-yellow": { 137 | "type": [ 138 | "object", 139 | "null" 140 | ], 141 | "title": "VT100 Text Style (Yellow color)", 142 | "default": { 143 | "dark": { 144 | "color": "#E5E510" 145 | }, 146 | "light": { 147 | "color": "#949800" 148 | } 149 | }, 150 | "description": "The VT100 yellow foreground color style" 151 | }, 152 | "vt100.foreground-color-blue": { 153 | "type": [ 154 | "object", 155 | "null" 156 | ], 157 | "title": "VT100 Text Style (Blue color)", 158 | "default": { 159 | "dark": { 160 | "color": "#2472C8" 161 | }, 162 | "light": { 163 | "color": "#0451A5" 164 | } 165 | }, 166 | "description": "The VT100 blue foreground color style" 167 | }, 168 | "vt100.foreground-color-magenta": { 169 | "type": [ 170 | "object", 171 | "null" 172 | ], 173 | "title": "VT100 Text Style (Magenta color)", 174 | "default": { 175 | "dark": { 176 | "color": "#BC3FBC" 177 | }, 178 | "light": { 179 | "color": "#BC05BC" 180 | } 181 | }, 182 | "description": "The VT100 magenta foreground color style" 183 | }, 184 | "vt100.foreground-color-cyan": { 185 | "type": [ 186 | "object", 187 | "null" 188 | ], 189 | "title": "VT100 Text Style (Cyan color)", 190 | "default": { 191 | "dark": { 192 | "color": "#11A8CD" 193 | }, 194 | "light": { 195 | "color": "#0598BC" 196 | } 197 | }, 198 | "description": "The VT100 cyan foreground color style" 199 | }, 200 | "vt100.foreground-color-light-gray": { 201 | "type": [ 202 | "object", 203 | "null" 204 | ], 205 | "title": "VT100 Text Style (Light Gray color)", 206 | "default": { 207 | "dark": { 208 | "color": "#A5A5A5" 209 | }, 210 | "light": { 211 | "color": "#777777" 212 | } 213 | }, 214 | "description": "The VT100 light gray foreground color style" 215 | }, 216 | "vt100.foreground-color-dark-gray": { 217 | "type": [ 218 | "object", 219 | "null" 220 | ], 221 | "title": "VT100 Text Style (Dark Gray color)", 222 | "default": { 223 | "dark": { 224 | "color": "#888888" 225 | }, 226 | "light": { 227 | "color": "#555555" 228 | } 229 | }, 230 | "description": "The VT100 dark gray foreground color style" 231 | }, 232 | "vt100.foreground-color-light-red": { 233 | "type": [ 234 | "object", 235 | "null" 236 | ], 237 | "title": "VT100 Text Style (Light Red color)", 238 | "default": { 239 | "dark": { 240 | "color": "#F14C4C" 241 | }, 242 | "light": { 243 | "color": "#CD3131" 244 | } 245 | }, 246 | "description": "The VT100 light red foreground color style" 247 | }, 248 | "vt100.foreground-color-light-green": { 249 | "type": [ 250 | "object", 251 | "null" 252 | ], 253 | "title": "VT100 Text Style (Light Green color)", 254 | "default": { 255 | "dark": { 256 | "color": "#23D18B" 257 | }, 258 | "light": { 259 | "color": "#14CE14" 260 | } 261 | }, 262 | "description": "The VT100 light green foreground color style" 263 | }, 264 | "vt100.foreground-color-light-yellow": { 265 | "type": [ 266 | "object", 267 | "null" 268 | ], 269 | "title": "VT100 Text Style (Light Yellow color)", 270 | "default": { 271 | "dark": { 272 | "color": "#F5F543" 273 | }, 274 | "light": { 275 | "color": "#B5BA00" 276 | } 277 | }, 278 | "description": "The VT100 light yellow foreground color style" 279 | }, 280 | "vt100.foreground-color-light-blue": { 281 | "type": [ 282 | "object", 283 | "null" 284 | ], 285 | "title": "VT100 Text Style (Light Blue color)", 286 | "default": { 287 | "dark": { 288 | "color": "#3B8EEA" 289 | }, 290 | "light": { 291 | "color": "#0451A5" 292 | } 293 | }, 294 | "description": "The VT100 light blue foreground color style" 295 | }, 296 | "vt100.foreground-color-light-magenta": { 297 | "type": [ 298 | "object", 299 | "null" 300 | ], 301 | "title": "VT100 Text Style (Light Magenta color)", 302 | "default": { 303 | "dark": { 304 | "color": "#D670D6" 305 | }, 306 | "light": { 307 | "color": "#BC05BC" 308 | } 309 | }, 310 | "description": "The VT100 light magenta foreground color style" 311 | }, 312 | "vt100.foreground-color-light-cyan": { 313 | "type": [ 314 | "object", 315 | "null" 316 | ], 317 | "title": "VT100 Text Style (Light Cyan color)", 318 | "default": { 319 | "dark": { 320 | "color": "#29B8DB" 321 | }, 322 | "light": { 323 | "color": "#0598BC" 324 | } 325 | }, 326 | "description": "The VT100 light cyan foreground color style" 327 | }, 328 | "vt100.foreground-color-white": { 329 | "type": [ 330 | "object", 331 | "null" 332 | ], 333 | "title": "VT100 Text Style (White color)", 334 | "default": { 335 | "dark": { 336 | "color": "#E5E5E5" 337 | }, 338 | "light": { 339 | "color": "#A5A5A5" 340 | } 341 | }, 342 | "description": "The VT100 white foreground color style" 343 | }, 344 | "vt100.background-color-default": { 345 | "type": [ 346 | "object", 347 | "null" 348 | ], 349 | "title": "VT100 Background Style (Default color)", 350 | "default": {}, 351 | "description": "The VT100 default background color style" 352 | }, 353 | "vt100.background-color-inverted": { 354 | "type": [ 355 | "object", 356 | "null" 357 | ], 358 | "title": "VT100 Background Style (Default inverted color)", 359 | "default": { 360 | "dark": { 361 | "background-color": "#CCCCCC" 362 | }, 363 | "light": { 364 | "background-color": "#333333" 365 | } 366 | }, 367 | "description": "The VT100 default (inverted) background color style" 368 | }, 369 | "vt100.background-color-black": { 370 | "type": [ 371 | "object", 372 | "null" 373 | ], 374 | "title": "VT100 Background Style (Black color)", 375 | "default": { 376 | "dark": { 377 | "background-color": "#000000" 378 | }, 379 | "light": { 380 | "background-color": "#000000" 381 | } 382 | }, 383 | "description": "The VT100 black background color style" 384 | }, 385 | "vt100.background-color-red": { 386 | "type": [ 387 | "object", 388 | "null" 389 | ], 390 | "title": "VT100 Background Style (Red color)", 391 | "default": { 392 | "dark": { 393 | "background-color": "#CD3131" 394 | }, 395 | "light": { 396 | "background-color": "#CD3131" 397 | } 398 | }, 399 | "description": "The VT100 red background color style" 400 | }, 401 | "vt100.background-color-green": { 402 | "type": [ 403 | "object", 404 | "null" 405 | ], 406 | "title": "VT100 Background Style (Green color)", 407 | "default": { 408 | "dark": { 409 | "background-color": "#0DBC79" 410 | }, 411 | "light": { 412 | "background-color": "#00BC00" 413 | } 414 | }, 415 | "description": "The VT100 green background color style" 416 | }, 417 | "vt100.background-color-yellow": { 418 | "type": [ 419 | "object", 420 | "null" 421 | ], 422 | "title": "VT100 Background Style (Yellow color)", 423 | "default": { 424 | "dark": { 425 | "background-color": "#E5E510" 426 | }, 427 | "light": { 428 | "background-color": "#949800" 429 | } 430 | }, 431 | "description": "The VT100 yellow background color style" 432 | }, 433 | "vt100.background-color-blue": { 434 | "type": [ 435 | "object", 436 | "null" 437 | ], 438 | "title": "VT100 Background Style (Blue color)", 439 | "default": { 440 | "dark": { 441 | "background-color": "#2472C8" 442 | }, 443 | "light": { 444 | "background-color": "#0451A5" 445 | } 446 | }, 447 | "description": "The VT100 blue background color style" 448 | }, 449 | "vt100.background-color-magenta": { 450 | "type": [ 451 | "object", 452 | "null" 453 | ], 454 | "title": "VT100 Background Style (Magenta color)", 455 | "default": { 456 | "dark": { 457 | "background-color": "#BC3FBC" 458 | }, 459 | "light": { 460 | "background-color": "#BC05BC" 461 | } 462 | }, 463 | "description": "The VT100 magenta background color style" 464 | }, 465 | "vt100.background-color-cyan": { 466 | "type": [ 467 | "object", 468 | "null" 469 | ], 470 | "title": "VT100 Background Style (Cyan color)", 471 | "default": { 472 | "dark": { 473 | "background-color": "#11A8CD" 474 | }, 475 | "light": { 476 | "background-color": "#0598BC" 477 | } 478 | }, 479 | "description": "The VT100 cyan background color style" 480 | }, 481 | "vt100.background-color-light-gray": { 482 | "type": [ 483 | "object", 484 | "null" 485 | ], 486 | "title": "VT100 Background Style (Light Gray color)", 487 | "default": { 488 | "dark": { 489 | "background-color": "#A5A5A5" 490 | }, 491 | "light": { 492 | "background-color": "#777777" 493 | } 494 | }, 495 | "description": "The VT100 light gray background color style" 496 | }, 497 | "vt100.background-color-dark-gray": { 498 | "type": [ 499 | "object", 500 | "null" 501 | ], 502 | "title": "VT100 Background Style (Dark Gray color)", 503 | "default": { 504 | "dark": { 505 | "background-color": "#666666" 506 | }, 507 | "light": { 508 | "background-color": "#555555" 509 | } 510 | }, 511 | "description": "The VT100 dark gray background color style" 512 | }, 513 | "vt100.background-color-light-red": { 514 | "type": [ 515 | "object", 516 | "null" 517 | ], 518 | "title": "VT100 Background Style (Light Red color)", 519 | "default": { 520 | "dark": { 521 | "background-color": "#F14C4C" 522 | }, 523 | "light": { 524 | "background-color": "#CD3131" 525 | } 526 | }, 527 | "description": "The VT100 light red background color style" 528 | }, 529 | "vt100.background-color-light-green": { 530 | "type": [ 531 | "object", 532 | "null" 533 | ], 534 | "title": "VT100 Background Style (Light Green color)", 535 | "default": { 536 | "dark": { 537 | "background-color": "#23D18B" 538 | }, 539 | "light": { 540 | "background-color": "#14CE14" 541 | } 542 | }, 543 | "description": "The VT100 light green background color style" 544 | }, 545 | "vt100.background-color-light-yellow": { 546 | "type": [ 547 | "object", 548 | "null" 549 | ], 550 | "title": "VT100 Background Style (Light Yellow color)", 551 | "default": { 552 | "dark": { 553 | "background-color": "#F5F543" 554 | }, 555 | "light": { 556 | "background-color": "#B5BA00" 557 | } 558 | }, 559 | "description": "The VT100 light yellow background color style" 560 | }, 561 | "vt100.background-color-light-blue": { 562 | "type": [ 563 | "object", 564 | "null" 565 | ], 566 | "title": "VT100 Background Style (Light Blue color)", 567 | "default": { 568 | "dark": { 569 | "background-color": "#3B8EEA" 570 | }, 571 | "light": { 572 | "background-color": "#0451A5" 573 | } 574 | }, 575 | "description": "The VT100 light blue background color style" 576 | }, 577 | "vt100.background-color-light-magenta": { 578 | "type": [ 579 | "object", 580 | "null" 581 | ], 582 | "title": "VT100 Background Style (Light Magenta color)", 583 | "default": { 584 | "dark": { 585 | "background-color": "#D670D6" 586 | }, 587 | "light": { 588 | "background-color": "#BC05BC" 589 | } 590 | }, 591 | "description": "The VT100 light magenta background color style" 592 | }, 593 | "vt100.background-color-light-cyan": { 594 | "type": [ 595 | "object", 596 | "null" 597 | ], 598 | "title": "VT100 Background Style (Light Cyan color)", 599 | "default": { 600 | "dark": { 601 | "background-color": "#29B8DB" 602 | }, 603 | "light": { 604 | "background-color": "#0598BC" 605 | } 606 | }, 607 | "description": "The VT100 light cyan background color style" 608 | }, 609 | "vt100.background-color-white": { 610 | "type": [ 611 | "object", 612 | "null" 613 | ], 614 | "title": "VT100 Background Style (White color)", 615 | "default": { 616 | "dark": { 617 | "background-color": "#E5E5E5" 618 | }, 619 | "light": { 620 | "background-color": "#A5A5A5" 621 | } 622 | }, 623 | "description": "The VT100 white background color style" 624 | }, 625 | "vt100.attribute-bold": { 626 | "type": [ 627 | "object", 628 | "null" 629 | ], 630 | "title": "VT100 Bold Style", 631 | "default": { 632 | "font-weight": "bold" 633 | }, 634 | "description": "The VT100 bold style" 635 | }, 636 | "vt100.attribute-dim": { 637 | "type": [ 638 | "object", 639 | "null" 640 | ], 641 | "title": "VT100 Dimmed Style", 642 | "default": { 643 | "opacity": "0.7", 644 | "font-weight": "lighter" 645 | }, 646 | "description": "The VT100 dim style" 647 | }, 648 | "vt100.attribute-underlined": { 649 | "type": [ 650 | "object", 651 | "null" 652 | ], 653 | "title": "VT100 Underlined Style", 654 | "default": { 655 | "text-decoration": "underline solid" 656 | }, 657 | "description": "The VT100 underlined style" 658 | }, 659 | "vt100.attribute-blink": { 660 | "type": [ 661 | "object", 662 | "null" 663 | ], 664 | "title": "VT100 Blinking Style", 665 | "default": { 666 | "editor": { 667 | "border": "1px dotted #FFFFFF77" 668 | }, 669 | "preview": { 670 | "animation": "blink-animation 1s step-start 0s infinite" 671 | } 672 | }, 673 | "description": "The VT100 blinking style" 674 | }, 675 | "vt100.attribute-inverted": { 676 | "type": [ 677 | "object", 678 | "null" 679 | ], 680 | "title": "VT100 Inverted Style", 681 | "default": {}, 682 | "description": "The VT100 inverted style" 683 | }, 684 | "vt100.attribute-hidden": { 685 | "type": [ 686 | "object", 687 | "null" 688 | ], 689 | "title": "VT100 Hidden Style", 690 | "default": { 691 | "opacity": "0.3" 692 | }, 693 | "description": "The VT100 hidden style" 694 | }, 695 | "vt100.escape-sequence": { 696 | "type": [ 697 | "object", 698 | "null" 699 | ], 700 | "title": "VT100 Escape Sequence Style (Only Editor)", 701 | "default": {}, 702 | "description": "The VT100 escape sequence style used in the editor" 703 | }, 704 | "vt100.text": { 705 | "type": [ 706 | "object", 707 | "null" 708 | ], 709 | "title": "VT100 Text Style (No Escape Sequences)", 710 | "default": {}, 711 | "description": "The VT100 text style" 712 | }, 713 | "vt100.custom-css": { 714 | "type": [ 715 | "object", 716 | "null" 717 | ], 718 | "title": "VT100 Custom CSS (Only Preview)", 719 | "default": { 720 | "*": { 721 | "padding": "0px", 722 | "margin": "0px" 723 | }, 724 | ".bg": { 725 | "display": "inline-block", 726 | "padding": "0.1em 0px 0.1em 0px" 727 | }, 728 | "@keyframes blink-animation": { 729 | "50%": { 730 | "opacity": "0.0" 731 | } 732 | } 733 | }, 734 | "description": "Additional CSS used for the VT100 preview" 735 | }, 736 | "vt100.font-family": { 737 | "type": [ 738 | "string", 739 | "null" 740 | ], 741 | "title": "VT100 Font Family (Only Preview)", 742 | "default": null, 743 | "description": "The font used for the VT100 preview (default: text editor font family)" 744 | }, 745 | "vt100.font-size": { 746 | "type": [ 747 | "integer", 748 | "null" 749 | ], 750 | "title": "VT100 Font Size (Only Preview)", 751 | "default": null, 752 | "description": "The font size in pixels used for the VT100 preview (default: text editor font size)" 753 | }, 754 | "vt100.font-weight": { 755 | "type": [ 756 | "string", 757 | "null" 758 | ], 759 | "title": "VT100 Font Weight (Only Preview)", 760 | "default": null, 761 | "description": "The font weight used for the VT100 preview (default: text editor font weight)" 762 | }, 763 | "vt100.synchronous-scrolling": { 764 | "type": "boolean", 765 | "title": "VT100 Synchronous Scrolling for Preview", 766 | "default": true, 767 | "description": "Enables synchronous scrolling of the preview when changing the view window of the editor" 768 | }, 769 | "vt100.use-native-theme": { 770 | "type": "boolean", 771 | "title": "Synchronize VT100 Theme with Terminal Theme", 772 | "default": true, 773 | "description": "Uses the active color theme as for the VT100 extension" 774 | }, 775 | "vt100.decorate-includes": { 776 | "type": "string", 777 | "title": "VT100 Highlighting Language Includes", 778 | "default": "vt100", 779 | "description": "Enables highlighting of VT100 escape sequences for the files matched by the regex, use null for all" 780 | }, 781 | "vt100.decorate-excludes": { 782 | "type": "string", 783 | "title": "VT100 Highlighting Language Excludes", 784 | "default": null, 785 | "description": "Disables highlighting of VT100 escape sequences for the files matched by the regex, use null to disable none" 786 | } 787 | } 788 | } 789 | ], 790 | "commands": [ 791 | { 792 | "command": "vt100.showPreview", 793 | "title": "Show Preview", 794 | "category": "VT100", 795 | "icon": "$(open-preview)" 796 | }, 797 | { 798 | "command": "vt100.showPreviewToSide", 799 | "title": "Show Preview on Side", 800 | "category": "VT100", 801 | "icon": "$(open-preview)" 802 | }, 803 | { 804 | "command": "vt100.toggleSynchronousScrolling", 805 | "title": "Toggle Synchronous Scrolling", 806 | "category": "VT100", 807 | "icon": "$(link)" 808 | }, 809 | { 810 | "command": "vt100.exportText", 811 | "title": "Export to Text", 812 | "category": "VT100", 813 | "icon": "$(file)" 814 | }, 815 | { 816 | "command": "vt100.exportHtml", 817 | "title": "Export to Html", 818 | "category": "VT100", 819 | "icon": "$(file-code)" 820 | }, 821 | { 822 | "command": "vt100.cancelExport", 823 | "title": "Cancel the currently running export", 824 | "category": "VT100", 825 | "icon": "$(file-code)" 826 | } 827 | ], 828 | "menus": { 829 | "editor/title": [ 830 | { 831 | "command": "vt100.showPreviewToSide", 832 | "when": "resourceLangId == vt100", 833 | "alt": "vt100.showPreview", 834 | "group": "navigation" 835 | }, 836 | { 837 | "command": "vt100.toggleSynchronousScrolling", 838 | "when": "resourceLangId == vt100", 839 | "group": "vt100" 840 | }, 841 | { 842 | "command": "vt100.exportText", 843 | "when": "resourceLangId == vt100", 844 | "group": "vt100" 845 | }, 846 | { 847 | "command": "vt100.exportHtml", 848 | "when": "resourceLangId == vt100", 849 | "group": "vt100" 850 | } 851 | ], 852 | "explorer/context": [ 853 | { 854 | "command": "vt100.showPreview", 855 | "when": "resourceLangId == vt100", 856 | "group": "navigation" 857 | }, 858 | { 859 | "command": "vt100.exportText", 860 | "when": "resourceLangId == vt100", 861 | "group": "vt100" 862 | }, 863 | { 864 | "command": "vt100.exportHtml", 865 | "when": "resourceLangId == vt100", 866 | "group": "vt100" 867 | } 868 | ], 869 | "editor/title/context": [ 870 | { 871 | "command": "vt100.showPreview", 872 | "when": "resourceLangId == vt100", 873 | "group": "navigation" 874 | }, 875 | { 876 | "command": "vt100.toggleSynchronousScrolling", 877 | "when": "resourceLangId == vt100", 878 | "group": "vt100" 879 | }, 880 | { 881 | "command": "vt100.exportText", 882 | "when": "resourceLangId == vt100", 883 | "group": "vt100" 884 | }, 885 | { 886 | "command": "vt100.exportHtml", 887 | "when": "resourceLangId == vt100", 888 | "group": "vt100" 889 | } 890 | ], 891 | "commandPalette": [ 892 | { 893 | "command": "vt100.showPreview", 894 | "when": "editorLangId == vt100", 895 | "group": "navigation" 896 | }, 897 | { 898 | "command": "vt100.showPreviewToSide", 899 | "when": "editorLangId == vt100", 900 | "group": "navigation" 901 | }, 902 | { 903 | "command": "vt100.toggleSynchronousScrolling", 904 | "when": "editorLangId == vt100", 905 | "group": "vt100" 906 | }, 907 | { 908 | "command": "vt100.exportText", 909 | "when": "editorLangId == vt100", 910 | "group": "vt100" 911 | }, 912 | { 913 | "command": "vt100.exportHtml", 914 | "when": "editorLangId == vt100", 915 | "group": "vt100" 916 | } 917 | ] 918 | }, 919 | "keybindings": [ 920 | { 921 | "command": "vt100.showPreview", 922 | "key": "shift+ctrl+v", 923 | "mac": "shift+cmd+v", 924 | "when": "editorLangId == vt100" 925 | }, 926 | { 927 | "command": "vt100.showPreviewToSide", 928 | "key": "ctrl+k v", 929 | "mac": "cmd+k v", 930 | "when": "editorLangId == vt100" 931 | } 932 | ], 933 | "snippets": [ 934 | { 935 | "language": "vt100", 936 | "path": "./snippets/vt100.json" 937 | } 938 | ], 939 | "customEditors": [ 940 | { 941 | "viewType": "vt100.preview", 942 | "displayName": "VT100 Preview", 943 | "selector": [ 944 | { 945 | "filenamePattern": "*.vt100" 946 | }, 947 | { 948 | "filenamePattern": "*.log" 949 | }, 950 | { 951 | "filenamePattern": "*.txt" 952 | } 953 | ], 954 | "priority": "option" 955 | } 956 | ] 957 | }, 958 | "scripts": { 959 | "vscode:prepublish": "npm run compile", 960 | "compile": "tsc -p ./", 961 | "lint": "eslint . --ext .ts,.tsx", 962 | "watch": "tsc -watch -p ./" 963 | }, 964 | "devDependencies": { 965 | "@types/node": "^18.11.18", 966 | "@types/vscode": "^1.74.0", 967 | "@typescript-eslint/eslint-plugin": "^5.48.0", 968 | "@typescript-eslint/parser": "^5.48.0", 969 | "eslint": "^8.31.0", 970 | "typescript": "^4.9.4" 971 | } 972 | } 973 | -------------------------------------------------------------------------------- /sample/ExampleEditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleEditor.png -------------------------------------------------------------------------------- /sample/ExampleInline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleInline.png -------------------------------------------------------------------------------- /sample/ExampleResult.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResult.gif -------------------------------------------------------------------------------- /sample/ExampleResult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResult.png -------------------------------------------------------------------------------- /sample/ExampleResultDarkTheme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResultDarkTheme.gif -------------------------------------------------------------------------------- /sample/ExampleResultDarkTheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResultDarkTheme.png -------------------------------------------------------------------------------- /sample/ExampleResultLightTheme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResultLightTheme.gif -------------------------------------------------------------------------------- /sample/ExampleResultLightTheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleResultLightTheme.png -------------------------------------------------------------------------------- /sample/ExampleSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasFaller/vscode-vt100-syntax-highlight/2b6df173493b1b16758f8c4802d717f6ee8068f8/sample/ExampleSource.png -------------------------------------------------------------------------------- /sample/generate_sample.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | COLORS = [ 3 | 39, 4 | 30, 31, 32, 33, 34, 35, 36, 37, 5 | 90, 91, 92, 93, 94, 95, 96, 97 6 | ] 7 | 8 | for fgColor in COLORS: 9 | line = "" 10 | for style in [0, 1, 2, 4, 5, 7, 8]: 11 | line += f"\x1b[0;{style};{fgColor}m {style};{fgColor} " 12 | line += "\x1b[0m" 13 | print(line) -------------------------------------------------------------------------------- /sample/generate_sample2.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | FGCOLORS = [ 4 | 39, 5 | 30, 31, 32, 33, 34, 35, 36, 37, 6 | 90, 91, 92, 93, 94, 95, 96, 97 7 | ] 8 | BGCOLORS = [ 9 | 49, 10 | 40, 41, 42, 43, 44, 45, 46, 47, 11 | 100, 101, 102, 103, 104, 105, 106, 107 12 | ] 13 | ATTRIBUTES = [ 0, 1, 2, 4, 5, 7, 8 ] 14 | 15 | for bgColor in BGCOLORS: 16 | for fgColor in FGCOLORS: 17 | line = "" 18 | for attribute in ATTRIBUTES: 19 | style = f"{attribute};{fgColor};{bgColor}" 20 | line += f"\x1b[0;{style}m {style}" 21 | line += "\x1b[0m" 22 | print(line) 23 | print() -------------------------------------------------------------------------------- /sample/sample.vt100: -------------------------------------------------------------------------------- 1 |  0;39  1;39  2;39  4;39  5;39  7;39  8;39  2 |  0;30  1;30  2;30  4;30  5;30  7;30  8;30  3 |  0;31  1;31  2;31  4;31  5;31  7;31  8;31  4 |  0;32  1;32  2;32  4;32  5;32  7;32  8;32  5 |  0;33  1;33  2;33  4;33  5;33  7;33  8;33  6 |  0;34  1;34  2;34  4;34  5;34  7;34  8;34  7 |  0;35  1;35  2;35  4;35  5;35  7;35  8;35  8 |  0;36  1;36  2;36  4;36  5;36  7;36  8;36  9 |  0;37  1;37  2;37  4;37  5;37  7;37  8;37  10 |  0;90  1;90  2;90  4;90  5;90  7;90  8;90  11 |  0;91  1;91  2;91  4;91  5;91  7;91  8;91  12 |  0;92  1;92  2;92  4;92  5;92  7;92  8;92  13 |  0;93  1;93  2;93  4;93  5;93  7;93  8;93  14 |  0;94  1;94  2;94  4;94  5;94  7;94  8;94  15 |  0;95  1;95  2;95  4;95  5;95  7;95  8;95  16 |  0;96  1;96  2;96  4;96  5;96  7;96  8;96  17 |  0;97  1;97  2;97  4;97  5;97  7;97  8;97  18 | -------------------------------------------------------------------------------- /sample/sample2.vt100: -------------------------------------------------------------------------------- 1 | 0;39;491;39;492;39;494;39;495;39;497;39;498;39;49 2 | 0;30;491;30;492;30;494;30;495;30;497;30;498;30;49 3 | 0;31;491;31;492;31;494;31;495;31;497;31;498;31;49 4 | 0;32;491;32;492;32;494;32;495;32;497;32;498;32;49 5 | 0;33;491;33;492;33;494;33;495;33;497;33;498;33;49 6 | 0;34;491;34;492;34;494;34;495;34;497;34;498;34;49 7 | 0;35;491;35;492;35;494;35;495;35;497;35;498;35;49 8 | 0;36;491;36;492;36;494;36;495;36;497;36;498;36;49 9 | 0;37;491;37;492;37;494;37;495;37;497;37;498;37;49 10 | 0;90;491;90;492;90;494;90;495;90;497;90;498;90;49 11 | 0;91;491;91;492;91;494;91;495;91;497;91;498;91;49 12 | 0;92;491;92;492;92;494;92;495;92;497;92;498;92;49 13 | 0;93;491;93;492;93;494;93;495;93;497;93;498;93;49 14 | 0;94;491;94;492;94;494;94;495;94;497;94;498;94;49 15 | 0;95;491;95;492;95;494;95;495;95;497;95;498;95;49 16 | 0;96;491;96;492;96;494;96;495;96;497;96;498;96;49 17 | 0;97;491;97;492;97;494;97;495;97;497;97;498;97;49 18 | 19 | 0;39;401;39;402;39;404;39;405;39;407;39;408;39;40 20 | 0;30;401;30;402;30;404;30;405;30;407;30;408;30;40 21 | 0;31;401;31;402;31;404;31;405;31;407;31;408;31;40 22 | 0;32;401;32;402;32;404;32;405;32;407;32;408;32;40 23 | 0;33;401;33;402;33;404;33;405;33;407;33;408;33;40 24 | 0;34;401;34;402;34;404;34;405;34;407;34;408;34;40 25 | 0;35;401;35;402;35;404;35;405;35;407;35;408;35;40 26 | 0;36;401;36;402;36;404;36;405;36;407;36;408;36;40 27 | 0;37;401;37;402;37;404;37;405;37;407;37;408;37;40 28 | 0;90;401;90;402;90;404;90;405;90;407;90;408;90;40 29 | 0;91;401;91;402;91;404;91;405;91;407;91;408;91;40 30 | 0;92;401;92;402;92;404;92;405;92;407;92;408;92;40 31 | 0;93;401;93;402;93;404;93;405;93;407;93;408;93;40 32 | 0;94;401;94;402;94;404;94;405;94;407;94;408;94;40 33 | 0;95;401;95;402;95;404;95;405;95;407;95;408;95;40 34 | 0;96;401;96;402;96;404;96;405;96;407;96;408;96;40 35 | 0;97;401;97;402;97;404;97;405;97;407;97;408;97;40 36 | 37 | 0;39;411;39;412;39;414;39;415;39;417;39;418;39;41 38 | 0;30;411;30;412;30;414;30;415;30;417;30;418;30;41 39 | 0;31;411;31;412;31;414;31;415;31;417;31;418;31;41 40 | 0;32;411;32;412;32;414;32;415;32;417;32;418;32;41 41 | 0;33;411;33;412;33;414;33;415;33;417;33;418;33;41 42 | 0;34;411;34;412;34;414;34;415;34;417;34;418;34;41 43 | 0;35;411;35;412;35;414;35;415;35;417;35;418;35;41 44 | 0;36;411;36;412;36;414;36;415;36;417;36;418;36;41 45 | 0;37;411;37;412;37;414;37;415;37;417;37;418;37;41 46 | 0;90;411;90;412;90;414;90;415;90;417;90;418;90;41 47 | 0;91;411;91;412;91;414;91;415;91;417;91;418;91;41 48 | 0;92;411;92;412;92;414;92;415;92;417;92;418;92;41 49 | 0;93;411;93;412;93;414;93;415;93;417;93;418;93;41 50 | 0;94;411;94;412;94;414;94;415;94;417;94;418;94;41 51 | 0;95;411;95;412;95;414;95;415;95;417;95;418;95;41 52 | 0;96;411;96;412;96;414;96;415;96;417;96;418;96;41 53 | 0;97;411;97;412;97;414;97;415;97;417;97;418;97;41 54 | 55 | 0;39;421;39;422;39;424;39;425;39;427;39;428;39;42 56 | 0;30;421;30;422;30;424;30;425;30;427;30;428;30;42 57 | 0;31;421;31;422;31;424;31;425;31;427;31;428;31;42 58 | 0;32;421;32;422;32;424;32;425;32;427;32;428;32;42 59 | 0;33;421;33;422;33;424;33;425;33;427;33;428;33;42 60 | 0;34;421;34;422;34;424;34;425;34;427;34;428;34;42 61 | 0;35;421;35;422;35;424;35;425;35;427;35;428;35;42 62 | 0;36;421;36;422;36;424;36;425;36;427;36;428;36;42 63 | 0;37;421;37;422;37;424;37;425;37;427;37;428;37;42 64 | 0;90;421;90;422;90;424;90;425;90;427;90;428;90;42 65 | 0;91;421;91;422;91;424;91;425;91;427;91;428;91;42 66 | 0;92;421;92;422;92;424;92;425;92;427;92;428;92;42 67 | 0;93;421;93;422;93;424;93;425;93;427;93;428;93;42 68 | 0;94;421;94;422;94;424;94;425;94;427;94;428;94;42 69 | 0;95;421;95;422;95;424;95;425;95;427;95;428;95;42 70 | 0;96;421;96;422;96;424;96;425;96;427;96;428;96;42 71 | 0;97;421;97;422;97;424;97;425;97;427;97;428;97;42 72 | 73 | 0;39;431;39;432;39;434;39;435;39;437;39;438;39;43 74 | 0;30;431;30;432;30;434;30;435;30;437;30;438;30;43 75 | 0;31;431;31;432;31;434;31;435;31;437;31;438;31;43 76 | 0;32;431;32;432;32;434;32;435;32;437;32;438;32;43 77 | 0;33;431;33;432;33;434;33;435;33;437;33;438;33;43 78 | 0;34;431;34;432;34;434;34;435;34;437;34;438;34;43 79 | 0;35;431;35;432;35;434;35;435;35;437;35;438;35;43 80 | 0;36;431;36;432;36;434;36;435;36;437;36;438;36;43 81 | 0;37;431;37;432;37;434;37;435;37;437;37;438;37;43 82 | 0;90;431;90;432;90;434;90;435;90;437;90;438;90;43 83 | 0;91;431;91;432;91;434;91;435;91;437;91;438;91;43 84 | 0;92;431;92;432;92;434;92;435;92;437;92;438;92;43 85 | 0;93;431;93;432;93;434;93;435;93;437;93;438;93;43 86 | 0;94;431;94;432;94;434;94;435;94;437;94;438;94;43 87 | 0;95;431;95;432;95;434;95;435;95;437;95;438;95;43 88 | 0;96;431;96;432;96;434;96;435;96;437;96;438;96;43 89 | 0;97;431;97;432;97;434;97;435;97;437;97;438;97;43 90 | 91 | 0;39;441;39;442;39;444;39;445;39;447;39;448;39;44 92 | 0;30;441;30;442;30;444;30;445;30;447;30;448;30;44 93 | 0;31;441;31;442;31;444;31;445;31;447;31;448;31;44 94 | 0;32;441;32;442;32;444;32;445;32;447;32;448;32;44 95 | 0;33;441;33;442;33;444;33;445;33;447;33;448;33;44 96 | 0;34;441;34;442;34;444;34;445;34;447;34;448;34;44 97 | 0;35;441;35;442;35;444;35;445;35;447;35;448;35;44 98 | 0;36;441;36;442;36;444;36;445;36;447;36;448;36;44 99 | 0;37;441;37;442;37;444;37;445;37;447;37;448;37;44 100 | 0;90;441;90;442;90;444;90;445;90;447;90;448;90;44 101 | 0;91;441;91;442;91;444;91;445;91;447;91;448;91;44 102 | 0;92;441;92;442;92;444;92;445;92;447;92;448;92;44 103 | 0;93;441;93;442;93;444;93;445;93;447;93;448;93;44 104 | 0;94;441;94;442;94;444;94;445;94;447;94;448;94;44 105 | 0;95;441;95;442;95;444;95;445;95;447;95;448;95;44 106 | 0;96;441;96;442;96;444;96;445;96;447;96;448;96;44 107 | 0;97;441;97;442;97;444;97;445;97;447;97;448;97;44 108 | 109 | 0;39;451;39;452;39;454;39;455;39;457;39;458;39;45 110 | 0;30;451;30;452;30;454;30;455;30;457;30;458;30;45 111 | 0;31;451;31;452;31;454;31;455;31;457;31;458;31;45 112 | 0;32;451;32;452;32;454;32;455;32;457;32;458;32;45 113 | 0;33;451;33;452;33;454;33;455;33;457;33;458;33;45 114 | 0;34;451;34;452;34;454;34;455;34;457;34;458;34;45 115 | 0;35;451;35;452;35;454;35;455;35;457;35;458;35;45 116 | 0;36;451;36;452;36;454;36;455;36;457;36;458;36;45 117 | 0;37;451;37;452;37;454;37;455;37;457;37;458;37;45 118 | 0;90;451;90;452;90;454;90;455;90;457;90;458;90;45 119 | 0;91;451;91;452;91;454;91;455;91;457;91;458;91;45 120 | 0;92;451;92;452;92;454;92;455;92;457;92;458;92;45 121 | 0;93;451;93;452;93;454;93;455;93;457;93;458;93;45 122 | 0;94;451;94;452;94;454;94;455;94;457;94;458;94;45 123 | 0;95;451;95;452;95;454;95;455;95;457;95;458;95;45 124 | 0;96;451;96;452;96;454;96;455;96;457;96;458;96;45 125 | 0;97;451;97;452;97;454;97;455;97;457;97;458;97;45 126 | 127 | 0;39;461;39;462;39;464;39;465;39;467;39;468;39;46 128 | 0;30;461;30;462;30;464;30;465;30;467;30;468;30;46 129 | 0;31;461;31;462;31;464;31;465;31;467;31;468;31;46 130 | 0;32;461;32;462;32;464;32;465;32;467;32;468;32;46 131 | 0;33;461;33;462;33;464;33;465;33;467;33;468;33;46 132 | 0;34;461;34;462;34;464;34;465;34;467;34;468;34;46 133 | 0;35;461;35;462;35;464;35;465;35;467;35;468;35;46 134 | 0;36;461;36;462;36;464;36;465;36;467;36;468;36;46 135 | 0;37;461;37;462;37;464;37;465;37;467;37;468;37;46 136 | 0;90;461;90;462;90;464;90;465;90;467;90;468;90;46 137 | 0;91;461;91;462;91;464;91;465;91;467;91;468;91;46 138 | 0;92;461;92;462;92;464;92;465;92;467;92;468;92;46 139 | 0;93;461;93;462;93;464;93;465;93;467;93;468;93;46 140 | 0;94;461;94;462;94;464;94;465;94;467;94;468;94;46 141 | 0;95;461;95;462;95;464;95;465;95;467;95;468;95;46 142 | 0;96;461;96;462;96;464;96;465;96;467;96;468;96;46 143 | 0;97;461;97;462;97;464;97;465;97;467;97;468;97;46 144 | 145 | 0;39;471;39;472;39;474;39;475;39;477;39;478;39;47 146 | 0;30;471;30;472;30;474;30;475;30;477;30;478;30;47 147 | 0;31;471;31;472;31;474;31;475;31;477;31;478;31;47 148 | 0;32;471;32;472;32;474;32;475;32;477;32;478;32;47 149 | 0;33;471;33;472;33;474;33;475;33;477;33;478;33;47 150 | 0;34;471;34;472;34;474;34;475;34;477;34;478;34;47 151 | 0;35;471;35;472;35;474;35;475;35;477;35;478;35;47 152 | 0;36;471;36;472;36;474;36;475;36;477;36;478;36;47 153 | 0;37;471;37;472;37;474;37;475;37;477;37;478;37;47 154 | 0;90;471;90;472;90;474;90;475;90;477;90;478;90;47 155 | 0;91;471;91;472;91;474;91;475;91;477;91;478;91;47 156 | 0;92;471;92;472;92;474;92;475;92;477;92;478;92;47 157 | 0;93;471;93;472;93;474;93;475;93;477;93;478;93;47 158 | 0;94;471;94;472;94;474;94;475;94;477;94;478;94;47 159 | 0;95;471;95;472;95;474;95;475;95;477;95;478;95;47 160 | 0;96;471;96;472;96;474;96;475;96;477;96;478;96;47 161 | 0;97;471;97;472;97;474;97;475;97;477;97;478;97;47 162 | 163 | 0;39;1001;39;1002;39;1004;39;1005;39;1007;39;1008;39;100 164 | 0;30;1001;30;1002;30;1004;30;1005;30;1007;30;1008;30;100 165 | 0;31;1001;31;1002;31;1004;31;1005;31;1007;31;1008;31;100 166 | 0;32;1001;32;1002;32;1004;32;1005;32;1007;32;1008;32;100 167 | 0;33;1001;33;1002;33;1004;33;1005;33;1007;33;1008;33;100 168 | 0;34;1001;34;1002;34;1004;34;1005;34;1007;34;1008;34;100 169 | 0;35;1001;35;1002;35;1004;35;1005;35;1007;35;1008;35;100 170 | 0;36;1001;36;1002;36;1004;36;1005;36;1007;36;1008;36;100 171 | 0;37;1001;37;1002;37;1004;37;1005;37;1007;37;1008;37;100 172 | 0;90;1001;90;1002;90;1004;90;1005;90;1007;90;1008;90;100 173 | 0;91;1001;91;1002;91;1004;91;1005;91;1007;91;1008;91;100 174 | 0;92;1001;92;1002;92;1004;92;1005;92;1007;92;1008;92;100 175 | 0;93;1001;93;1002;93;1004;93;1005;93;1007;93;1008;93;100 176 | 0;94;1001;94;1002;94;1004;94;1005;94;1007;94;1008;94;100 177 | 0;95;1001;95;1002;95;1004;95;1005;95;1007;95;1008;95;100 178 | 0;96;1001;96;1002;96;1004;96;1005;96;1007;96;1008;96;100 179 | 0;97;1001;97;1002;97;1004;97;1005;97;1007;97;1008;97;100 180 | 181 | 0;39;1011;39;1012;39;1014;39;1015;39;1017;39;1018;39;101 182 | 0;30;1011;30;1012;30;1014;30;1015;30;1017;30;1018;30;101 183 | 0;31;1011;31;1012;31;1014;31;1015;31;1017;31;1018;31;101 184 | 0;32;1011;32;1012;32;1014;32;1015;32;1017;32;1018;32;101 185 | 0;33;1011;33;1012;33;1014;33;1015;33;1017;33;1018;33;101 186 | 0;34;1011;34;1012;34;1014;34;1015;34;1017;34;1018;34;101 187 | 0;35;1011;35;1012;35;1014;35;1015;35;1017;35;1018;35;101 188 | 0;36;1011;36;1012;36;1014;36;1015;36;1017;36;1018;36;101 189 | 0;37;1011;37;1012;37;1014;37;1015;37;1017;37;1018;37;101 190 | 0;90;1011;90;1012;90;1014;90;1015;90;1017;90;1018;90;101 191 | 0;91;1011;91;1012;91;1014;91;1015;91;1017;91;1018;91;101 192 | 0;92;1011;92;1012;92;1014;92;1015;92;1017;92;1018;92;101 193 | 0;93;1011;93;1012;93;1014;93;1015;93;1017;93;1018;93;101 194 | 0;94;1011;94;1012;94;1014;94;1015;94;1017;94;1018;94;101 195 | 0;95;1011;95;1012;95;1014;95;1015;95;1017;95;1018;95;101 196 | 0;96;1011;96;1012;96;1014;96;1015;96;1017;96;1018;96;101 197 | 0;97;1011;97;1012;97;1014;97;1015;97;1017;97;1018;97;101 198 | 199 | 0;39;1021;39;1022;39;1024;39;1025;39;1027;39;1028;39;102 200 | 0;30;1021;30;1022;30;1024;30;1025;30;1027;30;1028;30;102 201 | 0;31;1021;31;1022;31;1024;31;1025;31;1027;31;1028;31;102 202 | 0;32;1021;32;1022;32;1024;32;1025;32;1027;32;1028;32;102 203 | 0;33;1021;33;1022;33;1024;33;1025;33;1027;33;1028;33;102 204 | 0;34;1021;34;1022;34;1024;34;1025;34;1027;34;1028;34;102 205 | 0;35;1021;35;1022;35;1024;35;1025;35;1027;35;1028;35;102 206 | 0;36;1021;36;1022;36;1024;36;1025;36;1027;36;1028;36;102 207 | 0;37;1021;37;1022;37;1024;37;1025;37;1027;37;1028;37;102 208 | 0;90;1021;90;1022;90;1024;90;1025;90;1027;90;1028;90;102 209 | 0;91;1021;91;1022;91;1024;91;1025;91;1027;91;1028;91;102 210 | 0;92;1021;92;1022;92;1024;92;1025;92;1027;92;1028;92;102 211 | 0;93;1021;93;1022;93;1024;93;1025;93;1027;93;1028;93;102 212 | 0;94;1021;94;1022;94;1024;94;1025;94;1027;94;1028;94;102 213 | 0;95;1021;95;1022;95;1024;95;1025;95;1027;95;1028;95;102 214 | 0;96;1021;96;1022;96;1024;96;1025;96;1027;96;1028;96;102 215 | 0;97;1021;97;1022;97;1024;97;1025;97;1027;97;1028;97;102 216 | 217 | 0;39;1031;39;1032;39;1034;39;1035;39;1037;39;1038;39;103 218 | 0;30;1031;30;1032;30;1034;30;1035;30;1037;30;1038;30;103 219 | 0;31;1031;31;1032;31;1034;31;1035;31;1037;31;1038;31;103 220 | 0;32;1031;32;1032;32;1034;32;1035;32;1037;32;1038;32;103 221 | 0;33;1031;33;1032;33;1034;33;1035;33;1037;33;1038;33;103 222 | 0;34;1031;34;1032;34;1034;34;1035;34;1037;34;1038;34;103 223 | 0;35;1031;35;1032;35;1034;35;1035;35;1037;35;1038;35;103 224 | 0;36;1031;36;1032;36;1034;36;1035;36;1037;36;1038;36;103 225 | 0;37;1031;37;1032;37;1034;37;1035;37;1037;37;1038;37;103 226 | 0;90;1031;90;1032;90;1034;90;1035;90;1037;90;1038;90;103 227 | 0;91;1031;91;1032;91;1034;91;1035;91;1037;91;1038;91;103 228 | 0;92;1031;92;1032;92;1034;92;1035;92;1037;92;1038;92;103 229 | 0;93;1031;93;1032;93;1034;93;1035;93;1037;93;1038;93;103 230 | 0;94;1031;94;1032;94;1034;94;1035;94;1037;94;1038;94;103 231 | 0;95;1031;95;1032;95;1034;95;1035;95;1037;95;1038;95;103 232 | 0;96;1031;96;1032;96;1034;96;1035;96;1037;96;1038;96;103 233 | 0;97;1031;97;1032;97;1034;97;1035;97;1037;97;1038;97;103 234 | 235 | 0;39;1041;39;1042;39;1044;39;1045;39;1047;39;1048;39;104 236 | 0;30;1041;30;1042;30;1044;30;1045;30;1047;30;1048;30;104 237 | 0;31;1041;31;1042;31;1044;31;1045;31;1047;31;1048;31;104 238 | 0;32;1041;32;1042;32;1044;32;1045;32;1047;32;1048;32;104 239 | 0;33;1041;33;1042;33;1044;33;1045;33;1047;33;1048;33;104 240 | 0;34;1041;34;1042;34;1044;34;1045;34;1047;34;1048;34;104 241 | 0;35;1041;35;1042;35;1044;35;1045;35;1047;35;1048;35;104 242 | 0;36;1041;36;1042;36;1044;36;1045;36;1047;36;1048;36;104 243 | 0;37;1041;37;1042;37;1044;37;1045;37;1047;37;1048;37;104 244 | 0;90;1041;90;1042;90;1044;90;1045;90;1047;90;1048;90;104 245 | 0;91;1041;91;1042;91;1044;91;1045;91;1047;91;1048;91;104 246 | 0;92;1041;92;1042;92;1044;92;1045;92;1047;92;1048;92;104 247 | 0;93;1041;93;1042;93;1044;93;1045;93;1047;93;1048;93;104 248 | 0;94;1041;94;1042;94;1044;94;1045;94;1047;94;1048;94;104 249 | 0;95;1041;95;1042;95;1044;95;1045;95;1047;95;1048;95;104 250 | 0;96;1041;96;1042;96;1044;96;1045;96;1047;96;1048;96;104 251 | 0;97;1041;97;1042;97;1044;97;1045;97;1047;97;1048;97;104 252 | 253 | 0;39;1051;39;1052;39;1054;39;1055;39;1057;39;1058;39;105 254 | 0;30;1051;30;1052;30;1054;30;1055;30;1057;30;1058;30;105 255 | 0;31;1051;31;1052;31;1054;31;1055;31;1057;31;1058;31;105 256 | 0;32;1051;32;1052;32;1054;32;1055;32;1057;32;1058;32;105 257 | 0;33;1051;33;1052;33;1054;33;1055;33;1057;33;1058;33;105 258 | 0;34;1051;34;1052;34;1054;34;1055;34;1057;34;1058;34;105 259 | 0;35;1051;35;1052;35;1054;35;1055;35;1057;35;1058;35;105 260 | 0;36;1051;36;1052;36;1054;36;1055;36;1057;36;1058;36;105 261 | 0;37;1051;37;1052;37;1054;37;1055;37;1057;37;1058;37;105 262 | 0;90;1051;90;1052;90;1054;90;1055;90;1057;90;1058;90;105 263 | 0;91;1051;91;1052;91;1054;91;1055;91;1057;91;1058;91;105 264 | 0;92;1051;92;1052;92;1054;92;1055;92;1057;92;1058;92;105 265 | 0;93;1051;93;1052;93;1054;93;1055;93;1057;93;1058;93;105 266 | 0;94;1051;94;1052;94;1054;94;1055;94;1057;94;1058;94;105 267 | 0;95;1051;95;1052;95;1054;95;1055;95;1057;95;1058;95;105 268 | 0;96;1051;96;1052;96;1054;96;1055;96;1057;96;1058;96;105 269 | 0;97;1051;97;1052;97;1054;97;1055;97;1057;97;1058;97;105 270 | 271 | 0;39;1061;39;1062;39;1064;39;1065;39;1067;39;1068;39;106 272 | 0;30;1061;30;1062;30;1064;30;1065;30;1067;30;1068;30;106 273 | 0;31;1061;31;1062;31;1064;31;1065;31;1067;31;1068;31;106 274 | 0;32;1061;32;1062;32;1064;32;1065;32;1067;32;1068;32;106 275 | 0;33;1061;33;1062;33;1064;33;1065;33;1067;33;1068;33;106 276 | 0;34;1061;34;1062;34;1064;34;1065;34;1067;34;1068;34;106 277 | 0;35;1061;35;1062;35;1064;35;1065;35;1067;35;1068;35;106 278 | 0;36;1061;36;1062;36;1064;36;1065;36;1067;36;1068;36;106 279 | 0;37;1061;37;1062;37;1064;37;1065;37;1067;37;1068;37;106 280 | 0;90;1061;90;1062;90;1064;90;1065;90;1067;90;1068;90;106 281 | 0;91;1061;91;1062;91;1064;91;1065;91;1067;91;1068;91;106 282 | 0;92;1061;92;1062;92;1064;92;1065;92;1067;92;1068;92;106 283 | 0;93;1061;93;1062;93;1064;93;1065;93;1067;93;1068;93;106 284 | 0;94;1061;94;1062;94;1064;94;1065;94;1067;94;1068;94;106 285 | 0;95;1061;95;1062;95;1064;95;1065;95;1067;95;1068;95;106 286 | 0;96;1061;96;1062;96;1064;96;1065;96;1067;96;1068;96;106 287 | 0;97;1061;97;1062;97;1064;97;1065;97;1067;97;1068;97;106 288 | 289 | 0;39;1071;39;1072;39;1074;39;1075;39;1077;39;1078;39;107 290 | 0;30;1071;30;1072;30;1074;30;1075;30;1077;30;1078;30;107 291 | 0;31;1071;31;1072;31;1074;31;1075;31;1077;31;1078;31;107 292 | 0;32;1071;32;1072;32;1074;32;1075;32;1077;32;1078;32;107 293 | 0;33;1071;33;1072;33;1074;33;1075;33;1077;33;1078;33;107 294 | 0;34;1071;34;1072;34;1074;34;1075;34;1077;34;1078;34;107 295 | 0;35;1071;35;1072;35;1074;35;1075;35;1077;35;1078;35;107 296 | 0;36;1071;36;1072;36;1074;36;1075;36;1077;36;1078;36;107 297 | 0;37;1071;37;1072;37;1074;37;1075;37;1077;37;1078;37;107 298 | 0;90;1071;90;1072;90;1074;90;1075;90;1077;90;1078;90;107 299 | 0;91;1071;91;1072;91;1074;91;1075;91;1077;91;1078;91;107 300 | 0;92;1071;92;1072;92;1074;92;1075;92;1077;92;1078;92;107 301 | 0;93;1071;93;1072;93;1074;93;1075;93;1077;93;1078;93;107 302 | 0;94;1071;94;1072;94;1074;94;1075;94;1077;94;1078;94;107 303 | 0;95;1071;95;1072;95;1074;95;1075;95;1077;95;1078;95;107 304 | 0;96;1071;96;1072;96;1074;96;1075;96;1077;96;1078;96;107 305 | 0;97;1071;97;1072;97;1074;97;1075;97;1077;97;1078;97;107 306 | 307 | -------------------------------------------------------------------------------- /sample/sample_inline.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | print('✅ [DEBUG] I am a debug message ') 3 | print('🆗 [INFO] I am an information ') 4 | print('⚠️ [WARNING] I am a warning ') 5 | print('🛑 [ERROR] I am an error ') 6 | -------------------------------------------------------------------------------- /snippets/vt100.json: -------------------------------------------------------------------------------- 1 | { 2 | "escape code": { 3 | "scope": "vt100", 4 | "prefix": "escape", 5 | "body": "\u001B[${1:0}m", 6 | "description": "Custom VT100 Color escape code" 7 | }, 8 | 9 | "normal all": { 10 | "scope": "vt100", 11 | "prefix": "normal all", 12 | "body": "\u001B[0m", 13 | "description": "Reset all VT100 escape code" 14 | }, 15 | "default all": { 16 | "scope": "vt100", 17 | "prefix": "default all", 18 | "body": "\u001B[0m", 19 | "description": "Reset all VT100 escape code" 20 | }, 21 | "reset all": { 22 | "scope": "vt100", 23 | "prefix": "reset all", 24 | "body": "\u001B[0m", 25 | "description": "Reset all VT100 escape code" 26 | }, 27 | 28 | "normal color": { 29 | "scope": "vt100", 30 | "prefix": "normal color", 31 | "body": "\u001B[39;49m", 32 | "description": "Reset all colors VT100 escape sequence" 33 | }, 34 | "default color": { 35 | "scope": "vt100", 36 | "prefix": "default color", 37 | "body": "\u001B[39;49m", 38 | "description": "Reset all colors VT100 escape sequence" 39 | }, 40 | "reset color": { 41 | "scope": "vt100", 42 | "prefix": "reset color", 43 | "body": "\u001B[39;49m", 44 | "description": "Reset all colors VT100 escape sequence" 45 | }, 46 | 47 | "normal text style": { 48 | "scope": "vt100", 49 | "prefix": "normal text", 50 | "body": "\u001B[21;22;24;25;27;28m", 51 | "description": "Reset to unstyled text VT100 escape sequence" 52 | }, 53 | "default text style": { 54 | "scope": "vt100", 55 | "prefix": "default text", 56 | "body": "\u001B[21;22;24;25;27;28m", 57 | "description": "Reset to unstyled text VT100 escape sequence" 58 | }, 59 | "reset text style": { 60 | "scope": "vt100", 61 | "prefix": "reset text", 62 | "body": "\u001B[21;22;24;25;27;28m", 63 | "description": "Reset unstyled text VT100 escape sequence" 64 | }, 65 | 66 | "normal foreground color": { 67 | "scope": "vt100", 68 | "prefix": "normal foreground color", 69 | "body": "\u001B[39m", 70 | "description": "Default Text (Foreground) Color VT100 escape code" 71 | }, 72 | "default foreground color": { 73 | "scope": "vt100", 74 | "prefix": "default foreground color", 75 | "body": "\u001B[39m", 76 | "description": "Default Text (Foreground) Color VT100 escape code" 77 | }, 78 | "reset foreground color": { 79 | "scope": "vt100", 80 | "prefix": "reset foreground color", 81 | "body": "\u001B[39m", 82 | "description": "Default Text (Foreground) Color VT100 escape code" 83 | }, 84 | 85 | "normal text color": { 86 | "scope": "vt100", 87 | "prefix": "normal text color", 88 | "body": "\u001B[39m", 89 | "description": "Default Text (Foreground) Color VT100 escape code" 90 | }, 91 | "default text color": { 92 | "scope": "vt100", 93 | "prefix": "default text color", 94 | "body": "\u001B[39m", 95 | "description": "Default Text (Foreground) Color VT100 escape code" 96 | }, 97 | "reset text color": { 98 | "scope": "vt100", 99 | "prefix": "reset text color", 100 | "body": "\u001B[39m", 101 | "description": "Default Text (Foreground) Color VT100 escape code" 102 | }, 103 | 104 | "normal background color": { 105 | "scope": "vt100", 106 | "prefix": "normal background color", 107 | "body": "\u001B[49m", 108 | "description": "Default Background Color VT100 escape code" 109 | }, 110 | "default background color": { 111 | "scope": "vt100", 112 | "prefix": "default background color", 113 | "body": "\u001B[49m", 114 | "description": "Default Background Color VT100 escape code" 115 | }, 116 | "reset background color": { 117 | "scope": "vt100", 118 | "prefix": "reset background color", 119 | "body": "\u001B[49m", 120 | "description": "Default Background Color VT100 escape code" 121 | }, 122 | 123 | "black foreground": { 124 | "scope": "vt100", 125 | "prefix": "black foreground", 126 | "body": "\u001B[30m", 127 | "description": "Black Text (Foreground) Color VT100 escape code" 128 | }, 129 | "red foreground": { 130 | "scope": "vt100", 131 | "prefix": "red foreground", 132 | "body": "\u001B[31m", 133 | "description": "Red Text (Foreground) Color VT100 escape code" 134 | }, 135 | "green foreground": { 136 | "scope": "vt100", 137 | "prefix": "green foreground", 138 | "body": "\u001B[32m", 139 | "description": "Green Text (Foreground) Color VT100 escape code" 140 | }, 141 | "yellow foreground": { 142 | "scope": "vt100", 143 | "prefix": "yellow foreground", 144 | "body": "\u001B[33m", 145 | "description": "Yellow Text (Foreground) Color VT100 escape code" 146 | }, 147 | "blue foreground": { 148 | "scope": "vt100", 149 | "prefix": "blue foreground", 150 | "body": "\u001B[34m", 151 | "description": "Blue Text (Foreground) Color VT100 escape code" 152 | }, 153 | "magenta foreground": { 154 | "scope": "vt100", 155 | "prefix": "magenta foreground", 156 | "body": "\u001B[35m", 157 | "description": "Magenta Text (Foreground) Color VT100 escape code" 158 | }, 159 | "cyan foreground": { 160 | "scope": "vt100", 161 | "prefix": "cyan foreground", 162 | "body": "\u001B[36m", 163 | "description": "Cyan Text (Foreground) Color VT100 escape code" 164 | }, 165 | "light gray foreground": { 166 | "scope": "vt100", 167 | "prefix": "light gray foreground", 168 | "body": "\u001B[37m", 169 | "description": "Light-Gray Text (Foreground) Color VT100 escape code" 170 | }, 171 | "dark gray foreground": { 172 | "scope": "vt100", 173 | "prefix": "dark gray foreground", 174 | "body": "\u001B[90m", 175 | "description": "Dark-Gray Text (Foreground) Color VT100 escape code" 176 | }, 177 | "light red foreground": { 178 | "scope": "vt100", 179 | "prefix": "light red foreground", 180 | "body": "\u001B[91m", 181 | "description": "Light-Red Text (Foreground) Color VT100 escape code" 182 | }, 183 | "light green foreground": { 184 | "scope": "vt100", 185 | "prefix": "light green foreground", 186 | "body": "\u001B[92m", 187 | "description": "Light-Green Text (Foreground) Color VT100 escape code" 188 | }, 189 | "light yellow foreground": { 190 | "scope": "vt100", 191 | "prefix": "light yellow foreground", 192 | "body": "\u001B[93m", 193 | "description": "Light-Yellow Text (Foreground) Color VT100 escape code" 194 | }, 195 | "light blue foreground": { 196 | "scope": "vt100", 197 | "prefix": "light blue foreground", 198 | "body": "\u001B[94m", 199 | "description": "Light-Blue Text (Foreground) Color VT100 escape code" 200 | }, 201 | "light magenta foreground": { 202 | "scope": "vt100", 203 | "prefix": "light magenta foreground", 204 | "body": "\u001B[95m", 205 | "description": "Light-Magenta Text (Foreground) Color VT100 escape code" 206 | }, 207 | "light cyan foreground": { 208 | "scope": "vt100", 209 | "prefix": "light cyan foreground", 210 | "body": "\u001B[96m", 211 | "description": "Light-Cyan Text (Foreground) Color VT100 escape code" 212 | }, 213 | "white foreground": { 214 | "scope": "vt100", 215 | "prefix": "white foreground", 216 | "body": "\u001B[97m", 217 | "description": "White Text (Foreground) Color VT100 escape code" 218 | }, 219 | 220 | "black text": { 221 | "scope": "vt100", 222 | "prefix": "black text", 223 | "body": "\u001B[30m", 224 | "description": "Black Text (Foreground) Color VT100 escape code" 225 | }, 226 | "red text": { 227 | "scope": "vt100", 228 | "prefix": "red text", 229 | "body": "\u001B[31m", 230 | "description": "Red Text (Foreground) Color VT100 escape code" 231 | }, 232 | "green text": { 233 | "scope": "vt100", 234 | "prefix": "green text", 235 | "body": "\u001B[32m", 236 | "description": "Green Text (Foreground) Color VT100 escape code" 237 | }, 238 | "yellow text": { 239 | "scope": "vt100", 240 | "prefix": "yellow text", 241 | "body": "\u001B[33m", 242 | "description": "Yellow Text (Foreground) Color VT100 escape code" 243 | }, 244 | "blue text": { 245 | "scope": "vt100", 246 | "prefix": "blue text", 247 | "body": "\u001B[34m", 248 | "description": "Blue Text (Foreground) Color VT100 escape code" 249 | }, 250 | "magenta text": { 251 | "scope": "vt100", 252 | "prefix": "magenta text", 253 | "body": "\u001B[35m", 254 | "description": "Magenta Text (Foreground) Color VT100 escape code" 255 | }, 256 | "cyan text": { 257 | "scope": "vt100", 258 | "prefix": "cyan text", 259 | "body": "\u001B[36m", 260 | "description": "Cyan Text (Foreground) Color VT100 escape code" 261 | }, 262 | "light gray text": { 263 | "scope": "vt100", 264 | "prefix": "light gray text", 265 | "body": "\u001B[37m", 266 | "description": "Light-Gray Text (Foreground) Color VT100 escape code" 267 | }, 268 | "dark gray text": { 269 | "scope": "vt100", 270 | "prefix": "dark gray text", 271 | "body": "\u001B[90m", 272 | "description": "Dark-Gray Text (Foreground) Color VT100 escape code" 273 | }, 274 | "light red text": { 275 | "scope": "vt100", 276 | "prefix": "light red text", 277 | "body": "\u001B[91m", 278 | "description": "Light-Red Text (Foreground) Color VT100 escape code" 279 | }, 280 | "light green text": { 281 | "scope": "vt100", 282 | "prefix": "light green text", 283 | "body": "\u001B[92m", 284 | "description": "Light-Green Text (Foreground) Color VT100 escape code" 285 | }, 286 | "light yellow text": { 287 | "scope": "vt100", 288 | "prefix": "light yellow text", 289 | "body": "\u001B[93m", 290 | "description": "Light-Yellow Text (Foreground) Color VT100 escape code" 291 | }, 292 | "light blue text": { 293 | "scope": "vt100", 294 | "prefix": "light blue text", 295 | "body": "\u001B[94m", 296 | "description": "Light-Blue Text (Foreground) Color VT100 escape code" 297 | }, 298 | "light magenta text": { 299 | "scope": "vt100", 300 | "prefix": "light magenta text", 301 | "body": "\u001B[95m", 302 | "description": "Light-Magenta Text (Foreground) Color VT100 escape code" 303 | }, 304 | "light cyan text": { 305 | "scope": "vt100", 306 | "prefix": "light cyan text", 307 | "body": "\u001B[96m", 308 | "description": "Light-Cyan Text (Foreground) Color VT100 escape code" 309 | }, 310 | "white text": { 311 | "scope": "vt100", 312 | "prefix": "white text", 313 | "body": "\u001B[97m", 314 | "description": "White Text (Foreground) Color VT100 escape code" 315 | }, 316 | 317 | "black background": { 318 | "scope": "vt100", 319 | "prefix": "black background", 320 | "body": "\u001B[40m", 321 | "description": "Black Background VT100 escape code" 322 | }, 323 | "red background": { 324 | "scope": "vt100", 325 | "prefix": "red background", 326 | "body": "\u001B[41m", 327 | "description": "Red Background VT100 escape code" 328 | }, 329 | "green background": { 330 | "scope": "vt100", 331 | "prefix": "green background", 332 | "body": "\u001B[42m", 333 | "description": "Green Background VT100 escape code" 334 | }, 335 | "yellow background": { 336 | "scope": "vt100", 337 | "prefix": "yellow background", 338 | "body": "\u001B[43m", 339 | "description": "Yellow Background VT100 escape code" 340 | }, 341 | "blue background": { 342 | "scope": "vt100", 343 | "prefix": "blue background", 344 | "body": "\u001B[44m", 345 | "description": "Blue Background VT100 escape code" 346 | }, 347 | "magenta background": { 348 | "scope": "vt100", 349 | "prefix": "magenta background", 350 | "body": "\u001B[45m", 351 | "description": "Magenta Background VT100 escape code" 352 | }, 353 | "cyan background": { 354 | "scope": "vt100", 355 | "prefix": "cyan background", 356 | "body": "\u001B[46m", 357 | "description": "Cyan Background VT100 escape code" 358 | }, 359 | "light gray background": { 360 | "scope": "vt100", 361 | "prefix": "light gray background", 362 | "body": "\u001B[47m", 363 | "description": "Light-Gray Background VT100 escape code" 364 | }, 365 | "dark gray background": { 366 | "scope": "vt100", 367 | "prefix": "dark gray background", 368 | "body": "\u001B[100m", 369 | "description": "Dark-Gray Background VT100 escape code" 370 | }, 371 | "light red background": { 372 | "scope": "vt100", 373 | "prefix": "light red background", 374 | "body": "\u001B[101m", 375 | "description": "Light-Red Background VT100 escape code" 376 | }, 377 | "light green background": { 378 | "scope": "vt100", 379 | "prefix": "light green background", 380 | "body": "\u001B[102m", 381 | "description": "Light-Green Background VT100 escape code" 382 | }, 383 | "light yellow background": { 384 | "scope": "vt100", 385 | "prefix": "light yellow background", 386 | "body": "\u001B[103m", 387 | "description": "Light-Yellow Background VT100 escape code" 388 | }, 389 | "light blue background": { 390 | "scope": "vt100", 391 | "prefix": "light blue background", 392 | "body": "\u001B[104m", 393 | "description": "Light-Blue Background VT100 escape code" 394 | }, 395 | "light magenta background": { 396 | "scope": "vt100", 397 | "prefix": "light magenta background", 398 | "body": "\u001B[105m", 399 | "description": "Light-Magenta Background VT100 escape code" 400 | }, 401 | "light cyan background": { 402 | "scope": "vt100", 403 | "prefix": "light cyan background", 404 | "body": "\u001B[106m", 405 | "description": "Light-Cyan Background VT100 escape code" 406 | }, 407 | "white background": { 408 | "scope": "vt100", 409 | "prefix": "white background", 410 | "body": "\u001B[107m", 411 | "description": "White Background VT100 escape code" 412 | }, 413 | 414 | "bold text": { 415 | "scope": "vt100", 416 | "prefix": "bold text", 417 | "body": "\u001B[1m", 418 | "description": "Bold Text VT100 escape code" 419 | }, 420 | "reset bold text": { 421 | "scope": "vt100", 422 | "prefix": "reset bold text", 423 | "body": "\u001B[21m", 424 | "description": "Reset Bold Text VT100 escape code" 425 | }, 426 | "dim text": { 427 | "scope": "vt100", 428 | "prefix": "dim text", 429 | "body": "\u001B[2m", 430 | "description": "Dimmed Text VT100 escape code" 431 | }, 432 | "reset dim text": { 433 | "scope": "vt100", 434 | "prefix": "reset dim text", 435 | "body": "\u001B[22m", 436 | "description": "Reset Dimmed Text VT100 escape code" 437 | }, 438 | "underliend text": { 439 | "scope": "vt100", 440 | "prefix": "underlined text", 441 | "body": "\u001B[4m", 442 | "description": "Underlined Text VT100 escape code" 443 | }, 444 | "reset underliend text": { 445 | "scope": "vt100", 446 | "prefix": "reset underlined text", 447 | "body": "\u001B[24m", 448 | "description": "Reset Underlined Text VT100 escape code" 449 | }, 450 | "blinking text": { 451 | "scope": "vt100", 452 | "prefix": "blinking text", 453 | "body": "\u001B[5m", 454 | "description": "Blinking Text VT100 escape code" 455 | }, 456 | "reset blinking text": { 457 | "scope": "vt100", 458 | "prefix": "reset blinking text", 459 | "body": "\u001B[25m", 460 | "description": "Reset Blinking Text VT100 escape code" 461 | }, 462 | "inverted text": { 463 | "scope": "vt100", 464 | "prefix": "inverted text", 465 | "body": "\u001B[5m", 466 | "description": "Inverted Text VT100 escape code" 467 | }, 468 | "reset inverted text": { 469 | "scope": "vt100", 470 | "prefix": "reset inverted text", 471 | "body": "\u001B[25m", 472 | "description": "Reset Inverted Text VT100 escape code" 473 | }, 474 | "reverse text": { 475 | "scope": "vt100", 476 | "prefix": "reverse text", 477 | "body": "\u001B[7m", 478 | "description": "Inverted Text VT100 escape code" 479 | }, 480 | "reset reverse text": { 481 | "scope": "vt100", 482 | "prefix": "reset reverse text", 483 | "body": "\u001B[27m", 484 | "description": "Reset Inverted Text VT100 escape code" 485 | }, 486 | "hidden text": { 487 | "scope": "vt100", 488 | "prefix": "hidden text", 489 | "body": "\u001B[8m", 490 | "description": "Hidden Text VT100 escape code" 491 | }, 492 | "reset hidden text": { 493 | "scope": "vt100", 494 | "prefix": "reset hidden text", 495 | "body": "\u001B[28m", 496 | "description": "Reset Hidden Text VT100 escape code" 497 | } 498 | } -------------------------------------------------------------------------------- /src/configurationManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export const COLORS: string[] = [ 4 | 'default', 'inverted', 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'light-gray', 'dark-gray', 5 | 'light-red', 'light-green', 'light-yellow', 'light-blue', 'light-magenta', 'light-cyan', 'white' 6 | ]; 7 | export const ATTRIBUTES: string[] = [ 8 | 'bold', 'dim', 'underlined', 'blink', 'inverted', 'hidden' 9 | ]; 10 | 11 | export class StyleConfiguration { 12 | 13 | constructor( 14 | public readonly editorStyle: vscode.DecorationRenderOptions, 15 | public readonly previewStyle: any 16 | ) { } 17 | 18 | } 19 | 20 | export class ConfigurationManager implements vscode.Disposable { 21 | 22 | private _styles: Map; 23 | private _customCss: any; 24 | private _fontSettings: any; 25 | private _disposables: vscode.Disposable[] = []; 26 | private _defaultConfiguration: Map; 27 | 28 | private _onReloadEmitter = new vscode.EventEmitter(); 29 | public onReload = this._onReloadEmitter.event; 30 | 31 | constructor() { 32 | this._styles = new Map(); 33 | this._customCss = {}; 34 | this._defaultConfiguration = this._loadDefaultConfiguration(); 35 | 36 | this._reload(); 37 | 38 | vscode.workspace.onDidChangeConfiguration((event) => { 39 | this._reload(); 40 | }, null, this._disposables); 41 | } 42 | 43 | dispose(): void { 44 | this._onReloadEmitter.dispose(); 45 | this._disposables.forEach((disposable) => disposable.dispose()); 46 | this._disposables = []; 47 | } 48 | 49 | public getSettings(): IterableIterator<[string, StyleConfiguration]> { 50 | return this._styles.entries(); 51 | } 52 | 53 | public getCustomCss(): any { 54 | return this._customCss; 55 | } 56 | 57 | public getFontSettings(): any { 58 | return this._fontSettings; 59 | } 60 | 61 | private _reload(): void { 62 | const configuration = vscode.workspace.getConfiguration('vt100'); 63 | 64 | for (const color of COLORS) { 65 | this._loadStyle('foreground-color-' + color, configuration); 66 | this._loadStyle('background-color-' + color, configuration); 67 | } 68 | 69 | for (const attribute of ATTRIBUTES) { 70 | this._loadStyle('attribute-' + attribute, configuration); 71 | } 72 | 73 | this._loadStyle('escape-sequence', configuration); 74 | this._loadStyle('text', configuration); 75 | 76 | this._loadFontSettings(configuration); 77 | this._loadCustomCss(configuration); 78 | 79 | this._onReloadEmitter.fire(); 80 | } 81 | 82 | private _loadDefaultConfiguration(): Map { 83 | const extension = vscode.extensions.getExtension('Tobias-Faller.vt100-syntax-highlighting'); 84 | const packageJSON = extension?.packageJSON; 85 | const packageConfiguration = packageJSON?.contributes?.configuration; 86 | 87 | if (!Array.isArray(packageConfiguration)) { 88 | return new Map(); 89 | } 90 | 91 | const configuration: Map = new Map(); 92 | 93 | const defaultConfiguration = packageConfiguration[0]?.properties; 94 | for (const [key, value] of Object.entries(defaultConfiguration)) { 95 | if (value !== null && typeof value === 'object') { 96 | configuration.set(key, ( value)?.default); 97 | } 98 | } 99 | 100 | return configuration; 101 | } 102 | 103 | private _loadFontSettings(configuration: vscode.WorkspaceConfiguration) { 104 | const editorConfiguration = vscode.workspace.getConfiguration('editor'); 105 | 106 | const fontFamily = configuration.get('font-family') 107 | || editorConfiguration.get('fontFamily') 108 | || this._getFallbackFont('font-family'); 109 | 110 | const fontSize = configuration.get('font-size') 111 | || editorConfiguration.get('fontSize') 112 | || this._getFallbackFont('font-size'); 113 | 114 | const fontWeight = configuration.get('font-weight') 115 | || editorConfiguration.get('fontWeight') 116 | || this._getFallbackFont('font-weight'); 117 | 118 | this._fontSettings = { 119 | 'font-family': fontFamily, 120 | 'font-size': fontSize + 'px', 121 | 'font-weight': fontWeight 122 | }; 123 | } 124 | 125 | private _loadCustomCss(configuration: vscode.WorkspaceConfiguration) { 126 | this._customCss = configuration['custom-css'] || this._getFallbackStyle('custom-css', 'preview'); 127 | } 128 | 129 | private _loadStyle(name: string, configuration: vscode.WorkspaceConfiguration) { 130 | const settings = configuration[name]; 131 | 132 | let editorSettings: any; 133 | let previewSettings: any; 134 | 135 | if (settings == null || typeof settings !== 'object') { 136 | // Settings are invalid 137 | editorSettings = this._getFallbackStyle(name, 'editor'); 138 | previewSettings = this._getFallbackStyle(name, 'preview'); 139 | } else { 140 | const editorSettingsExist = 'editor' in settings; 141 | const previewSettingsExist = 'preview' in settings; 142 | 143 | if (!editorSettingsExist && !previewSettingsExist) { 144 | // Neither the editor nor the preview settings exists 145 | // Use the same style for both views 146 | editorSettings = settings; 147 | previewSettings = editorSettings; 148 | } else { 149 | if (editorSettingsExist && typeof settings['editor'] === 'object') { 150 | editorSettings = settings['editor']; 151 | } else { 152 | editorSettings = this._getFallbackStyle(name, 'editor'); 153 | } 154 | 155 | if (previewSettingsExist && typeof settings['preview'] === 'object') { 156 | previewSettings = settings['preview']; 157 | } else { 158 | previewSettings = this._getFallbackStyle(name, 'preview'); 159 | } 160 | } 161 | } 162 | 163 | editorSettings = this._applyNativeTheme(name, editorSettings, configuration, 'theme-color'); 164 | previewSettings = this._applyNativeTheme(name, previewSettings, configuration, 'resolved-color'); 165 | 166 | const editorStyle = this._convertCssToEditorRenderOptions(name, editorSettings); 167 | this._styles.set(name, new StyleConfiguration(editorStyle, previewSettings)); 168 | } 169 | 170 | private _convertCssToEditorRenderOptions(name: string, style: any): vscode.DecorationRenderOptions { 171 | const defaultStyle = this._getFallbackStyle(name, 'editor'); 172 | 173 | let darkStyle: any = null; 174 | let lightStyle: any = null; 175 | 176 | if ('dark' in style && typeof style['dark'] === 'object') { 177 | darkStyle = this._convertObjectToRenderOptions(style['dark']); 178 | } else if (typeof style === 'object') { 179 | darkStyle = this._convertObjectToRenderOptions(style); 180 | } else if ('dark' in defaultStyle && typeof style['dark'] === 'object') { 181 | darkStyle = this._convertObjectToRenderOptions(defaultStyle['dark']); 182 | } else { 183 | darkStyle = this._convertObjectToRenderOptions(defaultStyle); 184 | } 185 | 186 | if ('light' in style && typeof style['light'] === 'object') { 187 | lightStyle = this._convertObjectToRenderOptions(style['light']); 188 | } else if (typeof style === 'object') { 189 | lightStyle = this._convertObjectToRenderOptions(style); 190 | } else if ('light' in defaultStyle && typeof style['light'] === 'object') { 191 | lightStyle = this._convertObjectToRenderOptions(defaultStyle['light']); 192 | } else { 193 | lightStyle = this._convertObjectToRenderOptions(defaultStyle); 194 | } 195 | 196 | return { 197 | "dark": darkStyle, 198 | "light": lightStyle 199 | }; 200 | } 201 | 202 | private _applyNativeTheme(name: string, style: any, configuration: vscode.WorkspaceConfiguration, type: string): any { 203 | const nativeColor = this._applyNativeColor(name, style, configuration, type); 204 | if (nativeColor) { 205 | style = {...style, ...nativeColor}; 206 | } 207 | 208 | if ('dark' in style && typeof style['dark'] === 'object') { 209 | const nativeDarkColor = this._applyNativeColor(name, style['dark'], configuration, type); 210 | if (nativeDarkColor) { 211 | style = {...style, ...{ 212 | 'dark': nativeDarkColor 213 | }}; 214 | } 215 | } 216 | if ('light' in style && typeof style['light'] === 'object') { 217 | const nativeLightColor = this._applyNativeColor(name, style['light'], configuration, type); 218 | if (nativeLightColor) { 219 | style = {...style, ...{ 220 | 'light': nativeLightColor 221 | }}; 222 | } 223 | } 224 | 225 | return style; 226 | } 227 | 228 | private _applyNativeColor(name: string, style: any, configuration: vscode.WorkspaceConfiguration, type: string): object | null { 229 | const key = name.startsWith('foreground') ? 'color' 230 | : name.startsWith('background') ? 'background-color' : null; 231 | if (!key) 232 | { 233 | // Do not change configuration 234 | return null; 235 | } 236 | 237 | if (configuration.get('use-native-theme', false) 238 | || (key in style && style[key] === 'native')) { 239 | // Override the color configuration on enabled native theme 240 | const nativeColor = this._getNativeColor(name); 241 | if (nativeColor) 242 | { 243 | switch (type) 244 | { 245 | case 'theme-color': 246 | return { [key]: new vscode.ThemeColor(nativeColor) }; 247 | case 'resolved-color': 248 | return { 249 | [key]: `var(--vscode-${nativeColor.replace('.', '-')})`, 250 | [key + '-fallback']: (key in style) ? style[key] : null 251 | }; 252 | } 253 | } 254 | } 255 | 256 | // Do not change configuration 257 | return null; 258 | } 259 | 260 | private _getNativeColor(name: string): string | undefined { 261 | switch (name) 262 | { 263 | case 'foreground-color-default': 264 | return 'editor.foreground'; 265 | case 'foreground-color-inverted': 266 | return 'editor.background'; 267 | case 'foreground-color-black': 268 | return 'terminal.ansiBlack'; 269 | case 'foreground-color-red': 270 | return 'terminal.ansiRed'; 271 | case 'foreground-color-green': 272 | return 'terminal.ansiGreen'; 273 | case 'foreground-color-yellow': 274 | return 'terminal.ansiYellow'; 275 | case 'foreground-color-blue': 276 | return 'terminal.ansiBlue'; 277 | case 'foreground-color-magenta': 278 | return 'terminal.ansiMagenta'; 279 | case 'foreground-color-cyan': 280 | return 'terminal.ansiCyan'; 281 | case 'foreground-color-light-gray': 282 | return 'terminal.ansiWhite'; 283 | case 'foreground-color-dark-gray': 284 | return 'terminal.ansiBrightBlack'; 285 | case 'foreground-color-light-red': 286 | return 'terminal.ansiBrightRed'; 287 | case 'foreground-color-light-green': 288 | return 'terminal.ansiBrightGreen'; 289 | case 'foreground-color-light-yellow': 290 | return 'terminal.ansiBrightYellow'; 291 | case 'foreground-color-light-blue': 292 | return 'terminal.ansiBrightBlue'; 293 | case 'foreground-color-light-magenta': 294 | return 'terminal.ansiBrightMagenta'; 295 | case 'foreground-color-light-cyan': 296 | return 'terminal.ansiBrightCyan'; 297 | case 'foreground-color-white': 298 | return 'terminal.ansiBrightWhite'; 299 | case 'background-color-default': 300 | return 'editor.background'; 301 | case 'background-color-inverted': 302 | return 'editor.foreground'; 303 | case 'background-color-black': 304 | return 'terminal.ansiBlack'; 305 | case 'background-color-red': 306 | return 'terminal.ansiRed'; 307 | case 'background-color-green': 308 | return 'terminal.ansiGreen'; 309 | case 'background-color-yellow': 310 | return 'terminal.ansiYellow'; 311 | case 'background-color-blue': 312 | return 'terminal.ansiBlue'; 313 | case 'background-color-magenta': 314 | return 'terminal.ansiMagenta'; 315 | case 'background-color-cyan': 316 | return 'terminal.ansiCyan'; 317 | case 'background-color-light-gray': 318 | return 'terminal.ansiWhite'; 319 | case 'background-color-dark-gray': 320 | return 'terminal.ansiBrightBlack'; 321 | case 'background-color-light-red': 322 | return 'terminal.ansiBrightRed'; 323 | case 'background-color-light-green': 324 | return 'terminal.ansiBrightGreen'; 325 | case 'background-color-light-yellow': 326 | return 'terminal.ansiBrightYellow'; 327 | case 'background-color-light-blue': 328 | return 'terminal.ansiBrightBlue'; 329 | case 'background-color-light-magenta': 330 | return 'terminal.ansiBrightMagenta'; 331 | case 'background-color-light-cyan': 332 | return 'terminal.ansiBrightCyan'; 333 | case 'background-color-white': 334 | return 'terminal.ansiBrightWhite'; 335 | } 336 | 337 | return undefined; 338 | } 339 | 340 | private _convertObjectToRenderOptions(object: any): any { 341 | const properties: [string, string][] = []; 342 | 343 | for (const [key, value] of Object.entries(object)) { 344 | if (value != null) { 345 | properties.push([this._convertCssToRenderOptionKey(key), value]); 346 | } 347 | } 348 | 349 | return Object.fromEntries(properties); 350 | } 351 | 352 | private _convertCssToRenderOptionKey(key: string): string { 353 | return key.replace(/-[A-Za-z]/g, (letter) => letter.substring(1).toUpperCase()); 354 | } 355 | 356 | private _getFallbackStyle(style: string, type: string): any { 357 | const configuration = this._defaultConfiguration.get('vt100.' + style) || { }; 358 | 359 | const editorSettingsExist = 'editor' in configuration; 360 | const previewSettingsExist = 'preview' in configuration; 361 | 362 | if (editorSettingsExist && type === 'editor') { 363 | return configuration['editor']; 364 | } 365 | 366 | if (previewSettingsExist && type === 'preview') { 367 | return configuration['preview']; 368 | } 369 | 370 | return configuration; 371 | } 372 | 373 | private _getFallbackFont(property: string): any { 374 | switch(property) { 375 | case'font-family': 376 | return '\'Lucida Console\', monospace'; 377 | case 'font-size': 378 | return 15; 379 | case 'font-weight': 380 | return 'normal'; 381 | 382 | default: 383 | return ''; 384 | } 385 | } 386 | 387 | } -------------------------------------------------------------------------------- /src/content/htmlContentProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { randomBytes } from 'crypto'; 4 | 5 | import { ConfigurationManager } from '../configurationManager'; 6 | import { VT100Parser } from '../vt100Parser'; 7 | 8 | export class HTMLContentProvider implements vscode.Disposable { 9 | 10 | static generateKeyMappings(): Map { 11 | const map: Map = new Map(); 12 | 13 | map.set('foreground', 'fg'); 14 | map.set('background', 'bg'); 15 | map.set('text', 'te'); 16 | map.set('escape-sequence', 'es'); 17 | 18 | for (const [color, abbreviation] of [ 19 | [ 'default', 'de' ], 20 | [ 'inverted', 'in' ], 21 | [ 'black', 'bl' ], 22 | [ 'red', 're' ], 23 | [ 'green', 'gr' ], 24 | [ 'yellow', 'yl' ], 25 | [ 'blue', 'blu' ], 26 | [ 'magenta', 'mg' ], 27 | [ 'cyan', 'cy' ], 28 | [ 'light-gray', 'lg' ], 29 | [ 'dark-gray', 'dg' ], 30 | [ 'light-red', 'lr' ], 31 | [ 'light-green', 'lgr' ], 32 | [ 'light-yellow', 'ly' ], 33 | [ 'light-blue', 'lb' ], 34 | [ 'light-magenta', 'lm' ], 35 | [ 'light-cyan', 'lc' ], 36 | [ 'white', 'wh' ] 37 | ]) { 38 | map.set('foreground-color-' + color, 'fg-' + abbreviation); 39 | map.set('background-color-' + color, 'bg-' + abbreviation); 40 | } 41 | 42 | for (const [attribute, abbreviation] of [ 43 | [ 'bold', 'bo' ], 44 | [ 'dim', 'di' ], 45 | [ 'underlined', 'ul' ], 46 | [ 'blink', 'bl' ], 47 | [ 'inverted', 'in' ], 48 | [ 'hidden', 'hi' ] 49 | ]) { 50 | map.set('attribute-' + attribute, 'at-' + abbreviation); 51 | } 52 | 53 | return map; 54 | } 55 | 56 | private _disposables: vscode.Disposable[] = []; 57 | private _configuration: ConfigurationManager; 58 | 59 | private _styles: any; 60 | private _styleKeyMappings: Map; 61 | private _customCss: any; 62 | private _fontSettings: any; 63 | 64 | constructor(configuration: ConfigurationManager) { 65 | this._configuration = configuration; 66 | this._configuration.onReload(() => { 67 | this.reloadConfiguration(); 68 | }, null, this._disposables); 69 | 70 | this._customCss = { }; 71 | this._styles = new Map(); 72 | this._styleKeyMappings = HTMLContentProvider.generateKeyMappings(); 73 | 74 | this.reloadConfiguration(); 75 | } 76 | 77 | public dispose(): void { 78 | for (const disposable of this._disposables) { 79 | disposable.dispose(); 80 | } 81 | 82 | this._disposables = []; 83 | } 84 | 85 | public reloadConfiguration(): void { 86 | this._customCss = this._configuration.getCustomCss(); 87 | 88 | // Convert font settings to CSS class properties 89 | this._fontSettings = { 'body': this._configuration.getFontSettings() }; 90 | 91 | // Convert styles to CSS class properties 92 | this._styles = this._loadStyles(); 93 | } 94 | 95 | private _loadStyles(): any { 96 | const styles: [string, any][] = []; 97 | 98 | for (const [key, value] of this._configuration.getSettings()) { 99 | const previewSettings = value.previewStyle; 100 | 101 | const darkSettingsExist = 'dark' in previewSettings; 102 | const lightSettingsExist = 'light' in previewSettings; 103 | const highContrastSettingsExist = 'high-contrast' in previewSettings; 104 | 105 | const shortKey = this._getShortKey(key); 106 | 107 | if (!darkSettingsExist && !lightSettingsExist && !highContrastSettingsExist) { 108 | // Neither the dark, the light nor the high-contrast settings exists 109 | // Use the same style for all modes 110 | styles.push([`.${shortKey}`, previewSettings]); 111 | } else { 112 | // Use separate style configurations 113 | if (darkSettingsExist && typeof previewSettings['dark'] === 'object') { 114 | styles.push([`.vscode-dark .${shortKey}`, previewSettings['dark']]); 115 | } else { 116 | styles.push([`.vscode-dark .${shortKey}`, {}]); 117 | } 118 | 119 | if (lightSettingsExist && typeof previewSettings['light'] === 'object') { 120 | styles.push([`.vscode-light .${shortKey}`, previewSettings['light']]); 121 | } else { 122 | styles.push([`.vscode-light .${shortKey}`, {}]); 123 | } 124 | 125 | if (highContrastSettingsExist && typeof previewSettings['high-contrast'] === 'object') { 126 | styles.push([`.vscode-high-contrast .${shortKey}`, previewSettings['high-contrast']]); 127 | } else { 128 | styles.push([`.vscode-high-contrast .${shortKey}`, {}]); 129 | } 130 | } 131 | } 132 | 133 | return Object.fromEntries(styles); 134 | } 135 | 136 | private _getShortKey(key: string): string { 137 | return this._styleKeyMappings.get(key)!; 138 | } 139 | 140 | /** 141 | * Converts the source text to rendered HTML code. 142 | * The state parameter contains the editor state when generating a preview 143 | * for the Webview in VS Code. 144 | * The state parameter is undefined, when exporting HTML to a file. 145 | * 146 | * @param document The document to render 147 | * @param callback A callback to receive the generated data 148 | * @param state The state information, when in preview mode 149 | */ 150 | public async provideTextDocumentContent(document: vscode.TextDocument, options: Map, callback: (data: string) => Promise, cancel: vscode.CancellationToken | null): Promise { 151 | if (cancel?.isCancellationRequested) { 152 | return; 153 | } 154 | 155 | const cssNonce = this._generateNonce(); 156 | const jsNonce = this._generateNonce(); 157 | const inEditor = options.get('is-editor') || false; 158 | 159 | // Try to add at least a little bit of security with Content-Security-Policy so that 160 | // the rendered file can not include arbitrary CSS code 161 | // JavaScript is disabled by CSP and the WebView settings 162 | let header = ''; 163 | header += ''; 164 | header += ``; 165 | header += ''; 166 | header += `${this._getFilename(document.uri)}`; 167 | if (inEditor) { 168 | // Register event handler that is invoked when the extension sends a command to the preview panel 169 | header += ``; 182 | } 183 | header += ``; 192 | header += ``; 193 | header += ``; 194 | header += ``; 195 | header += ''; 196 | 197 | if (inEditor) { 198 | header += ''; 199 | } else { 200 | // TODO: Maybe add export option for different themes 201 | header += ''; 202 | header += ``; 203 | } 204 | await callback(header); 205 | 206 | if (cancel?.isCancellationRequested) { 207 | return; 208 | } 209 | 210 | await VT100Parser.parse(document, async (range, context) => { 211 | if (cancel?.isCancellationRequested) { 212 | return; 213 | } 214 | 215 | // Just ignore escape sequences and don't render them 216 | if (context.get('type') === 'escape-sequence') { 217 | return; 218 | } 219 | 220 | const text = document.getText(range); 221 | if (text.length > 0) 222 | { 223 | const [foregroundColor, backgroundColor] = this._getColors(context); 224 | 225 | const foregroundClasses: string[] = [ ]; 226 | foregroundClasses.push(this._getShortKey('foreground')); 227 | foregroundClasses.push(this._getShortKey(context.get('type')!)); 228 | foregroundClasses.push(this._getShortKey(`foreground-color-${foregroundColor}`)); 229 | for (const attribute of ['bold', 'dim', 'underlined', 'blink', 'inverted', 'hidden']) { 230 | if (context.get(attribute) === 'yes') { 231 | foregroundClasses.push(this._getShortKey('attribute-' + attribute)); 232 | } 233 | } 234 | 235 | const backgroundClasses: string[] = [ ]; 236 | backgroundClasses.push(this._getShortKey('background')); 237 | backgroundClasses.push(this._getShortKey(`background-color-${backgroundColor}`)); 238 | 239 | let line = ``; 240 | line += ``; 241 | line += this._escapeHtml(this._stripEscapeCodes(text)); 242 | line += ''; 243 | await callback(line); 244 | } 245 | 246 | if (context.get('line-end') == 'yes') { 247 | await callback('
\n'); 248 | } 249 | }, cancel); 250 | 251 | if (cancel?.isCancellationRequested) { 252 | return; 253 | } 254 | 255 | let footer = ''; 256 | if (!inEditor) { 257 | footer += '
'; 258 | } 259 | footer += ''; 260 | footer += ''; 261 | await callback(footer); 262 | } 263 | 264 | private _generateNonce(): string { 265 | const buffer: Buffer = randomBytes(64); 266 | return buffer.toString('base64'); 267 | } 268 | 269 | private _getColors(context: Map): [string, string] { 270 | let foregroundColor: string; 271 | let backgroundColor: string; 272 | 273 | if (context.get('inverted') === 'yes') { 274 | foregroundColor = context.get('background-color')!; 275 | backgroundColor = context.get('foreground-color')!; 276 | 277 | if (foregroundColor === 'default') { 278 | foregroundColor = 'inverted'; 279 | } 280 | if (backgroundColor === 'default') { 281 | backgroundColor = 'inverted'; 282 | } 283 | } else { 284 | foregroundColor = context.get('foreground-color')!; 285 | backgroundColor = context.get('background-color')!; 286 | } 287 | 288 | return [foregroundColor, backgroundColor]; 289 | } 290 | 291 | private _generateCss(properties: any, inEditor: boolean): string { 292 | let css = ''; 293 | 294 | for (const [key, value] of Object.entries(properties)) { 295 | // Ignore fallback settings 296 | if (key.endsWith('-fallback') || value == null || value == undefined) { 297 | continue; 298 | } 299 | 300 | if (typeof value === 'object') { 301 | css += `${key} {\n`; 302 | css += this._generateCss(value, inEditor); 303 | css += `}\n`; 304 | } else if (typeof value === 'string') { 305 | let cssValue = value; 306 | if (!inEditor && value.startsWith('var(--vscode-')) 307 | { 308 | // Use the fallback color values when not rendering for the editor. 309 | // This is currently necessary as the raw color value 310 | // from the theme can not be extracted with an official API. 311 | css += `/* VS Code Theme color option ${value} currently not supported. */\n`; 312 | if (key + '-fallback' in properties && properties[key + '-fallback']) { 313 | css += '/* Using alternative value instead. */\n'; 314 | cssValue = properties[key + '-fallback']; // Overwrite value with fallback setting 315 | } else { 316 | continue; 317 | } 318 | } 319 | css += `${key}: ${cssValue};\n`; 320 | } 321 | } 322 | 323 | return css; 324 | } 325 | 326 | private _escapeHtml(value: string): string { 327 | return value 328 | .replace(/&/g, "&") 329 | .replace(//g, ">") 331 | .replace(/"/g, """) 332 | .replace(/'/g, "'") 333 | .replace(/ /g, ' '); 334 | } 335 | 336 | private _getFilename(uri: vscode.Uri): string { 337 | const path = uri.path; 338 | const separatorIndex = path.lastIndexOf('/'); 339 | 340 | if (separatorIndex !== -1) { 341 | return path.substring(separatorIndex + 1); 342 | } 343 | 344 | return path; 345 | } 346 | 347 | private _stripEscapeCodes(text: string): string { 348 | // See http://ascii-table.com/ansi-escape-sequences-vt-100.php 349 | // And https://invisible-island.net/xterm/ctlseqs/ctlseqs.html 350 | // eslint-disable-next-line no-control-regex 351 | return text.replace(/\x1B\[([0-9?]*[hl]|[0-9;]*[mrHfy]|[0-9]*[ABCDgKJnqi]|[0-9;?]*[c])/g, '') 352 | // eslint-disable-next-line no-control-regex 353 | .replace(/\x1B([NODMEHc<=>FGABCDHIKJ]|[()][AB012]|#[0-9]|[0-9;]R|\/?Z|[0-9]+|O[PQRSABCDpqrstuvwxymlnM])/g, ''); 354 | } 355 | 356 | } -------------------------------------------------------------------------------- /src/content/textContentProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { ConfigurationManager } from '../configurationManager'; 4 | 5 | export class TextContentProvider implements vscode.Disposable { 6 | 7 | constructor(_configuration: ConfigurationManager) { 8 | // Do nothing 9 | } 10 | 11 | public dispose(): void { 12 | // Do nothing 13 | } 14 | 15 | public async provideTextDocumentContent(document: vscode.TextDocument, callback: (data: string) => Promise, cancel: vscode.CancellationToken | null): Promise { 16 | for (let lineNumber = 0; lineNumber < document.lineCount; lineNumber++) { 17 | if (cancel?.isCancellationRequested) { 18 | return; 19 | } 20 | 21 | const text = document.lineAt(lineNumber).text; 22 | 23 | // See http://ascii-table.com/ansi-escape-sequences-vt-100.php 24 | // And https://invisible-island.net/xterm/ctlseqs/ctlseqs.html 25 | // eslint-disable-next-line no-control-regex 26 | const stripped = text.replace(/\x1B\[([0-9?]*[hl]|[0-9;]*[mrHfy]|[0-9]*[ABCDgKJnqi]|[0-9;?]*[c])/g, '') 27 | // eslint-disable-next-line no-control-regex 28 | .replace(/\x1B([NODMEHc<=>FGABCDHIKJ]|[()][AB012]|#[0-9]|[0-9;]R|\/?Z|[0-9]+|O[PQRSABCDpqrstuvwxymlnM])/g, ''); 29 | 30 | await callback(`${stripped}\n`); 31 | } 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/decorationManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { debounce } from './util'; 4 | 5 | import { ConfigurationManager } from './configurationManager'; 6 | import { VT100Parser } from './vt100Parser'; 7 | 8 | export class DecorationManager implements vscode.Disposable { 9 | 10 | private _configuration: ConfigurationManager; 11 | private _disposables: vscode.Disposable[] = []; 12 | private _decorations: Map; 13 | private _decorator: EditorDecorator; 14 | 15 | constructor(configuration: ConfigurationManager) { 16 | this._configuration = configuration; 17 | this._decorations = new Map(); 18 | this._decorator = new EditorDecorator(this._decorations); 19 | 20 | this._registerDecorations(); 21 | 22 | this._configuration.onReload(() => { 23 | this._reloadDecorations(); 24 | this._updateDecorations(vscode.window.visibleTextEditors); 25 | }, null, this._disposables); 26 | 27 | vscode.window.onDidChangeVisibleTextEditors(async (editors) => { 28 | this._updateDecorations(editors); 29 | }, null, this._disposables); 30 | 31 | // Debounce since there might be a lot of small changes during writing 32 | const lazyUpdate = debounce(async (editors) => this._applyDecorations(editors), 500); 33 | vscode.workspace.onDidChangeTextDocument(async (event) => { 34 | const editors = vscode.window.visibleTextEditors.filter(editor => editor.document == event.document); 35 | await lazyUpdate(editors); 36 | }, null, this._disposables); 37 | 38 | vscode.workspace.onDidOpenTextDocument(async (document) => { 39 | const editors = vscode.window.visibleTextEditors.filter(editor => editor.document == document); 40 | await this._applyDecorations(editors); 41 | }, null, this._disposables); 42 | 43 | vscode.workspace.onDidCloseTextDocument(async (document) => { 44 | const editors = vscode.window.visibleTextEditors.filter(editor => editor.document == document); 45 | await this._removeDecorations(editors); 46 | }, null, this._disposables); 47 | 48 | this._applyDecorations(vscode.window.visibleTextEditors); 49 | } 50 | 51 | public dispose(): void { 52 | this._disposables.forEach((disposable) => disposable.dispose()); 53 | this._disposables = []; 54 | this._decorations.forEach((value, _key, _map) => value.dispose()); 55 | this._decorations.clear(); 56 | } 57 | 58 | private _shouldDecorate(language: string): boolean { 59 | if (language == 'vt100') { 60 | return true; 61 | } 62 | 63 | const configuration = vscode.workspace.getConfiguration('vt100'); 64 | const includes: string | null = configuration['decorate-includes']; 65 | const excludes: string | null = configuration['decorate-excludes']; 66 | 67 | return (includes == null || (language.match(includes) != null)) 68 | && (excludes == null || (language.match(excludes) == null)); 69 | } 70 | 71 | private async _applyDecorations(editors: readonly vscode.TextEditor[]): Promise { 72 | const promises: Promise[] = []; 73 | 74 | for (const editor of editors) { 75 | if (editor != null && this._shouldDecorate(editor.document.languageId)) { 76 | promises.push(this._decorator.apply(editor)); 77 | } 78 | } 79 | 80 | return Promise.all(promises); 81 | } 82 | 83 | private async _removeDecorations(editors: readonly vscode.TextEditor[]): Promise { 84 | const promises: Promise[] = []; 85 | 86 | for (const editor of editors) { 87 | if (editor == null) { 88 | continue; 89 | } 90 | 91 | promises.push(this._decorator.remove(editor)); 92 | } 93 | 94 | return Promise.all(promises); 95 | } 96 | 97 | private async _updateDecorations(editors: readonly vscode.TextEditor[]): Promise { 98 | const promises: Promise[] = []; 99 | 100 | for (const editor of editors) { 101 | if (editor == null) { 102 | continue; 103 | } 104 | 105 | if (this._shouldDecorate(editor.document.languageId)) { 106 | promises.push(this._decorator.apply(editor)); 107 | } else { 108 | promises.push(this._decorator.remove(editor)); 109 | } 110 | } 111 | 112 | return Promise.all(promises); 113 | } 114 | 115 | private _registerDecorations() { 116 | for (const [key, value] of this._configuration.getSettings()) { 117 | const decoration = vscode.window.createTextEditorDecorationType(value.editorStyle); 118 | this._decorations.set(key, decoration); 119 | } 120 | } 121 | 122 | private _reloadDecorations() { 123 | for (const [_key, value] of this._decorations) { 124 | value.dispose(); 125 | } 126 | this._decorations.clear(); 127 | 128 | this._registerDecorations(); 129 | } 130 | 131 | } 132 | 133 | class EditorDecorator { 134 | 135 | private _decorations: Map; 136 | 137 | constructor(decorations: Map) { 138 | this._decorations = decorations; 139 | } 140 | 141 | public async apply(editor: vscode.TextEditor): Promise { 142 | const appliedDecorations: Map = new Map(); 143 | for (const decorationName of this._decorations.keys()) { 144 | appliedDecorations.set(decorationName, []); 145 | } 146 | 147 | const progressView = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); 148 | progressView.text = 'Parsing file'; 149 | progressView.show(); 150 | 151 | // The callback only calls synchronous methods. A cancellation token 152 | // will not do anything, as the method never awaits and therefore 153 | // never enables the extension host to fire an event cancelling the process. 154 | // Note: Correct me if I'm wrong here and it makes sense to have a cancellation token. 155 | await VT100Parser.parse(editor.document, async (range, context) => { 156 | this._applyDecoration(range, context, appliedDecorations); 157 | }, null); 158 | 159 | progressView.hide(); 160 | progressView.dispose(); 161 | 162 | for (const [key, value] of appliedDecorations) { 163 | const isDefaultColour = (key == 'background-color-default') 164 | || (key == 'foreground-color-default'); 165 | if (editor.document.languageId != 'vt100' && isDefaultColour) { 166 | continue; 167 | } 168 | 169 | editor.setDecorations(this._decorations.get(key)!, value); 170 | } 171 | } 172 | 173 | public async remove(editor: vscode.TextEditor): Promise { 174 | // Undecorate editor if decorated 175 | for (const decorations of this._decorations.values()) { 176 | editor.setDecorations(decorations, []); 177 | } 178 | } 179 | 180 | private _applyDecoration(range: vscode.Range, context: Map, appliedDecorations: Map) { 181 | let foregroundColor: string; 182 | let backgroundColor: string; 183 | 184 | if (context.get('inverted') === 'yes') { 185 | foregroundColor = context.get('background-color'); 186 | backgroundColor = context.get('foreground-color'); 187 | 188 | if (foregroundColor === 'default') { 189 | foregroundColor = 'inverted'; 190 | } 191 | if (backgroundColor === 'default') { 192 | backgroundColor = 'inverted'; 193 | } 194 | } else { 195 | foregroundColor = context.get('foreground-color'); 196 | backgroundColor = context.get('background-color'); 197 | } 198 | 199 | appliedDecorations.get('background-color-' + backgroundColor)!.push(range); 200 | appliedDecorations.get('foreground-color-' + foregroundColor)!.push(range); 201 | 202 | for (const attribute of ['bold', 'dim', 'underlined', 'blink', 'inverted', 'hidden']) { 203 | if (context.get(attribute) === 'yes') { 204 | appliedDecorations.get('attribute-' + attribute)!.push(range); 205 | } 206 | } 207 | 208 | switch (context.get('type')) { 209 | case 'escape-sequence': 210 | appliedDecorations.get('escape-sequence')!.push(range); 211 | break; 212 | case 'text': 213 | appliedDecorations.get('text')!.push(range); 214 | break; 215 | default: 216 | break; 217 | } 218 | } 219 | 220 | } 221 | -------------------------------------------------------------------------------- /src/exportManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { promises as fs } from 'fs'; 3 | 4 | import { ConfigurationManager } from './configurationManager'; 5 | import { HTMLContentProvider } from './content/htmlContentProvider'; 6 | import { TextContentProvider } from './content/textContentProvider'; 7 | 8 | export class ExportManager implements vscode.Disposable { 9 | 10 | private _htmlContentProvider: HTMLContentProvider; 11 | private _textContentProvider: TextContentProvider; 12 | private _statusBarItem: vscode.StatusBarItem; 13 | private _cancelSource: vscode.CancellationTokenSource | null; 14 | 15 | constructor(configuration: ConfigurationManager) { 16 | this._htmlContentProvider = new HTMLContentProvider(configuration); 17 | this._textContentProvider = new TextContentProvider(configuration); 18 | this._cancelSource = null; 19 | 20 | this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 21 | this._statusBarItem.command = 'vt100.cancelExport'; 22 | } 23 | 24 | public dispose(): void { 25 | this.cancelExport(); 26 | this._htmlContentProvider.dispose(); 27 | this._textContentProvider.dispose(); 28 | this._statusBarItem.dispose(); 29 | } 30 | 31 | public async exportText(param: vscode.Uri, params: vscode.Uri[]): Promise { 32 | this.cancelExport(); 33 | this._cancelSource = new vscode.CancellationTokenSource(); 34 | const cancelToken = this._cancelSource?.token; 35 | 36 | const contentProvider: ContentProvider = (document, callback) => 37 | this._textContentProvider.provideTextDocumentContent(document, callback, cancelToken); 38 | 39 | const editor = vscode.window.activeTextEditor; 40 | if (Array.isArray(params)) { 41 | await this._exportAllUris(params, { type: "text", extension: "txt" }, contentProvider, cancelToken); 42 | } else if (param && (param instanceof vscode.Uri)) { 43 | await this._exportAllUris([ param ], { type: "text", extension: "txt" }, contentProvider, cancelToken); 44 | } else if (editor != null && editor.document.languageId === 'vt100') { 45 | await this._exportDocument(editor.document, { type: "text", extension: "txt" }, contentProvider, cancelToken); 46 | } 47 | } 48 | 49 | public async exportHtml(param: vscode.Uri, params: vscode.Uri[]): Promise { 50 | this.cancelExport(); 51 | this._cancelSource = new vscode.CancellationTokenSource(); 52 | const cancelToken = this._cancelSource?.token; 53 | 54 | const options: Map = new Map([ 55 | ['is-editor', false] 56 | ]); 57 | const contentProvider: ContentProvider = (document, callback) => 58 | this._htmlContentProvider.provideTextDocumentContent(document, options, callback, cancelToken); 59 | 60 | const editor = vscode.window.activeTextEditor; 61 | if (Array.isArray(params)) { 62 | await this._exportAllUris(params, { type: "HTML", extension: "html" }, contentProvider, cancelToken); 63 | } else if (param && (param instanceof vscode.Uri)) { 64 | await this._exportAllUris([ param ], { type: "HTML", extension: "html" }, contentProvider, cancelToken); 65 | } else if (editor != null && editor.document.languageId === 'vt100') { 66 | await this._exportDocument(editor?.document, { type: "HTML", extension: "html" }, contentProvider, cancelToken); 67 | } 68 | } 69 | 70 | public async cancelExport(): Promise { 71 | if (this._cancelSource != null) { 72 | this._cancelSource.cancel(); 73 | this._cancelSource.dispose(); 74 | this._cancelSource = null; 75 | } 76 | } 77 | 78 | private async _exportAllUris(uris: vscode.Uri[], options: ExportOptions, contentProvider: ContentProvider, cancelToken: vscode.CancellationToken | null): Promise { 79 | for (const uri of uris) { 80 | if (cancelToken?.isCancellationRequested) { 81 | return; 82 | } 83 | 84 | await this._exportDocument(await vscode.workspace.openTextDocument(uri), options, contentProvider, cancelToken); 85 | } 86 | } 87 | 88 | private async _exportDocument(document: vscode.TextDocument, options: ExportOptions, contentProvider: ContentProvider, cancelToken: vscode.CancellationToken | null): Promise { 89 | if (cancelToken?.isCancellationRequested) { 90 | return; 91 | } 92 | 93 | const path = document.uri.fsPath + '.' + options.extension; 94 | const name = this._getFilename(path); 95 | 96 | let file: fs.FileHandle | null = null; 97 | try { 98 | file = await fs.open(path, 'w'); 99 | if (file != null) 100 | { 101 | this._statusBarItem.text = `Exporting document to ${options.type} file ${name} ...`; 102 | this._statusBarItem.show(); 103 | 104 | const fileHandle = file; 105 | let size = 0; 106 | await contentProvider(document, async (data: string) => { 107 | if (cancelToken?.isCancellationRequested) { 108 | return; 109 | } 110 | 111 | const buffer = Buffer.from(data, 'utf8'); 112 | await fileHandle.write(buffer); 113 | 114 | size += buffer.byteLength; 115 | this._statusBarItem.text = `Exporting document to ${options.type} file ${name} (${this._getSize(size)}) ...`; 116 | }); 117 | 118 | this._statusBarItem.hide(); 119 | await vscode.window.showInformationMessage(`Exported document to ${options.type} file ${name} (${this._getSize(size)})`); 120 | } 121 | } catch (error) { 122 | console.error(`Could not export document to ${options.type} file ${name}.`); 123 | console.error(error); 124 | 125 | this._statusBarItem.hide(); 126 | await vscode.window.showErrorMessage(`Could not export document to ${options.type} file ${name}`); 127 | } finally { 128 | if (file != null) 129 | { 130 | await file.close(); 131 | } 132 | } 133 | } 134 | 135 | private _getFilename(path: string) { 136 | const separatorIndex = path.lastIndexOf('/'); 137 | 138 | if (separatorIndex !== -1) { 139 | return path.substring(separatorIndex + 1); 140 | } 141 | 142 | return path; 143 | } 144 | 145 | private _getSize(size: number): string { 146 | const digits = 1; 147 | const base = 1024; 148 | const sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; 149 | const unit = (size === 0) ? 0 150 | : Math.min(Math.floor(Math.log(size) / Math.log(base)), sizes.length - 1); 151 | 152 | return `${(size / Math.pow(base, unit)).toFixed(digits)} ${sizes[unit]}`; 153 | } 154 | 155 | } 156 | 157 | type ContentProvider = (document: vscode.TextDocument, callback: (data: string) => Promise) => Promise; 158 | 159 | interface ExportOptions { 160 | 161 | readonly type: string; 162 | readonly extension: string; 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { ConfigurationManager } from './configurationManager'; 4 | import { DecorationManager } from './decorationManager'; 5 | import { PreviewManager } from './previewManager'; 6 | import { ExportManager } from './exportManager'; 7 | 8 | export function activate(context: vscode.ExtensionContext) { 9 | const configuration = new ConfigurationManager(); 10 | const decoratorManager = new DecorationManager(configuration); 11 | const previewManager = new PreviewManager(configuration); 12 | const exportManager = new ExportManager(configuration); 13 | 14 | context.subscriptions.push(configuration); 15 | context.subscriptions.push(decoratorManager); 16 | context.subscriptions.push(previewManager); 17 | context.subscriptions.push(exportManager); 18 | 19 | context.subscriptions.push(vscode.commands.registerCommand('vt100.showPreview', previewManager.showPreview, previewManager)); 20 | context.subscriptions.push(vscode.commands.registerCommand('vt100.showPreviewToSide', previewManager.showPreviewToSide, previewManager)); 21 | context.subscriptions.push(vscode.commands.registerCommand('vt100.toggleSynchronousScrolling', previewManager.toggleSynchronousScrolling, previewManager)); 22 | context.subscriptions.push(vscode.commands.registerCommand('vt100.exportText', exportManager.exportText, exportManager)); 23 | context.subscriptions.push(vscode.commands.registerCommand('vt100.exportHtml', exportManager.exportHtml, exportManager)); 24 | context.subscriptions.push(vscode.commands.registerCommand('vt100.cancelExport', exportManager.cancelExport, exportManager)); 25 | } 26 | 27 | export function deactivate(context: vscode.ExtensionContext) { 28 | context.subscriptions.forEach(element => { 29 | element.dispose(); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/previewManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { debounce } from './util'; 4 | 5 | import { ConfigurationManager } from './configurationManager'; 6 | import { HTMLContentProvider } from './content/htmlContentProvider'; 7 | 8 | export class PreviewManager implements vscode.Disposable, vscode.WebviewPanelSerializer, vscode.CustomTextEditorProvider { 9 | 10 | private _configuration: ConfigurationManager; 11 | private _contentProvider: HTMLContentProvider; 12 | 13 | private _previews: VT100Preview[] = []; 14 | private _disposables: vscode.Disposable[] = []; 15 | 16 | constructor(configuration: ConfigurationManager) { 17 | this._contentProvider = new HTMLContentProvider(configuration); 18 | this._configuration = configuration; 19 | this._configuration.onReload(() => { 20 | this._previews.forEach((preview) => preview.rerender()); 21 | }, null, this._disposables); 22 | 23 | this._disposables.push(vscode.window.registerCustomEditorProvider('vt100.preview', this)); 24 | this._disposables.push(vscode.window.registerWebviewPanelSerializer('vt100.preview', this)); 25 | 26 | // Only the side by side preview needs update. 27 | const lazyChangeEditor = debounce(async (uri, ranges) => { 28 | const previews = this._previews.filter(preview => preview.isIndexIn([''])); 29 | await Promise.all(previews.map((preview) => preview.update(uri))); 30 | 31 | if (vscode.workspace.getConfiguration('vt100').get('synchronous-scrolling', false)) { 32 | await Promise.all(previews.map((preview) => preview.updateRange(ranges))); 33 | } 34 | }, 250); 35 | vscode.window.onDidChangeActiveTextEditor(async (editor) => { 36 | if (editor?.document?.languageId === 'vt100') { 37 | await lazyChangeEditor(editor.document.uri, editor.visibleRanges); 38 | } 39 | }, null, this._disposables); 40 | 41 | // Debounce since there might be a lot of small changes during writing 42 | const lazyUpdateText = debounce(async (uri) => { 43 | const previews = this._previews.filter(preview => preview.isIndexIn(['', uri.toString()])); 44 | await Promise.all(previews.map((preview) => preview.update(uri))); 45 | }, 500); 46 | vscode.workspace.onDidChangeTextDocument(async (event) => { 47 | if (event.document?.languageId === 'vt100') { 48 | await lazyUpdateText(event.document.uri); 49 | } 50 | }, null, this._disposables); 51 | 52 | // Debounce as the scroll event might be repeated quite fast 53 | const lazyUpdateRange = debounce(async (uri, ranges) => { 54 | if (vscode.workspace.getConfiguration('vt100').get('synchronous-scrolling', false)) { 55 | const previews = this._previews.filter(preview => preview.isIndexIn(['', uri.toString()])); 56 | await Promise.all(previews.map((preview) => preview.updateRange(ranges))); 57 | } 58 | }, 250); 59 | vscode.window.onDidChangeTextEditorVisibleRanges(async (event) => { 60 | const editor = event.textEditor; 61 | if (editor.document?.languageId === 'vt100') { 62 | await lazyUpdateRange(editor.document.uri, editor.visibleRanges); 63 | } 64 | }, null, this._disposables); 65 | } 66 | 67 | public dispose(): void { 68 | this._disposables.forEach((disposable) => disposable.dispose()); 69 | this._disposables = []; 70 | this._previews.forEach((preview) => preview.dispose()); 71 | this._previews = []; 72 | } 73 | 74 | public async showPreview(param: vscode.Uri, params: vscode.Uri[]): Promise { 75 | await this._showPreview(param, params, false); 76 | } 77 | 78 | public async showPreviewToSide(param: vscode.Uri, params: vscode.Uri[]): Promise { 79 | await this._showPreview(param, params, true); 80 | } 81 | 82 | public async toggleSynchronousScrolling(): Promise { 83 | const configuration = vscode.workspace.getConfiguration('vt100'); 84 | await configuration.update('synchronous-scrolling', !configuration.get('synchronous-scrolling', false), vscode.ConfigurationTarget.Global); 85 | } 86 | 87 | public async deserializeWebviewPanel(panel: vscode.WebviewPanel, state: any): Promise { 88 | const uri = vscode.Uri.parse(state.uri); 89 | const index = state.index; 90 | const preview = this._createPreview(panel, uri, index); 91 | await preview.update(uri); 92 | } 93 | 94 | public async resolveCustomTextEditor(document: vscode.TextDocument, panel: vscode.WebviewPanel, token: vscode.CancellationToken): Promise { 95 | const uri = document.uri; 96 | const index = document.uri.toString(); 97 | const preview = this._createPreview(panel, uri, index); 98 | 99 | const cancel = token.onCancellationRequested((event) => preview.cancelCurrentUpdate()); 100 | try { 101 | await preview.update(uri); 102 | } finally { 103 | cancel.dispose(); 104 | } 105 | } 106 | 107 | private async _showPreview(param: vscode.Uri, params: vscode.Uri[], sideBySide: boolean): Promise { 108 | const promises: Promise[] = []; 109 | 110 | if (Array.isArray(params)) { 111 | params.forEach((uri) => promises.push(this._previewDocument(uri, sideBySide))); 112 | } else if (param && (param instanceof vscode.Uri)) { 113 | promises.push(this._previewDocument(param, sideBySide)); 114 | } else { 115 | const editor = vscode.window.activeTextEditor; 116 | if (editor?.document?.languageId == 'vt100') { 117 | promises.push(this._previewDocument(editor.document.uri, sideBySide)); 118 | } 119 | } 120 | 121 | await Promise.all(promises); 122 | } 123 | 124 | private async _previewDocument(uri: vscode.Uri, sideBySide: boolean): Promise { 125 | const previewColumn = sideBySide 126 | ? vscode.ViewColumn.Two 127 | : (vscode?.window?.activeTextEditor?.viewColumn || vscode.ViewColumn.One); 128 | const index = sideBySide ? '' : uri.toString(); 129 | 130 | let preview = this._previews.find(preview => preview.isIndexIn([index])); 131 | if (preview == null) { 132 | const panel = vscode.window.createWebviewPanel('vt100.preview', `Preview loading ...`, previewColumn); 133 | preview = this._createPreview(panel, uri, index); 134 | } 135 | preview.update(uri); 136 | } 137 | 138 | private _createPreview(panel: vscode.WebviewPanel, uri: vscode.Uri, index: string): VT100Preview { 139 | const preview = new VT100Preview(panel, this._contentProvider, uri, index); 140 | this._previews.push(preview); 141 | preview.onDispose(() => { 142 | const index = this._previews.indexOf(preview); 143 | if (index != -1) { 144 | this._previews.splice(index, 1); 145 | } 146 | }); 147 | return preview; 148 | } 149 | 150 | } 151 | 152 | class VT100Preview { 153 | 154 | private _contentProvider: HTMLContentProvider; 155 | private _panel: vscode.WebviewPanel; 156 | private _version?: { uri: vscode.Uri, version: number }; 157 | private _disposables: vscode.Disposable[] = []; 158 | private _index: any; 159 | private _uri: vscode.Uri; 160 | private _cancelSource: vscode.CancellationTokenSource | null; 161 | 162 | private _onDisposeEmitter = new vscode.EventEmitter(); 163 | public onDispose = this._onDisposeEmitter.event; 164 | 165 | constructor(panel: vscode.WebviewPanel, contentProvider: HTMLContentProvider, uri: vscode.Uri, index: any) { 166 | this._contentProvider = contentProvider; 167 | this._panel = panel; 168 | this._panel.webview.options = { 169 | enableScripts: true, 170 | enableCommandUris: false, 171 | localResourceRoots: [], 172 | portMapping: [] 173 | }; 174 | this._panel.onDidDispose(() => this.dispose(), null, this._disposables); 175 | this._index = index; 176 | this._uri = uri; 177 | this._cancelSource = null; 178 | } 179 | 180 | public dispose(): void { 181 | this.cancelCurrentUpdate(); 182 | this._onDisposeEmitter.fire(); 183 | this._onDisposeEmitter.dispose(); 184 | this._panel.dispose(); 185 | this._disposables.forEach((disposable) => disposable.dispose()); 186 | this._disposables = []; 187 | } 188 | 189 | public async rerender(): Promise { 190 | this.cancelCurrentUpdate(); 191 | this._cancelSource = new vscode.CancellationTokenSource(); 192 | const cancelToken = this._cancelSource.token; 193 | 194 | this._refresh(this._uri, true, cancelToken); 195 | } 196 | 197 | public async update(uri: vscode.Uri): Promise { 198 | this.cancelCurrentUpdate(); 199 | this._cancelSource = new vscode.CancellationTokenSource(); 200 | const cancelToken = this._cancelSource.token; 201 | 202 | this._uri = uri; 203 | this._refresh(uri, false, cancelToken); 204 | } 205 | 206 | public async updateRange(ranges: readonly vscode.Range[]): Promise { 207 | if (ranges.length > 0) { 208 | const line = ranges[0].start.line; 209 | await this._panel.webview.postMessage({ 210 | 'command': 'scroll-to', 211 | 'line': line 212 | }); 213 | } 214 | } 215 | 216 | public isIndexIn(indices: any[]): boolean { 217 | return indices.findIndex((value, _index, _obj) => (value == this._index)) != -1; 218 | } 219 | 220 | public async cancelCurrentUpdate(): Promise { 221 | if (this._cancelSource != null) { 222 | this._cancelSource.cancel(); 223 | this._cancelSource.dispose(); 224 | this._cancelSource = null; 225 | } 226 | } 227 | 228 | private async _refresh(uri: vscode.Uri, force: boolean, cancelToken: vscode.CancellationToken | null): Promise { 229 | if (cancelToken?.isCancellationRequested) { 230 | return; 231 | } 232 | 233 | const document = await vscode.workspace.openTextDocument(uri); 234 | const version: { uri: vscode.Uri, version: number } = { uri: uri, version: document.version }; 235 | if (force || version === this._version) { 236 | return; 237 | } 238 | 239 | const options: Map = new Map([ 240 | ['is-editor', true] 241 | ]); 242 | let content = ''; 243 | await this._contentProvider.provideTextDocumentContent(document, options, async (data) => { 244 | if (cancelToken?.isCancellationRequested) { 245 | return; 246 | } 247 | 248 | content += data; 249 | }, cancelToken); 250 | 251 | if (cancelToken?.isCancellationRequested) { 252 | return; 253 | } 254 | 255 | this._version = version; 256 | this._panel.title = this._getTitleForUri(uri); 257 | this._panel.webview.html = content; 258 | 259 | const state = { uri: uri.toString(), index: this._index?.toString() }; 260 | await this._panel.webview.postMessage({ 261 | 'command': 'set-state', 262 | 'state': state 263 | }); 264 | } 265 | 266 | private _getTitleForUri(uri: vscode.Uri): string { 267 | const path = uri.path; 268 | const index = path.lastIndexOf('/'); 269 | const name = (index != -1) ? path.substring(index + 1) : path; 270 | return `${name} (Preview)`; 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | type PromiseType Promise> 2 | = T extends ((...args: any[]) => Promise) ? R : any; 3 | 4 | export const debounce = Promise>(func: F, waitFor: number) => { 5 | let timeout: NodeJS.Timeout; 6 | 7 | return (...args: Parameters): Promise> => 8 | new Promise((resolve) => { 9 | if (timeout) { 10 | clearTimeout(timeout); 11 | } 12 | 13 | timeout = setTimeout(async () => resolve(await func(...args)), waitFor); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /src/vt100Parser.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export class VT100Parser { 4 | 5 | public static async parse(document: vscode.TextDocument, callback: (range: vscode.Range, context: Map) => Promise, cancelToken: vscode.CancellationToken | null): Promise { 6 | const context = new Map(); 7 | 8 | // Initialize defaults 9 | context.set('foreground-color', 'default'); 10 | context.set('background-color', 'default'); 11 | context.set('bold', 'no'); 12 | context.set('dim', 'no'); 13 | context.set('underlined', 'no'); 14 | context.set('blink', 'no'); 15 | context.set('inverted', 'no'); 16 | context.set('hidden', 'no'); 17 | 18 | // eslint-disable-next-line no-control-regex 19 | const escapeRegex = /\x1B\[((?:[0-9]+;)*[0-9]+)?m/g; 20 | 21 | const lines = document.getText().split(/\r\n|\r|\n/); 22 | for (let i = 0; i < lines.length; i++) { 23 | if (cancelToken?.isCancellationRequested) { 24 | return; 25 | } 26 | 27 | const line = lines[i]; 28 | 29 | context.set('line-number', i); 30 | context.set('line-start', 'yes'); 31 | context.set('line-end', 'no'); 32 | 33 | escapeRegex.lastIndex = 0; 34 | let lastIndex = 0; 35 | let match; 36 | while ((match = escapeRegex.exec(line)) !== null) { 37 | // Push last result 38 | if (match.index - lastIndex > 0) { 39 | context.set('type', 'text'); 40 | await callback(new vscode.Range(i, lastIndex, i, match.index), context); 41 | } 42 | 43 | this._applyParams(match[1], context); 44 | context.set('type', 'escape-sequence'); 45 | await callback(new vscode.Range(i, match.index, i, escapeRegex.lastIndex), context); 46 | 47 | lastIndex = escapeRegex.lastIndex; 48 | context.set('line-start', 'no'); 49 | } 50 | 51 | context.set('type', 'text'); 52 | context.set('line-end', 'yes'); 53 | await callback(new vscode.Range(i, lastIndex, i, line.length), context); 54 | } 55 | } 56 | 57 | private static _getParams(params: string): string[] { 58 | // ESC[m is equivalent to ESC[0m 59 | if (typeof(params) == 'undefined') { 60 | return ['0']; 61 | } 62 | 63 | return params.split(';').map(this._trimLeadingZeroes); 64 | } 65 | 66 | private static _trimLeadingZeroes(value: string): string { 67 | let index = 0; 68 | 69 | while (index + 1 < value.length && value[index] == '0') { 70 | index++; 71 | } 72 | 73 | return value.substring(index); 74 | } 75 | 76 | private static _applyParams(params: string, context: Map): void { 77 | const splittedParams = this._getParams(params); 78 | 79 | // See https://misc.flogisoft.com/bash/tip_colors_and_formatting 80 | // And https://invisible-island.net/xterm/ctlseqs/ctlseqs.html 81 | for (const param of splittedParams) { 82 | if (param === "0") { 83 | context.set('foreground-color', 'default'); 84 | context.set('background-color', 'default'); 85 | context.set('bold', 'no'); 86 | context.set('dim', 'no'); 87 | context.set('underlined', 'no'); 88 | context.set('blink', 'no'); 89 | context.set('inverted', 'no'); 90 | context.set('hidden', 'no'); 91 | 92 | } else if (param === "1") { 93 | context.set('bold', 'yes'); 94 | } else if (param === "2") { 95 | context.set('dim', 'yes'); 96 | } else if (param === "4") { 97 | context.set('underlined', 'yes'); 98 | } else if (param === "5") { 99 | context.set('blink', 'yes'); 100 | } else if (param === "7") { 101 | context.set('inverted', 'yes'); 102 | } else if (param === "8") { 103 | context.set('hidden', 'yes'); 104 | 105 | } else if (param === "21") { 106 | context.set('bold', 'no'); 107 | } else if (param === "22") { 108 | context.set('dim', 'no'); 109 | } else if (param === "24") { 110 | context.set('underlined', 'no'); 111 | } else if (param === "25") { 112 | context.set('blink', 'no'); 113 | } else if (param === "27") { 114 | context.set('inverted', 'no'); 115 | } else if (param === "28") { 116 | context.set('hidden', 'no'); 117 | 118 | } else if (param === "39") { 119 | context.set('foreground-color', 'default'); 120 | } else if (param === "30") { 121 | context.set('foreground-color', 'black'); 122 | } else if (param === "31") { 123 | context.set('foreground-color', 'red'); 124 | } else if (param === "32") { 125 | context.set('foreground-color', 'green'); 126 | } else if (param === "33") { 127 | context.set('foreground-color', 'yellow'); 128 | } else if (param === "34") { 129 | context.set('foreground-color', 'blue'); 130 | } else if (param === "35") { 131 | context.set('foreground-color', 'magenta'); 132 | } else if (param === "36") { 133 | context.set('foreground-color', 'cyan'); 134 | } else if (param === "37") { 135 | context.set('foreground-color', 'light-gray'); 136 | } else if (param === "90") { 137 | context.set('foreground-color', 'dark-gray'); 138 | } else if (param === "91") { 139 | context.set('foreground-color', 'light-red'); 140 | } else if (param === "92") { 141 | context.set('foreground-color', 'light-green'); 142 | } else if (param === "93") { 143 | context.set('foreground-color', 'light-yellow'); 144 | } else if (param === "94") { 145 | context.set('foreground-color', 'light-blue'); 146 | } else if (param === "95") { 147 | context.set('foreground-color', 'light-magenta'); 148 | } else if (param === "96") { 149 | context.set('foreground-color', 'light-cyan'); 150 | } else if (param === "97") { 151 | context.set('foreground-color', 'white'); 152 | 153 | } else if (param === "49") { 154 | context.set('background-color', 'default'); 155 | } else if (param === "40") { 156 | context.set('background-color', 'black'); 157 | } else if (param === "41") { 158 | context.set('background-color', 'red'); 159 | } else if (param === "42") { 160 | context.set('background-color', 'green'); 161 | } else if (param === "43") { 162 | context.set('background-color', 'yellow'); 163 | } else if (param === "44") { 164 | context.set('background-color', 'blue'); 165 | } else if (param === "45") { 166 | context.set('background-color', 'magenta'); 167 | } else if (param === "46") { 168 | context.set('background-color', 'cyan'); 169 | } else if (param === "47") { 170 | context.set('background-color', 'light-gray'); 171 | } else if (param === "100") { 172 | context.set('background-color', 'dark-gray'); 173 | } else if (param === "101") { 174 | context.set('background-color', 'light-red'); 175 | } else if (param === "102") { 176 | context.set('background-color', 'light-green'); 177 | } else if (param === "103") { 178 | context.set('background-color', 'light-yellow'); 179 | } else if (param === "104") { 180 | context.set('background-color', 'light-blue'); 181 | } else if (param === "105") { 182 | context.set('background-color', 'light-magenta'); 183 | } else if (param === "106") { 184 | context.set('background-color', 'light-cyan'); 185 | } else if (param === "107") { 186 | context.set('background-color', 'white'); 187 | } 188 | } 189 | } 190 | 191 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2019", 5 | "lib": ["ES2019"], 6 | "outDir": "out", 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true 10 | }, 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | --------------------------------------------------------------------------------