├── .eslintrc.json ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── vscode-math-to-image.gif └── vscode-math-to-image.png ├── azure-pipelines.yml ├── examples ├── example.md └── svg │ ├── 9GYiSwRTee.svg │ ├── tbOH9fuFKM.svg │ └── zRKV2EFdAA.svg ├── package-lock.json ├── package.json ├── src ├── extension.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ └── index.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/class-name-casing": "warn", 11 | "@typescript-eslint/semi": ["warn", "never"], 12 | "curly": "warn", 13 | "eqeqeq": "warn", 14 | "no-throw-literal": "warn", 15 | "semi": "off" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | vsc-extension-quickstart.md 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # Diagnostic reports (https://nodejs.org/api/report.html) 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | *.lcov 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # Snowpack dependency directory (https://snowpack.dev/) 51 | web_modules/ 52 | 53 | # TypeScript cache 54 | *.tsbuildinfo 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Microbundle cache 63 | .rpt2_cache/ 64 | .rts2_cache_cjs/ 65 | .rts2_cache_es/ 66 | .rts2_cache_umd/ 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | .env.test 80 | 81 | # parcel-bundler cache (https://parceljs.org/) 82 | .cache 83 | .parcel-cache 84 | 85 | # Next.js build output 86 | .next 87 | out 88 | 89 | # Nuxt.js build / generate output 90 | .nuxt 91 | dist 92 | 93 | # Gatsby files 94 | .cache/ 95 | # Comment in the public line in if your project uses Gatsby and not Next.js 96 | # https://nextjs.org/blog/next-9-1#public-directory-support 97 | # public 98 | 99 | # vuepress build output 100 | .vuepress/dist 101 | 102 | # Serverless directories 103 | .serverless/ 104 | 105 | # FuseBox cache 106 | .fusebox/ 107 | 108 | # DynamoDB Local files 109 | .dynamodb/ 110 | 111 | # TernJS port file 112 | .tern-port 113 | 114 | # Stores VSCode versions used for testing VSCode extensions 115 | .vscode-test 116 | 117 | # yarn v2 118 | .yarn/cache 119 | .yarn/unplugged 120 | .yarn/build-state.yml 121 | .yarn/install-state.gz 122 | .pnp.* 123 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.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 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}", 15 | "--disable-extensions=1" 16 | ], 17 | "outFiles": [ 18 | "${workspaceFolder}/out/**/*.js" 19 | ], 20 | // "preLaunchTask": "${defaultBuildTask}" 21 | }, 22 | { 23 | "name": "Extension Tests", 24 | "type": "extensionHost", 25 | "request": "launch", 26 | "runtimeExecutable": "${execPath}", 27 | "args": [ 28 | "--extensionDevelopmentPath=${workspaceFolder}", 29 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index", 30 | "--disable-extensions=1" 31 | ], 32 | "outFiles": [ 33 | "${workspaceFolder}/out/test/**/*.js" 34 | ], 35 | "preLaunchTask": "${defaultBuildTask}" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.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 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | examples/** 6 | 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | 12 | .gitignore 13 | vsc-extension-quickstart.md 14 | azure-pipelines.yml 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "vscode-math-to-image" extension will be documented in this file. 4 | 5 | ## Release `v0.1.1` 6 | 7 | * Locally rendered equations are now using the new MathJax 3.0 engine. 8 | * Implemented optional styles for locally and remotely rendered SVGs. 9 | * Supported both GitHub and CodeCogs for remote render servers. 10 | 11 | ## Release `v0.0.2` 12 | 13 | ### Improvements 14 | 15 | * Changed the settings options to be more concise. 16 | 17 | ### Bug fixes 18 | 19 | * Fixed an issue where the README rating badge won't show up. 20 | 21 | ## Release `v0.0.1` 22 | 23 | * Initial release 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Team Meow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | logo 4 | 5 |

Math » Image

