├── .gitignore ├── .tool-versions ├── .vscode └── launch.json ├── .vscodeignore ├── CHANGELOG.md ├── README.md ├── images ├── logo.png └── logo.svg ├── language-configuration.json ├── package-lock.json ├── package.json ├── snippets └── mint.json ├── src ├── commands.ts ├── extension.ts ├── formatter.ts └── utils.ts ├── syntaxes ├── mint.tmLanguage.json └── mint.tmLanguage.yaml └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.vsix 3 | out -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 20.9.0 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension 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": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"] 14 | } 15 | ], 16 | "log": true 17 | } 18 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Mint VS Code Changelog 2 | 3 | ## 0.6.0 4 | 5 | - Add line comment ([#23](https://github.com/mint-lang/mint-vscode/pull/23)) 6 | - Add missing module snippet ([#24](https://github.com/mint-lang/mint-vscode/pull/24)) 7 | 8 | ## 0.5.0 9 | 10 | - Better syntax highlighting ([#21](https://github.com/mint-lang/mint-vscode/pull/21)) 11 | 12 | ## 0.4.0 13 | 14 | - Added support for language server ([#20](https://github.com/mint-lang/mint-vscode/pull/20)) 15 | 16 | ## 0.3.3 17 | 18 | - Highlight `const` keyword ([#18](https://github.com/mint-lang/mint-vscode/pull/18)) 19 | 20 | ## 0.3.2 21 | 22 | - Highlight `using` keyword ([#16](https://github.com/mint-lang/mint-vscode/pull/16)) 23 | 24 | ## 0.3.1 25 | 26 | - Highlight `enum` keyword ([#15](https://github.com/mint-lang/mint-vscode/pull/15)) 27 | - Hide Command Palette commands when not in a Mint project ([#14](https://github.com/mint-lang/mint-vscode/pull/14)) 28 | 29 | ## 0.3.0 30 | 31 | - Add Mint CLI commands to command palette ([#10](https://github.com/mint-lang/mint-vscode/pull/10)) 32 | 33 | ## 0.2.0 34 | 35 | - Add formatting support ([#7](https://github.com/mint-lang/mint-vscode/pull/7)) 36 | 37 | ## 0.1.0 38 | 39 | - Fix broken HTML highlighting ([#1](https://github.com/mint-lang/mint-vscode/pull/1)) 40 | 41 | ## 0.0.2 42 | 43 | - Initial Fork 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mint VS Code Support 2 | 3 | ### This extension provides support for the [Mint](https://mint-lang.com) programming language. 4 | 5 | ### Features 6 | 7 | - Simple Syntax Highlighting 8 | - Snippets 9 | - Formatting 10 | - Commands 11 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mint-lang/mint-vscode/9b0c8b54f80c9e32bb479ae8737b2c68ec275dd4/images/logo.png -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 16 | 18 | image/svg+xml 19 | 21 | 22 | 23 | 24 | 25 | 27 | 29 | 34 | 39 | 40 | 49 | 51 | 55 | 59 | 60 | 69 | 78 | 87 | 96 | 105 | 114 | 123 | 132 | 133 | 137 | 141 | 145 | 146 | -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": ["/*", "*/"] 5 | }, 6 | "brackets": [ 7 | ["{", "}"], 8 | ["[", "]"], 9 | ["(", ")"] 10 | ], 11 | "autoClosingPairs": [ 12 | ["{", "}"], 13 | ["[", "]"], 14 | ["(", ")"], 15 | ["\"", "\""], 16 | ["'", "'"] 17 | ], 18 | "surroundingPairs": [ 19 | ["{", "}"], 20 | ["[", "]"], 21 | ["(", ")"], 22 | ["\"", "\""], 23 | ["'", "'"] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mint", 3 | "version": "0.7.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mint", 9 | "version": "0.7.0", 10 | "hasInstallScript": true, 11 | "license": "MIT", 12 | "dependencies": { 13 | "vscode-languageclient": "^6.1.3" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^14.11.2", 17 | "@types/vscode": "^1.77.0", 18 | "js-yaml": "^3.14.0", 19 | "typescript": "^4.0.3" 20 | }, 21 | "engines": { 22 | "vscode": "^1.77.0" 23 | } 24 | }, 25 | "node_modules/@types/node": { 26 | "version": "14.11.2", 27 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", 28 | "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", 29 | "dev": true 30 | }, 31 | "node_modules/@types/vscode": { 32 | "version": "1.96.0", 33 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", 34 | "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==", 35 | "dev": true 36 | }, 37 | "node_modules/argparse": { 38 | "version": "1.0.10", 39 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 40 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 41 | "dev": true, 42 | "dependencies": { 43 | "sprintf-js": "~1.0.2" 44 | } 45 | }, 46 | "node_modules/esprima": { 47 | "version": "4.0.1", 48 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 49 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 50 | "dev": true, 51 | "bin": { 52 | "esparse": "bin/esparse.js", 53 | "esvalidate": "bin/esvalidate.js" 54 | }, 55 | "engines": { 56 | "node": ">=4" 57 | } 58 | }, 59 | "node_modules/js-yaml": { 60 | "version": "3.14.0", 61 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 62 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 63 | "dev": true, 64 | "dependencies": { 65 | "argparse": "^1.0.7", 66 | "esprima": "^4.0.0" 67 | }, 68 | "bin": { 69 | "js-yaml": "bin/js-yaml.js" 70 | } 71 | }, 72 | "node_modules/semver": { 73 | "version": "6.3.0", 74 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 75 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 76 | "bin": { 77 | "semver": "bin/semver.js" 78 | } 79 | }, 80 | "node_modules/sprintf-js": { 81 | "version": "1.0.3", 82 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 83 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 84 | "dev": true 85 | }, 86 | "node_modules/typescript": { 87 | "version": "4.0.3", 88 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", 89 | "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", 90 | "dev": true, 91 | "bin": { 92 | "tsc": "bin/tsc", 93 | "tsserver": "bin/tsserver" 94 | }, 95 | "engines": { 96 | "node": ">=4.2.0" 97 | } 98 | }, 99 | "node_modules/vscode-jsonrpc": { 100 | "version": "5.0.1", 101 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", 102 | "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==", 103 | "engines": { 104 | "node": ">=8.0.0 || >=10.0.0" 105 | } 106 | }, 107 | "node_modules/vscode-languageclient": { 108 | "version": "6.1.4", 109 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.4.tgz", 110 | "integrity": "sha512-EUOU+bJu6axmt0RFNo3nrglQLPXMfanbYViJee3Fbn2VuQoX0ZOI4uTYhSRvYLP2vfwTP/juV62P/mksCdTZMA==", 111 | "dependencies": { 112 | "semver": "^6.3.0", 113 | "vscode-languageserver-protocol": "3.15.3" 114 | }, 115 | "engines": { 116 | "vscode": "^1.41.0" 117 | } 118 | }, 119 | "node_modules/vscode-languageserver-protocol": { 120 | "version": "3.15.3", 121 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", 122 | "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", 123 | "dependencies": { 124 | "vscode-jsonrpc": "^5.0.1", 125 | "vscode-languageserver-types": "3.15.1" 126 | } 127 | }, 128 | "node_modules/vscode-languageserver-types": { 129 | "version": "3.15.1", 130 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", 131 | "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" 132 | } 133 | }, 134 | "dependencies": { 135 | "@types/node": { 136 | "version": "14.11.2", 137 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", 138 | "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", 139 | "dev": true 140 | }, 141 | "@types/vscode": { 142 | "version": "1.96.0", 143 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", 144 | "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==", 145 | "dev": true 146 | }, 147 | "argparse": { 148 | "version": "1.0.10", 149 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 150 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 151 | "dev": true, 152 | "requires": { 153 | "sprintf-js": "~1.0.2" 154 | } 155 | }, 156 | "esprima": { 157 | "version": "4.0.1", 158 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 159 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 160 | "dev": true 161 | }, 162 | "js-yaml": { 163 | "version": "3.14.0", 164 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 165 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 166 | "dev": true, 167 | "requires": { 168 | "argparse": "^1.0.7", 169 | "esprima": "^4.0.0" 170 | } 171 | }, 172 | "semver": { 173 | "version": "6.3.0", 174 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 175 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 176 | }, 177 | "sprintf-js": { 178 | "version": "1.0.3", 179 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 180 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 181 | "dev": true 182 | }, 183 | "typescript": { 184 | "version": "4.0.3", 185 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", 186 | "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", 187 | "dev": true 188 | }, 189 | "vscode-jsonrpc": { 190 | "version": "5.0.1", 191 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", 192 | "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" 193 | }, 194 | "vscode-languageclient": { 195 | "version": "6.1.4", 196 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.4.tgz", 197 | "integrity": "sha512-EUOU+bJu6axmt0RFNo3nrglQLPXMfanbYViJee3Fbn2VuQoX0ZOI4uTYhSRvYLP2vfwTP/juV62P/mksCdTZMA==", 198 | "requires": { 199 | "semver": "^6.3.0", 200 | "vscode-languageserver-protocol": "3.15.3" 201 | } 202 | }, 203 | "vscode-languageserver-protocol": { 204 | "version": "3.15.3", 205 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", 206 | "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", 207 | "requires": { 208 | "vscode-jsonrpc": "^5.0.1", 209 | "vscode-languageserver-types": "3.15.1" 210 | } 211 | }, 212 | "vscode-languageserver-types": { 213 | "version": "3.15.1", 214 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", 215 | "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mint", 3 | "license": "MIT", 4 | "displayName": "Mint", 5 | "description": "VS Code Support for the Mint programming language.", 6 | "version": "0.7.0", 7 | "publisher": "mint-lang", 8 | "repository": { 9 | "url": "https://github.com/mint-lang/mint-vscode" 10 | }, 11 | "engines": { 12 | "vscode": "^1.77.0" 13 | }, 14 | "activationEvents": [ 15 | "onLanguage:mint", 16 | "workspaceContains:**/*.mint", 17 | "onCommand:mint.init" 18 | ], 19 | "main": "./out/src/extension", 20 | "dependencies": { 21 | "vscode-languageclient": "^6.1.3" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^14.11.2", 25 | "@types/vscode": "^1.77.0", 26 | "js-yaml": "^3.14.0", 27 | "typescript": "^4.0.3" 28 | }, 29 | "scripts": { 30 | "compile": "echo Compiling... && npm run convert && tsc -p ./", 31 | "convert": "js-yaml syntaxes/mint.tmLanguage.yaml > syntaxes/mint.tmLanguage.json", 32 | "postinstall": "tsc -p ./", 33 | "watch": "tsc -watch -p ./", 34 | "vscode:prepublish": "npm run compile" 35 | }, 36 | "icon": "images/logo.png", 37 | "categories": [ 38 | "Programming Languages" 39 | ], 40 | "contributes": { 41 | "configuration": { 42 | "type": "object", 43 | "title": "Mint Language Server", 44 | "properties": { 45 | "mint.languageServer.location": { 46 | "type": "string", 47 | "description": "Provide a custom location for the language server binary" 48 | }, 49 | "mint.trace.server": { 50 | "scope": "window", 51 | "type": "string", 52 | "enum": ["off","messages","verbose"], 53 | "default": "verbose" 54 | } 55 | } 56 | }, 57 | "languages": [ 58 | { 59 | "id": "mint", 60 | "aliases": [ 61 | "Mint", 62 | "mint" 63 | ], 64 | "extensions": [ 65 | ".mint" 66 | ], 67 | "configuration": "./language-configuration.json" 68 | } 69 | ], 70 | "grammars": [ 71 | { 72 | "language": "mint", 73 | "scopeName": "source.mint", 74 | "path": "./syntaxes/mint.tmLanguage.json" 75 | } 76 | ], 77 | "snippets": [ 78 | { 79 | "language": "mint", 80 | "path": "./snippets/mint.json" 81 | } 82 | ], 83 | "commands": [ 84 | { 85 | "command": "mint.build", 86 | "title": "Build production bundle", 87 | "category": "Mint" 88 | }, 89 | { 90 | "command": "mint.compile", 91 | "title": "Compile project into single JavaScript file", 92 | "category": "Mint" 93 | }, 94 | { 95 | "command": "mint.docs", 96 | "title": "Start documentation server", 97 | "category": "Mint" 98 | }, 99 | { 100 | "command": "mint.formatAll", 101 | "title": "Format all files", 102 | "category": "Mint" 103 | }, 104 | { 105 | "command": "mint.init", 106 | "title": "Initialize new project", 107 | "category": "Mint" 108 | }, 109 | { 110 | "command": "mint.install", 111 | "title": "Install dependencies", 112 | "category": "Mint" 113 | }, 114 | { 115 | "command": "mint.loc", 116 | "title": "Count lines of code", 117 | "category": "Mint" 118 | }, 119 | { 120 | "command": "mint.start", 121 | "title": "Start development server", 122 | "category": "Mint" 123 | }, 124 | { 125 | "command": "mint.test", 126 | "title": "Run tests", 127 | "category": "Mint" 128 | }, 129 | { 130 | "command": "mint.version", 131 | "title": "Show current version", 132 | "category": "Mint" 133 | } 134 | ], 135 | "menus": { 136 | "commandPalette": [ 137 | { 138 | "command": "mint.build", 139 | "when": "mint:isActivated" 140 | }, 141 | { 142 | "command": "mint.compile", 143 | "when": "mint:isActivated" 144 | }, 145 | { 146 | "command": "mint.docs", 147 | "when": "mint:isActivated" 148 | }, 149 | { 150 | "command": "mint.formatAll", 151 | "when": "mint:isActivated" 152 | }, 153 | { 154 | "command": "mint.init" 155 | }, 156 | { 157 | "command": "mint.install", 158 | "when": "mint:isActivated" 159 | }, 160 | { 161 | "command": "mint.loc", 162 | "when": "mint:isActivated" 163 | }, 164 | { 165 | "command": "mint.start", 166 | "when": "mint:isActivated" 167 | }, 168 | { 169 | "command": "mint.test", 170 | "when": "mint:isActivated" 171 | }, 172 | { 173 | "command": "mint.version", 174 | "when": "mint:isActivated" 175 | } 176 | ] 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /snippets/mint.json: -------------------------------------------------------------------------------- 1 | { 2 | "Main component": { 3 | "prefix": "main", 4 | "body": [ 5 | "component Main {", 6 | "\tfun render : Html {", 7 | "\t\t<$0/>", 8 | "\t}", 9 | "}" 10 | ], 11 | "description": "The component named Main is the one that get's rendered on the screen." 12 | }, 13 | "Store": { 14 | "prefix": "store", 15 | "body": ["store ${1:StoreName}.Store {", "\t$0", "}"], 16 | "description": "Stores are global containers of application specific data." 17 | }, 18 | "Store property": { 19 | "prefix": "prop", 20 | "body": ["property ${1:name} : ${2:Number} = ${0:0}"], 21 | "description": "The property keyword when used in a store defines part of the data that the store contains." 22 | }, 23 | "Functions": { 24 | "prefix": "fun", 25 | "body": [ 26 | "fun ${1:name}(${2:object} : ${3:String}) : ${4:Void} {", 27 | "\t$0", 28 | "}" 29 | ], 30 | "description": "Functions can be defined on modules, components, stores and providers." 31 | }, 32 | "Next Call": { 33 | "prefix": "next", 34 | "body": ["next { state | ${1:name} = ${1:name} + 1 }"], 35 | "description": "Functions can be defined on modules, components, stores and providers." 36 | }, 37 | "Render": { 38 | "prefix": "render", 39 | "body": ["fun render : Html {", "\t$0", "}"], 40 | "description": "The render function renders the component into an HTML tree." 41 | }, 42 | "HTML Elements": { 43 | "prefix": "div", 44 | "body": ["<${1:div}::${2:stylename}>", "\t$0", ""], 45 | "description": "HTML elements can be written as in standard HTML." 46 | }, 47 | "Span Element": { 48 | "prefix": "span", 49 | "body": ["<${1:span}::${2:stylename}>", "\t$0", ""], 50 | "description": "HTML elements can be written as in standard HTML." 51 | }, 52 | "Paragraph Element": { 53 | "prefix": "p", 54 | "body": ["<${1:p}::${2:stylename}>", "\t$0", ""], 55 | "description": "HTML elements can be written as in standard HTML." 56 | }, 57 | "Title Element": { 58 | "prefix": "h", 59 | "body": ["<${1:h1}::${2:stylename}>", "\t$0", ""], 60 | "description": "HTML elements can be written as in standard HTML." 61 | }, 62 | "Button Element": { 63 | "prefix": "button", 64 | "body": [ 65 | "<${1:button}::${2:stylename} ${3:disabled}={${4:disabled}}>", 66 | "\t<{ \"${5:Text}\" }>", 67 | "" 68 | ], 69 | "description": "HTML elements can be written as in standard HTML." 70 | }, 71 | "HTML Expressions": { 72 | "prefix": "exp", 73 | "body": ["<{ \"${Message}\" }>"], 74 | "description": "HTML Expressions allows inserting data into HTML elements or components." 75 | }, 76 | "Attributes": { 77 | "prefix": "att", 78 | "body": ["{${disabled}}"], 79 | "description": "Attributes are either strings or expressions." 80 | }, 81 | "Modules": { 82 | "prefix": "mod", 83 | "body": ["module ${1:ModuleName} {", "\t$0", "}"], 84 | "description": "Modules are kind of containers for a set of relatable functions, usually used to gather functions that relate to a specific type." 85 | }, 86 | "Components": { 87 | "prefix": "com", 88 | "body": ["component ${1:ComponentName} {", "\t$0", "}"], 89 | "description": "Components are reusable pieces of code that have specific behavior, styles and content." 90 | }, 91 | "Components Tags": { 92 | "prefix": "tag", 93 | "body": ["<${1:Component} ${2:attribute}={${3:value}}/>"], 94 | "description": "Tags that have the name of a component will render that component at the point where the tag is defined." 95 | }, 96 | "Events": { 97 | "prefix": "event", 98 | "body": ["${onEvent}={\\event : Html.Event => ${handler}()}"], 99 | "description": "All event handlers are functions, they take an event record and return Void." 100 | }, 101 | "Connect": { 102 | "prefix": "con", 103 | "body": ["connect ${1:StoreName}.Store exposing { $0 }"], 104 | "description": "The connect directive lets you connect a component to a store which allows you to call the stores functions and properties without using the stores name." 105 | }, 106 | "Style": { 107 | "prefix": "sty", 108 | "body": ["style ${1:base} {", "\t${2:attribute}: ${3:value};", "}"], 109 | "description": "Styles define with CSS how an HTML element looks." 110 | }, 111 | "Computed Properties": { 112 | "prefix": "get", 113 | "body": ["get ${1:computedProperty} : ${2:String} {", "\t$0", "}"], 114 | "description": "Computed properties works like regular properties but instead of returning a constant value it can return different values base on the state and the properties." 115 | }, 116 | "If": { 117 | "prefix": "if", 118 | "body": [ 119 | "if (${1:condition}) {", 120 | "\t${2:value}", 121 | "} else {", 122 | "\t${3:otherValue}", 123 | "}" 124 | ], 125 | "description": "The if...else conditional expression can return two different values based on a condition." 126 | }, 127 | "Case": { 128 | "prefix": "case", 129 | "body": [ 130 | "case (${1:condition}) {", 131 | "\t${2:match1} => ${3:value1}", 132 | "\t${4:match3} => ${5:value3}", 133 | "\t=> ${6:defaultValue}", 134 | "}" 135 | ], 136 | "description": "A case expression is useful for matching enums or exact values, while also supporting a default value." 137 | }, 138 | "Record": { 139 | "prefix": "record", 140 | "body": [ 141 | "record ${1:User} {", 142 | "\t${2:name} : ${3:String},", 143 | "\t${4:email} : ${5:String}", 144 | "}" 145 | ], 146 | "description": "Records are object like data structures, that have a fix set of keys and values." 147 | }, 148 | "Try": { 149 | "prefix": "try", 150 | "body": [ 151 | "try {", 152 | "\t$1", 153 | "} catch ${2:Json}.Error => error {", 154 | "\t$3", 155 | "}" 156 | ], 157 | "description": "try is a control expression for handling synchronous computations that might fail, for example when you are trying to convert an untyped JavaScript object into a typed Record." 158 | }, 159 | "Do": { 160 | "prefix": "do", 161 | "body": ["do {", "\t$1", "} catch ${2:Http}.Error => error {", "\t$3", "}"], 162 | "description": "do expressions are for two things: handling asynchronous computations that might fail, for example when loading something with a request, or executing asynchronous expressions sequentially." 163 | }, 164 | "Routes": { 165 | "prefix": "routes", 166 | "body": [ 167 | "routes {", 168 | "\t/ {", 169 | "\t\t${1:Application.setPage(\"index\")}", 170 | "\t}", 171 | "\t/${2:user}/:${3:id} {", 172 | "\t\t do {", 173 | "\t\t\t${4:Application.setPage(\"show\")}", 174 | "\t\t\t${5:Application.loadUser(id)}", 175 | "\t\t}", 176 | "\t}", 177 | "}" 178 | ], 179 | "description": "In Mint routes of an application are defined at the top level with the routes block." 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import { runMintCommandAsTask, promiseSeconds } from "./utils"; 2 | import * as vscode from "vscode"; 3 | 4 | export function mintBuildCommand() { 5 | runMintCommandAsTask("build", "Build production bundle"); 6 | } 7 | 8 | export function mintCompileCommand() { 9 | runMintCommandAsTask( 10 | "compile", 11 | "Compile project into single JavaScript file" 12 | ); 13 | } 14 | 15 | export function mintDocsCommand() { 16 | runMintCommandAsTask("docs", "Start documentation server"); 17 | } 18 | 19 | export function mintFormatAllCommand() { 20 | runMintCommandAsTask("format", "Format all files"); 21 | } 22 | 23 | export async function mintInitCommand() { 24 | const projectName = await vscode.window.showInputBox({ 25 | prompt: "Type the name of your project", 26 | placeHolder: "mint-project", 27 | }); 28 | 29 | const folder = await vscode.window.showOpenDialog({ 30 | canSelectFiles: false, 31 | canSelectFolders: true, 32 | canSelectMany: false, 33 | openLabel: "Create project", 34 | }); 35 | 36 | const newProjectRoot = `${folder[0].path}/${projectName}`; 37 | 38 | await runMintCommandAsTask(`init ${newProjectRoot}`, "Init a new project"); 39 | 40 | await promiseSeconds(2); 41 | 42 | await vscode.commands.executeCommand( 43 | "vscode.openFolder", 44 | vscode.Uri.file(newProjectRoot) 45 | ); 46 | } 47 | 48 | export function mintInstallCommand() { 49 | runMintCommandAsTask("install", "Install dependencies"); 50 | } 51 | 52 | export function mintCountLinesCommand() { 53 | runMintCommandAsTask("loc", "Count lines of code"); 54 | } 55 | 56 | export function mintStartCommand() { 57 | runMintCommandAsTask("start", "Start development server"); 58 | } 59 | 60 | export function mintTestCommand() { 61 | runMintCommandAsTask("test", "Run tests"); 62 | } 63 | 64 | export function mintVersionCommand() { 65 | runMintCommandAsTask("version", "Show current version"); 66 | } 67 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as cmd from "./commands"; 2 | import * as vscode from "vscode"; 3 | import * as fs from "fs"; 4 | 5 | import { LanguageClient } from 'vscode-languageclient'; 6 | import { MintFormattingProvider } from "./formatter"; 7 | 8 | let client: LanguageClient 9 | 10 | export async function activate( 11 | context: vscode.ExtensionContext, 12 | isRestart: boolean = false 13 | ): Promise { 14 | // Set context activated 15 | vscode.commands.executeCommand("setContext", "mint:isActivated", true); 16 | 17 | // Register formatting provider 18 | vscode.languages.registerDocumentFormattingEditProvider( 19 | "mint", 20 | new MintFormattingProvider() 21 | ); 22 | 23 | // Register commands 24 | vscode.commands.registerCommand("mint.build", cmd.mintBuildCommand); 25 | vscode.commands.registerCommand("mint.compile", cmd.mintCompileCommand); 26 | vscode.commands.registerCommand("mint.docs", cmd.mintDocsCommand); 27 | vscode.commands.registerCommand("mint.formatAll", cmd.mintFormatAllCommand); 28 | vscode.commands.registerCommand("mint.init", cmd.mintInitCommand); 29 | vscode.commands.registerCommand("mint.install", cmd.mintInstallCommand); 30 | vscode.commands.registerCommand("mint.loc", cmd.mintCountLinesCommand); 31 | vscode.commands.registerCommand("mint.start", cmd.mintStartCommand); 32 | vscode.commands.registerCommand("mint.test", cmd.mintTestCommand); 33 | vscode.commands.registerCommand("mint.version", cmd.mintVersionCommand); 34 | 35 | const binaryLocation : string = vscode.workspace.getConfiguration('mint.languageServer').get('location') 36 | 37 | if (binaryLocation) { 38 | if (fs.existsSync(binaryLocation)) { 39 | // Create the language client 40 | client = new LanguageClient( 41 | 'mint', 42 | 'Mint Language Server', 43 | { 44 | command: binaryLocation, 45 | args: ['tool', 'ls'], 46 | }, 47 | { 48 | documentSelector: [ 49 | {scheme: 'file', language: 'mint'}, 50 | ] 51 | } 52 | ); 53 | 54 | // Start the client 55 | context.subscriptions.push(client.start()); 56 | } else { 57 | vscode.window.showErrorMessage('Mint binary not found! You specified ' + binaryLocation); 58 | } 59 | } 60 | } 61 | 62 | export async function deactivate(isRestart: boolean = false): Promise { 63 | // Set context deactivated 64 | vscode.commands.executeCommand("setContext", "mint:isActivated", false); 65 | 66 | // Stop the language server client. 67 | if (client) { 68 | client.stop() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/formatter.ts: -------------------------------------------------------------------------------- 1 | import vscode = require("vscode"); 2 | import cp = require("child_process"); 3 | import fs = require("fs"); 4 | 5 | import { getDirtyFile } from "./utils"; 6 | 7 | export class MintFormattingProvider 8 | implements vscode.DocumentFormattingEditProvider { 9 | public provideDocumentFormattingEdits( 10 | document: vscode.TextDocument, 11 | options: vscode.FormattingOptions, 12 | token: vscode.CancellationToken 13 | ): vscode.TextEdit[] | Thenable { 14 | return new Promise((resolve, reject) => { 15 | let file = getDirtyFile(document); 16 | 17 | let res = cp.spawnSync("mint", ["format", file], { 18 | cwd: vscode.workspace.rootPath, 19 | }); 20 | 21 | if (res.status !== 0) { 22 | reject(res.error); 23 | } else { 24 | if (!fs.existsSync(file)) { 25 | reject(file + " file not found"); 26 | } else { 27 | let content = fs.readFileSync(file, "utf-8"); 28 | let range = document.validateRange( 29 | new vscode.Range( 30 | new vscode.Position(0, 0), 31 | new vscode.Position(1000000, 1000000) 32 | ) 33 | ); 34 | resolve([vscode.TextEdit.replace(range, content)]); 35 | } 36 | } 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import fs = require("fs"); 2 | import path = require("path"); 3 | import os = require("os"); 4 | import vscode = require("vscode"); 5 | 6 | /** 7 | * Returns temporary file path of edited document. 8 | */ 9 | export function getDirtyFile(document: vscode.TextDocument): string { 10 | var dirtyFilePath = path.normalize( 11 | path.join(os.tmpdir(), "vscodemintdirty.mint") 12 | ); 13 | fs.writeFileSync(dirtyFilePath, document.getText()); 14 | return dirtyFilePath; 15 | } 16 | 17 | export function createAndShowOutputWindow(): vscode.OutputChannel { 18 | var channel = vscode.window.createOutputChannel("mint"); 19 | channel.show(); 20 | return channel; 21 | } 22 | /** 23 | * Run a mint subcommand as a VSCode task, ie `mint format` 24 | * 25 | * @param subcommand The mint subcommand to run, ie `format` 26 | * @param description The VSCode description to show, ie "Format all files" 27 | */ 28 | export function runMintCommandAsTask( 29 | subcommand: string, 30 | description: string 31 | ): Thenable { 32 | return vscode.tasks.executeTask( 33 | new vscode.Task( 34 | { command: "", type: "" }, 35 | vscode.TaskScope.Workspace, 36 | description, 37 | "mint", 38 | new vscode.ShellExecution(`mint ${subcommand}`) 39 | ) 40 | ); 41 | } 42 | 43 | /** 44 | * Wait for a number of seconds 45 | * 46 | * @param seconds The number of seconds to wait before completing 47 | */ 48 | export function promiseSeconds(seconds: number): Thenable { 49 | return new Promise((res) => { 50 | setTimeout(res, 1000 * seconds); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /syntaxes/mint.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Mint", 4 | "scopeName": "source.mint", 5 | "patterns": [ 6 | { 7 | "include": "#comments" 8 | }, 9 | { 10 | "include": "#html" 11 | }, 12 | { 13 | "include": "#regex" 14 | }, 15 | { 16 | "include": "#style" 17 | }, 18 | { 19 | "include": "#js" 20 | }, 21 | { 22 | "include": "#routes" 23 | }, 24 | { 25 | "include": "#keywords" 26 | }, 27 | { 28 | "include": "#strings" 29 | }, 30 | { 31 | "include": "#directives" 32 | } 33 | ], 34 | "repository": { 35 | "comments": { 36 | "patterns": [ 37 | { 38 | "name": "comment.block.mint", 39 | "begin": "/\\*", 40 | "end": "\\*/" 41 | }, 42 | { 43 | "match": "((//).*)$", 44 | "captures": { 45 | "1": { 46 | "name": "comment.line.double-slash.mint" 47 | } 48 | } 49 | } 50 | ] 51 | }, 52 | "css": { 53 | "patterns": [ 54 | { 55 | "match": "//", 56 | "comment": "invalidate as css comment block" 57 | }, 58 | { 59 | "include": "#style-nesting" 60 | }, 61 | { 62 | "include": "source.css#pseudo-classes" 63 | }, 64 | { 65 | "include": "source.css#pseudo-elements" 66 | }, 67 | { 68 | "include": "source.css.scss#general" 69 | }, 70 | { 71 | "include": "source.css.scss#selectors" 72 | }, 73 | { 74 | "include": "source.css.scss#properties" 75 | }, 76 | { 77 | "include": "source.css.scss#at_rule_import" 78 | }, 79 | { 80 | "include": "source.css.scss#at_rule_media" 81 | }, 82 | { 83 | "include": "source.css.scss#at_rule_charset" 84 | }, 85 | { 86 | "include": "source.css.scss#at_rule_namespace" 87 | }, 88 | { 89 | "include": "source.css.scss#at_rule_fontface" 90 | }, 91 | { 92 | "include": "source.css.scss#at_rule_page" 93 | }, 94 | { 95 | "include": "source.css.scss#at_rule_supports" 96 | }, 97 | { 98 | "begin": "(?<=^|\\s)(@)(?:-(?:webkit|moz)-)?keyframes\\b", 99 | "beginCaptures": { 100 | "0": { 101 | "name": "keyword.control.at-rule.keyframes.scss" 102 | }, 103 | "1": { 104 | "name": "punctuation.definition.keyword.scss" 105 | } 106 | }, 107 | "end": "(?<=})", 108 | "name": "meta.at-rule.keyframes.scss", 109 | "patterns": [ 110 | { 111 | "match": "(?<=@keyframes)\\s+((?:[_A-Za-z][-\\w]|-[_A-Za-z])[-\\w]*)", 112 | "captures": { 113 | "1": { 114 | "name": "entity.name.function.scss" 115 | } 116 | } 117 | }, 118 | { 119 | "begin": "(?<=@keyframes)\\s+(\")", 120 | "beginCaptures": { 121 | "1": { 122 | "name": "punctuation.definition.string.begin.scss" 123 | } 124 | }, 125 | "end": "\"", 126 | "endCaptures": { 127 | "0": { 128 | "name": "punctuation.definition.string.end.scss" 129 | } 130 | }, 131 | "name": "string.quoted.double.scss", 132 | "contentName": "entity.name.function.scss", 133 | "patterns": [ 134 | { 135 | "match": "\\\\(\\h{1,6}|.)", 136 | "name": "constant.character.escape.scss" 137 | }, 138 | { 139 | "include": "source.css.scss#interpolation" 140 | } 141 | ] 142 | }, 143 | { 144 | "begin": "(?<=@keyframes)\\s+(')", 145 | "beginCaptures": { 146 | "1": { 147 | "name": "punctuation.definition.string.begin.scss" 148 | } 149 | }, 150 | "end": "'", 151 | "endCaptures": { 152 | "0": { 153 | "name": "punctuation.definition.string.end.scss" 154 | } 155 | }, 156 | "name": "string.quoted.single.scss", 157 | "contentName": "entity.name.function.scss", 158 | "patterns": [ 159 | { 160 | "match": "\\\\(\\h{1,6}|.)", 161 | "name": "constant.character.escape.scss" 162 | }, 163 | { 164 | "include": "source.css.scss#interpolation" 165 | } 166 | ] 167 | }, 168 | { 169 | "begin": "{", 170 | "beginCaptures": { 171 | "0": { 172 | "name": "punctuation.section.keyframes.begin.scss" 173 | } 174 | }, 175 | "end": "}", 176 | "endCaptures": { 177 | "0": { 178 | "name": "punctuation.section.keyframes.end.scss" 179 | } 180 | }, 181 | "patterns": [ 182 | { 183 | "include": "#comments" 184 | }, 185 | { 186 | "match": "\\b(?:(?:100|[1-9]\\d|\\d)%|from|to)(?=\\s*{)", 187 | "name": "entity.other.attribute-name.scss" 188 | }, 189 | { 190 | "include": "#style-nesting" 191 | } 192 | ] 193 | } 194 | ] 195 | } 196 | ] 197 | }, 198 | "directives": { 199 | "begin": "(@(svg|format|documentation))((\\().*\\))?", 200 | "beginCaptures": { 201 | "0": { 202 | "name": "keyword.directive.mint", 203 | "patterns": [ 204 | { 205 | "match": "(?<=@svg\\()[^\\)]*", 206 | "name": "string.unquoted.mint" 207 | }, 208 | { 209 | "match": "(?<=@documentation\\()[^\\)]*", 210 | "name": "entity.name.class.mint" 211 | } 212 | ] 213 | } 214 | }, 215 | "end": "\\G" 216 | }, 217 | "html": { 218 | "begin": "<{?", 219 | "beginCaptures": { 220 | "0": { 221 | "name": "punctuation.definition.tag.html.mint" 222 | } 223 | }, 224 | "end": "}?>", 225 | "endCaptures": { 226 | "0": { 227 | "name": "punctuation.definition.tag.html.mint" 228 | } 229 | }, 230 | "name": "meta.tag.html.mint", 231 | "patterns": [ 232 | { 233 | "match": "(?<=<)/|/(?=>)", 234 | "name": "punctuation.definition.tag.html.mint" 235 | }, 236 | { 237 | "match": "(?<=<||/]*", 238 | "name": "entity.name.tag.block.any.html.mint" 239 | }, 240 | { 241 | "match": "(?![A-Z][a-z]*)(?<=::)[^\\s|>|::|\\(|/]*", 242 | "name": "entity.name.tag.css.mint" 243 | }, 244 | { 245 | "include": "#html" 246 | }, 247 | { 248 | "include": "#directives" 249 | }, 250 | { 251 | "include": "#regex" 252 | }, 253 | { 254 | "include": "#js" 255 | }, 256 | { 257 | "include": "#strings" 258 | }, 259 | { 260 | "include": "#keywords" 261 | } 262 | ] 263 | }, 264 | "interpolation": { 265 | "begin": "#{", 266 | "beginCaptures": { 267 | "0": { 268 | "name": "punctuation.definition.template-expression.begin.mint" 269 | } 270 | }, 271 | "end": "}", 272 | "endCaptures": { 273 | "0": { 274 | "name": "punctuation.definition.template-expression.end.mint" 275 | } 276 | }, 277 | "name": "meta.embedded.line.mint", 278 | "patterns": [ 279 | { 280 | "name": "punctuation.accessor.mint", 281 | "match": "\\." 282 | }, 283 | { 284 | "include": "#keywords" 285 | } 286 | ] 287 | }, 288 | "js": { 289 | "begin": "`", 290 | "end": "`", 291 | "patterns": [ 292 | { 293 | "include": "source.js" 294 | } 295 | ], 296 | "name": "meta.embedded.block.js.mint" 297 | }, 298 | "keywords": { 299 | "patterns": [ 300 | { 301 | "match": "(?", 314 | "name": "storage.type.function.arrow.mint" 315 | }, 316 | { 317 | "match": "(?=?|<=?(?=\\s|\\d|\\w)", 362 | "name": "keyword.operator.relational.mint" 363 | }, 364 | { 365 | "match": "(?", 378 | "name": "keyword.operator.pipe.mint" 379 | }, 380 | { 381 | "match": "\\|(?!>)", 382 | "name": "keyword.operator.copy.mint" 383 | }, 384 | { 385 | "match": "\\b(true|false)\\b", 386 | "name": "constant.language.boolean.mint" 387 | }, 388 | { 389 | "match": "(?' 118 | endCaptures: 119 | '0': 120 | name: punctuation.definition.tag.html.mint 121 | name: meta.tag.html.mint 122 | patterns: 123 | - match: '(?<=<)/|/(?=>)' 124 | name: punctuation.definition.tag.html.mint 125 | - match: "(?<=<||/]*" 126 | name: entity.name.tag.block.any.html.mint 127 | - match: "(?![A-Z][a-z]*)(?<=::)[^\\s|>|::|\\(|/]*" 128 | name: entity.name.tag.css.mint 129 | - include: '#html' 130 | - include: '#directives' 131 | - include: '#regex' 132 | - include: '#js' 133 | - include: '#strings' 134 | - include: '#keywords' 135 | 136 | interpolation: 137 | begin: '#{' 138 | beginCaptures: 139 | '0': 140 | name: punctuation.definition.template-expression.begin.mint 141 | end: '}' 142 | endCaptures: 143 | '0': 144 | name: punctuation.definition.template-expression.end.mint 145 | name: meta.embedded.line.mint 146 | patterns: 147 | - name: punctuation.accessor.mint 148 | match: "\\." 149 | - include: '#keywords' 150 | 151 | js: 152 | begin: '`' 153 | end: '`' 154 | patterns: 155 | - include: source.js 156 | name: meta.embedded.block.js.mint 157 | 158 | keywords: 159 | patterns: 160 | - match: "(?' 167 | name: storage.type.function.arrow.mint 168 | - match: "(?=?|<=?(?=\\s|\\d|\\w)" 191 | name: keyword.operator.relational.mint 192 | - match: '(?" 199 | name: keyword.operator.pipe.mint 200 | - match: "\\|(?!>)" 201 | name: keyword.operator.copy.mint 202 | - match: "\\b(true|false)\\b" 203 | name: constant.language.boolean.mint 204 | - match: "(?