6 | 7 | 📐 We can help you render LaTeX math equations in any Markdown file! 8 | 9 | [![Azure DevOps builds (branch)](https://img.shields.io/azure-devops/build/MeowTeam/9f842be1-8208-4cb2-ab10-228d34a2c525/1/master?color=578a8a&label=azure%20pipelines&logo=azure-pipelines)](https://dev.azure.com/MeowTeam/vscode-math-to-image/_build/latest?definitionId=1&branchName=master) 10 | [![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/MeowTeam.vscode-math-to-image?color=db8465&label=vs%20marketplace&logo=visual-studio)](https://marketplace.visualstudio.com/items?itemName=MeowTeam.vscode-math-to-image) 11 | ![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/MeowTeam.vscode-math-to-image?label=downloads&color=82a0ba) 12 | ![Visual Studio Marketplace Rating (Stars)](https://img.shields.io/visual-studio-marketplace/stars/MeowTeam.vscode-math-to-image?color=e89927) 13 | 14 |
15 | 16 | This is a VS Code extension to help you convert a standard LaTeX math equation like `$E=mc^2$` to an image like (remote) or a local SVG which can be embedded inside Markdown files or websites that doesn't support rendering LaTeX yet. ~~(That's you GitHub!)~~ 17 | 18 | Read more about _Math to Image_ here: 19 | - **少数派:**[不支持 LaTeX 也能插入数学公式,这个小插件帮你实现「徒手渲染」](https://sspai.com/post/61877)。 20 | - **Medium:** [VS Code Math to Image: Write LaTeX Math Equations in GitHub Markdown the Easy Way!](https://medium.com/spencerweekly/vs-code-math-to-image-write-latex-math-equations-in-github-markdown-the-easy-way-9fa8b81dc910?source=friends_link&sk=cff035b443fb81f5b20a47370f23aed3) 21 | 22 |

Table of Contents

23 | 24 | - [Demo](#demo) 25 | - [Features](#features) 26 | - [Rendering remotely](#rendering-remotely) 27 | - [Rendering locally](#rendering-locally) 28 | - [Extension Settings](#extension-settings) 29 | - [Change Log](#change-log) 30 | 31 | ## Demo 32 | 33 | ![](assets/vscode-math-to-image.gif) 34 | 35 | ## Features 36 | 37 | There are two modes in which we will render your math equations in Markdown: 38 | 39 | * Locally (with MathJax and sourcing relative SVG), and... 40 | * Remotely (with GitHub's LaTeX rendering server). 41 | 42 | ### Rendering remotely 43 | 44 | This is actually a hack. GitHub won't render LaTeX equations inside normal places like GitHub README, but it can render them in Jupyter notebooks, so we took advantage of this feature, utilizing GitHub's equation rendering server to embed SVG equations in GitHub. (See here for details: [A hack for showing LaTeX formulas in GitHub markdown](https://gist.github.com/a-rodin/fef3f543412d6e1ec5b6cf55bf197d7b).) 45 | 46 | Basically we can convert a standard LaTeX math equation like the *Gaussian Normal Distribution*... 47 | 48 | ```latex 49 | $$ 50 | P(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{\frac{-(x-\mu)^2}{2\sigma^2}} 51 | $$ 52 | ``` 53 | 54 | ... to a rendered image tag with the help of GitHub's math rendering server: 55 | 56 | ```html 57 |
58 | ``` 59 | 60 |
61 | 62 | In addition to GitHub's rendering server, we also newly added support for CodeCogs' rendering server: 63 | 64 | ```html 65 |
66 | ``` 67 | 68 |
69 | 70 | ### Rendering locally 71 | 72 | Not everywhere accept external SVGs. To circumvent this type of scenario, we can render math equations directly to local SVGs (with MathJax), and embed these local SVGs into our Markdown as a workaround. 73 | 74 | We can convert the same LaTeX math equation: 75 | 76 | ```latex 77 | $$ 78 | P(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{\frac{-(x-\mu)^2}{2\sigma^2}} 79 | $$ 80 | ``` 81 | 82 | To a local SVG like: `svg/e40qQ5G9jw.svg`, which will be saved to a dedicate folder called `svg`, and sourced inside the Markdown file that requires math-embedding. 83 | 84 | > 🌸 NOTE: There used to be a demo SVG here, but Visual Studio Marketplace won't allow us to add local SVGs inside their README. 85 | 86 | If you are reading this on GitHub, you can see that both of these methods work when we need to add math equations to READMEs or other Markdown files on GitHub. 87 | 88 | See [examples](examples/example.md) for more math equation rendering scenarios, i.e, inline math, aligned environments... 89 | 90 | ## Extension Settings 91 | 92 | You can specify the path to save the locally rendered SVG image. The settings are self-explanatory. 93 | 94 | * **vscode-math-to-image.svgSavePath**: 95 | * `Current file directory`: Generated SVG files will be put in a folder under current file's directory. 96 | * `Current workspace directory`: Generated SVG files will be put in a folder under current workspace directory. 97 | * **vscode-math-to-image.remoteRenderEngine**: 98 | * `GitHub`: Equations will be rendered with GitHub's rendering engine remotely. 99 | * `CodeCogs`: Equations will be rendered with CodeCogs' rendering engine remotely. 100 | * **vscode-math-to-image.inlineSvgStyle**: Optional style for rendered inline SVG equations. Defaults to `transform: translateY(0.1em); background: white;`. Note: Not supported with Markdown-insertionType. 101 | * **vscode-math-to-image.displaySvgStyle**: Optional style for rendered display SVG equations. Defaults to `background: white;`. Note: Not supported with Markdown-insertionType. 102 | * **vscode-math-to-image.insertionType**: Choose whether to insert the rendered equations as HTML (default) or Markdown. Note that Markdown doesn't support styling. 103 | 104 | ## Change Log 105 | 106 | For version updates and bug fixes, please see: [CHANGELOG](CHANGELOG.md). 107 | 108 | --- 109 | 110 | 📐 **Math » Image** © TeamMeow. Released under the MIT License. 111 | 112 | Authored and maintained by TeamMeow members. 113 | 114 | > 🦁 We build things that go "meow" · [@GitHub](https://github.com/TeamMeow) · [@Members](https://github.com/orgs/TeamMeow/people?type=source) 115 | -------------------------------------------------------------------------------- /assets/vscode-math-to-image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMeow/vscode-math-to-image/98b199c5051e57455d653adc30ecd7ec8b3ad3c3/assets/vscode-math-to-image.gif -------------------------------------------------------------------------------- /assets/vscode-math-to-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMeow/vscode-math-to-image/98b199c5051e57455d653adc30ecd7ec8b3ad3c3/assets/vscode-math-to-image.png -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | branches: 8 | include: 9 | - master 10 | tags: 11 | include: 12 | - refs/tags/v* 13 | 14 | pool: 15 | vmImage: 'ubuntu-latest' 16 | 17 | steps: 18 | - task: NodeTool@0 19 | inputs: 20 | versionSpec: '14.x' 21 | displayName: 'Install Node.js' 22 | 23 | - bash: | 24 | echo ">>> Compile extension" 25 | npm install && npm run vscode:prepublish 26 | displayName: Compile extension 27 | 28 | - bash: | 29 | echo ">>> Publish" 30 | npm run deploy 31 | displayName: Publish 32 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['Agent.OS'], 'Linux')) 33 | env: 34 | VSCE_PAT: $(VSCE_PAT) 35 | -------------------------------------------------------------------------------- /examples/example.md: -------------------------------------------------------------------------------- 1 |

Example vscode-math-to-image rendering

2 | 3 | - [Native markdown](#native-markdown) 4 | - [Display math](#display-math) 5 | - [Inline equation](#inline-equation) 6 | - [GitHub LaTeX image](#github-latex-image) 7 | - [Display math](#display-math-1) 8 | - [Inline equation](#inline-equation-1) 9 | - [Local SVG](#local-svg) 10 | - [Display math](#display-math-2) 11 | - [Inline equation](#inline-equation-2) 12 | 13 | ## Native markdown 14 | 15 | > This section won't be rendered inside GitHub. Markdown LaTeX equation rendering relies on third-party libraries (for instance: KaTeX). 16 | 17 | ### Display math 18 | 19 | Equation: 20 | 21 | $$ 22 | \ell = \sum_{i}^{N}(y_i - \hat{y}_i)^2 - ||w||_2^2 23 | $$ 24 | 25 | Aligned environment: 26 | 27 | $$ 28 | \begin{aligned} 29 | f &= 2 + x + 3 \\ 30 | &= 5 + x 31 | \end{aligned} 32 | $$ 33 | 34 | ### Inline equation 35 | 36 | In physics, the mass-energy equivalence is stated by the equation $E=mc^2$, discovered in 1905 by Albert Einstein. 37 | 38 | ## GitHub LaTeX image 39 | 40 | 🛠 Command: `Math » Image: Insert rendered equation (remote)`. 41 | 42 | > Equations in this section **WILL** be rendered as they have been converted to images (by this extension!) which are rendered with GitHub's official Jupyter Notebook rendering engine. This hack is provided here: [A hack for showing LaTeX formulas in GitHub markdown](https://gist.github.com/a-rodin/fef3f543412d6e1ec5b6cf55bf197d7b). 43 | 44 | ### Display math 45 | 46 | Equation: 47 | 48 | 51 | 52 |
53 | 54 | Aligned environment: 55 | 56 | 62 | 63 |
64 | 65 | ### Inline equation 66 | 67 | In physics, the mass-energy equivalence is stated by the equation , discovered in 1905 by Albert Einstein. 68 | 69 | ## Local SVG 70 | 71 | 🛠 Command: `Math » Image: Insert rendered equation (local)`. 72 | 73 | > Equations in this section **WILL** be rendered with MathJax to an SVG, and will be saved to a local folder. Then we will source the SVG's path to your Markdown file, and thus rendering the image. 74 | 75 | ### Display math 76 | 77 | Equation: 78 | 79 | 82 | 83 |
84 | 85 | Aligned environment: 86 | 87 | 93 | 94 |
95 | 96 | ### Inline equation 97 | 98 | In physics, the mass-energy equivalence is stated by the equation , discovered in 1905 by Albert Einstein. 99 | -------------------------------------------------------------------------------- /examples/svg/9GYiSwRTee.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/svg/tbOH9fuFKM.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/svg/zRKV2EFdAA.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-math-to-image", 3 | "displayName": "Math to Image", 4 | "description": "📐 Render LaTeX math equations in any Markdown file", 5 | "license": "MIT", 6 | "version": "0.1.1", 7 | "publisher": "MeowTeam", 8 | "engines": { 9 | "vscode": "^1.47.0" 10 | }, 11 | "categories": [ 12 | "Other" 13 | ], 14 | "icon": "assets/vscode-math-to-image.png", 15 | "galleryBanner": { 16 | "color": "#2b3838", 17 | "theme": "dark" 18 | }, 19 | "homepage": "https://github.com/TeamMeow/vscode-math-to-image", 20 | "bugs": { 21 | "url": "https://github.com/TeamMeow/vscode-math-to-image/issues" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/TeamMeow/vscode-math-to-image" 26 | }, 27 | "activationEvents": [ 28 | "onCommand:vscode-math-to-image.render-selection-remote", 29 | "onCommand:vscode-math-to-image.render-selection-local" 30 | ], 31 | "main": "./out/extension.js", 32 | "contributes": { 33 | "commands": [ 34 | { 35 | "command": "vscode-math-to-image.render-selection-remote", 36 | "title": "Math » Image: Insert rendered equation (remote)" 37 | }, 38 | { 39 | "command": "vscode-math-to-image.render-selection-local", 40 | "title": "Math » Image: Insert rendered equation (local)" 41 | } 42 | ], 43 | "menus": { 44 | "editor/context": [ 45 | { 46 | "command": "vscode-math-to-image.render-selection-remote", 47 | "when": "resourceLangId == markdown && editorFocus && editorHasSelection", 48 | "group": "modification" 49 | }, 50 | { 51 | "command": "vscode-math-to-image.render-selection-local", 52 | "when": "resourceLangId == markdown && editorFocus && editorHasSelection", 53 | "group": "modification" 54 | } 55 | ] 56 | }, 57 | "configuration": { 58 | "title": "vscode-math-to-image", 59 | "properties": { 60 | "vscode-math-to-image.svgSavePath": { 61 | "type": "string", 62 | "default": "Current workspace directory", 63 | "enum": [ 64 | "Current file directory", 65 | "Current workspace directory" 66 | ], 67 | "enumDescriptions": [ 68 | "Generated SVG files will be put in a folder under current file's directory.", 69 | "Generated SVG files will be put in a folder under current workspace directory." 70 | ] 71 | }, 72 | "vscode-math-to-image.remoteRenderEngine": { 73 | "type": "string", 74 | "default": "GitHub", 75 | "enum": [ 76 | "GitHub", 77 | "CodeCogs" 78 | ], 79 | "enumDescriptions": [ 80 | "Equations will be rendered with GitHub's rendering engine remotely.", 81 | "Equations will be rendered with CodeCogs' rendering engine remotely." 82 | ] 83 | }, 84 | "vscode-math-to-image.insertionType": { 85 | "type": "string", 86 | "default": "HTML", 87 | "enum": [ 88 | "HTML", 89 | "Markdown" 90 | ], 91 | "enumDescriptions": [ 92 | "Equations will be inserted as HTML.", 93 | "Equations will be inserted as Markdown. Note that this doesn't support styles!" 94 | ] 95 | }, 96 | "vscode-math-to-image.inlineSvgStyle": { 97 | "type": "string", 98 | "default": "transform: translateY(0.1em); background: white;", 99 | "description": "Optional style for rendered inline SVG equations. (Only supported when inserting as HTML)" 100 | }, 101 | "vscode-math-to-image.displaySvgStyle": { 102 | "type": "string", 103 | "default": "background: white;", 104 | "description": "Optional style for rendered display SVG equations. (Only supported when inserting as HTML)" 105 | } 106 | } 107 | } 108 | }, 109 | "scripts": { 110 | "vscode:prepublish": "npm run compile", 111 | "compile": "tsc -p ./", 112 | "lint": "eslint src --ext ts", 113 | "watch": "tsc -watch -p ./", 114 | "pretest": "npm run compile && npm run lint", 115 | "test": "node ./out/test/runTest.js", 116 | "deploy": "vsce publish" 117 | }, 118 | "devDependencies": { 119 | "@types/glob": "^7.1.1", 120 | "@types/mocha": "^7.0.2", 121 | "@types/node": "^13.11.0", 122 | "@types/vscode": "^1.47.0", 123 | "@typescript-eslint/eslint-plugin": "^2.30.0", 124 | "@typescript-eslint/parser": "^2.30.0", 125 | "eslint": "^6.8.0", 126 | "glob": "^7.1.6", 127 | "mocha": "^7.1.2", 128 | "typescript": "^3.8.3", 129 | "vsce": "^1.77.0", 130 | "vscode-test": "^1.3.0" 131 | }, 132 | "dependencies": { 133 | "mathjax": "^3.1.2", 134 | "mathjax-full": "^3.1.2" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import * as fs from 'fs' 3 | import * as path from 'path' 4 | 5 | let editor = vscode.window.activeTextEditor 6 | 7 | enum RenderType { 8 | REMOTE, 9 | LOCAL, 10 | } 11 | enum MathType { 12 | INLINE, 13 | DISPLAY, 14 | } 15 | 16 | /** 17 | * Get absolute SVG file path to write 18 | * 19 | * @param fileName Name of SVG file 20 | */ 21 | function getSVGPath(fileName: string) { 22 | const svgSavePathConf = vscode.workspace.getConfiguration().get('vscode-math-to-image.svgSavePath') 23 | const currentPath: string = editor?.document.uri.fsPath! 24 | let folderPath = '' 25 | 26 | if (svgSavePathConf === 'Current file directory') { 27 | folderPath = path.dirname(currentPath) 28 | } else { 29 | folderPath = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri.fsPath : '' 30 | } 31 | 32 | return path.join(folderPath, 'svg', fileName) 33 | } 34 | 35 | /** 36 | * Write rendered SVG content to local SVG file 37 | * 38 | * @param filePath Path to write generated SVG file 39 | * @param fileContent SVG content to write to file 40 | */ 41 | function writeSVGFile(filePath: string, fileContent: string) { 42 | const dirPath = path.dirname(filePath) 43 | if (!fs.existsSync(dirPath)) { 44 | fs.mkdir(dirPath, { recursive: true }, (err: any) => { 45 | if (err) { 46 | vscode.window.showErrorMessage(`Failed to create SVG folder at ${dirPath}.`) 47 | return 48 | } 49 | }) 50 | } 51 | fs.writeFile(filePath, fileContent, (err: any) => { 52 | if (err) { 53 | vscode.window.showErrorMessage(`Failed to create SVG file at ${filePath}.`) 54 | } 55 | }) 56 | } 57 | 58 | /** 59 | * Generate a random filename 60 | * 61 | * @param len Length of the generated filename 62 | * @param ext Intended file extension 63 | */ 64 | function randomFilename(len: number, ext: string) { 65 | const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') 66 | 67 | // Create an array with length len and fill with random digits 68 | const sequence = [...Array(len)].map(_ => { 69 | return digits[Math.floor(Math.random() * digits.length)] 70 | }) 71 | return `${sequence.join('')}.${ext}` 72 | } 73 | 74 | function generateEquationHtml(src: string, mathType: MathType) { 75 | const inlineStyle = vscode.workspace.getConfiguration().get("vscode-math-to-image.inlineSvgStyle") 76 | const displayStyle = vscode.workspace.getConfiguration().get("vscode-math-to-image.displaySvgStyle") 77 | 78 | if (mathType === MathType.DISPLAY) { 79 | return `\n\n
` 80 | } else { 81 | return `` 82 | } 83 | } 84 | 85 | /** 86 | * Encode URL and return rendered image path based on selected equation 87 | * 88 | * @param equation Selected inline or display equation 89 | * @param mathType Equation type (inline / display) 90 | */ 91 | function renderEquationRemote(equation: string, mathType: MathType) { 92 | const renderingEngine = vscode.workspace.getConfiguration().get('vscode-math-to-image.remoteRenderEngine') 93 | 94 | let renderAPIUrl = '' 95 | if (renderingEngine === 'GitHub') { 96 | renderAPIUrl = 'https://render.githubusercontent.com/render/math?math=' 97 | } else if (renderingEngine === 'CodeCogs') { 98 | renderAPIUrl = 'https://latex.codecogs.com/svg.latex?' 99 | } 100 | 101 | const encodedMath = encodeURIComponent(equation) 102 | return `${renderAPIUrl}${encodedMath}` 103 | } 104 | 105 | /** 106 | * Render equation with MathJax to SVG and source from local file 107 | * 108 | * @param equation Selected inline or display equation 109 | * @param mathType Equation type (inline / display) 110 | */ 111 | function renderEquationLocal(equation: string, mathType: MathType) { 112 | const filename = randomFilename(10, 'svg') 113 | const svgPath = getSVGPath(filename) 114 | const documentPath: string = editor?.document.uri.fsPath! 115 | const relativeSvgPath = path.relative(path.dirname(documentPath), svgPath) 116 | 117 | require('mathjax') 118 | .init({ 119 | loader: { load: ['input/tex', 'output/svg'] }, 120 | }) 121 | .then((MathJax: any) => { 122 | const renderedNode = MathJax.tex2svg(equation, { 123 | display: mathType === MathType.DISPLAY, 124 | }) 125 | const renderedSvg = MathJax.startup.adaptor.innerHTML(renderedNode) 126 | writeSVGFile(svgPath, renderedSvg) 127 | }) 128 | .catch((err: string) => { 129 | vscode.window.showErrorMessage(`[err]: ${err}`) 130 | }) 131 | 132 | return relativeSvgPath 133 | } 134 | 135 | /** 136 | * Insert rendered image into current VS Code editor 137 | * 138 | * @param renderedImagePath Path to rendered image (remote or local image) 139 | * @param start Selection start 140 | * @param end Selection end 141 | */ 142 | function insertMathImage(renderedImagePath: string, start: vscode.Position, end: vscode.Position, mathType: MathType) { 143 | const insertionType = vscode.workspace.getConfiguration().get("vscode-math-to-image.insertionType") 144 | if(insertionType === 'HTML') 145 | { 146 | const renderedImageCode = generateEquationHtml(renderedImagePath, mathType) 147 | editor?.edit(editBuilder => { 148 | editBuilder.insert(start, ' ${renderedImageCode}`) 150 | }) 151 | } 152 | else if(insertionType === 'Markdown') 153 | { 154 | editor?.edit(editBuilder => { 155 | if(mathType === MathType.DISPLAY) 156 | { 157 | editBuilder.insert(start, '\n\n') 158 | start.translate(0,2) 159 | } 160 | editBuilder.insert(start, '![') 161 | editBuilder.insert(end, `](${renderedImagePath})`) 162 | }) 163 | } 164 | vscode.window.showInformationMessage(`Render equation successfully!`) 165 | } 166 | 167 | /** 168 | * Render function to insert image form of the selected equation into current editor 169 | * 170 | * @param renderType Type of equation to render (inline or multiline) 171 | */ 172 | function renderEntry(renderType: RenderType) { 173 | editor = vscode.window.activeTextEditor 174 | const selection = editor?.document.getText(editor.selection) 175 | const selectionStart = editor?.selection.start 176 | const selectionEnd = editor?.selection.end 177 | 178 | const inlineMath = /^\$[\s\S]*[\S]+[\s\S]*\$$/ 179 | const displayMath = /^\$\$[\s\S]*[\S]+[\s\S]*\$\$$/ 180 | 181 | if (selection === undefined || selectionStart === undefined || selectionEnd === undefined) { 182 | vscode.window.showErrorMessage('Nothing selected!') 183 | } else { 184 | if (displayMath.test(selection)) { 185 | // Remove leading $$ and trailing $$ 186 | const equation = selection.split('\n').slice(1, -1).join('\n') 187 | 188 | const renderedImage = 189 | renderType === RenderType.REMOTE 190 | ? renderEquationRemote(equation, MathType.DISPLAY) 191 | : renderEquationLocal(equation, MathType.DISPLAY) 192 | insertMathImage(renderedImage, selectionStart, selectionEnd, MathType.DISPLAY) 193 | } else if (inlineMath.test(selection)) { 194 | // Remove leading $ and trailing $ 195 | const equation = selection.slice(1, -1).trim() 196 | 197 | const renderedImage = 198 | renderType === RenderType.REMOTE 199 | ? renderEquationRemote(equation, MathType.INLINE) 200 | : renderEquationLocal(equation, MathType.INLINE) 201 | insertMathImage(renderedImage, selectionStart, selectionEnd, MathType.INLINE) 202 | } else { 203 | vscode.window.showErrorMessage('Not a valid equation, include leading and trailing dollar signs: $ as well.') 204 | } 205 | } 206 | } 207 | 208 | export function activate(context: vscode.ExtensionContext) { 209 | const renderSelectionRemote = vscode.commands.registerCommand('vscode-math-to-image.render-selection-remote', () => { 210 | renderEntry(RenderType.REMOTE) 211 | }) 212 | 213 | const renderSelectionLocal = vscode.commands.registerCommand('vscode-math-to-image.render-selection-local', () => { 214 | renderEntry(RenderType.LOCAL) 215 | }) 216 | 217 | context.subscriptions.push(renderSelectionRemote) 218 | context.subscriptions.push(renderSelectionLocal) 219 | } 220 | 221 | export function deactivate() {} 222 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | 3 | import { runTests } from 'vscode-test' 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../') 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index') 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }) 17 | } catch (err) { 18 | console.error('Failed to run tests') 19 | process.exit(1) 20 | } 21 | } 22 | 23 | main() 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode' 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.') 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)) 13 | assert.equal(-1, [1, 2, 3].indexOf(0)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as Mocha from 'mocha' 3 | import * as glob from 'glob' 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true, 10 | }) 11 | 12 | const testsRoot = path.resolve(__dirname, '..') 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err) 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))) 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)) 28 | } else { 29 | c() 30 | } 31 | }) 32 | } catch (err) { 33 | console.error(err) 34 | e(err) 35 | } 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | --------------------------------------------------------------------------------