├── src ├── types │ └── component-meta.ts ├── logger.ts ├── extension.ts ├── component-metadata.ts ├── grammar.ts └── completion-providers.ts ├── .npmrc ├── .eslintrc.json ├── images ├── mdc.png ├── icon.png └── code-folding.gif ├── .gitignore ├── tsconfig.test.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── renovate.json ├── .vscodeignore ├── .editorconfig ├── scripts └── build.ts ├── tsconfig.json ├── snippets └── markdown.code-snippets ├── .github └── workflows │ └── deploy.yml ├── test ├── suite │ ├── index.ts │ └── extension.test.ts └── runTest.ts ├── LICENSE ├── language-configuration.json ├── example.md ├── package.json ├── CHANGELOG.md ├── README.md └── syntaxes ├── mdc.tmLanguage.json └── mdc.standalone.tmLanguage.json /src/types/component-meta.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | node-linker=hoisted 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@nuxtjs/typescript" 3 | } 4 | -------------------------------------------------------------------------------- /images/mdc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vscode-mdc/HEAD/images/mdc.png -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vscode-mdc/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/code-folding.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vscode-mdc/HEAD/images/code-folding.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | .vscode-test-web 7 | .env 8 | 9 | # OSX files 10 | .DS_Store 11 | 12 | out-test/ -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "out-test", 5 | "rootDir": "." 6 | }, 7 | "include": [ 8 | "test" 9 | ] 10 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@nuxtjs" 4 | ], 5 | "packageRules": [ 6 | { 7 | "matchPackageNames": ["vscode", "@types/vscode"], 8 | "enabled": false 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | 5 | src/** 6 | .gitignore 7 | .yarnrc 8 | vsc-extension-quickstart.md 9 | **/tsconfig.json 10 | **/.eslintrc.json 11 | **/*.map 12 | **/*.ts 13 | example.md 14 | scritps/** 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /scripts/build.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { grammar } from '../src/grammar' 3 | 4 | console.log('Start build') 5 | 6 | fs.writeFileSync('syntaxes/mdc.tmLanguage.json', JSON.stringify({ 7 | $schema: 'https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json', 8 | ...grammar 9 | }, null, 2), 'utf-8') 10 | 11 | console.log('Write syntaxes/mdc.tmLanguage.json') 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "rootDir": "./src", 6 | "outDir": "out", 7 | "lib": [ 8 | "es6" 9 | ], 10 | "sourceMap": true, 11 | "strict": true, 12 | "skipLibCheck": true, 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | ".vscode-test", 17 | "scripts", 18 | "**/*.spec.ts", 19 | "test/**/*" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/dist/**/*.js" 14 | ], 15 | "preLaunchTask": "npm: dev" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /snippets/markdown.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Insert span text": { 3 | "prefix": "span", 4 | "body": "[${1:${TM_SELECTED_TEXT}}]$0", 5 | "description": "Insert span text" 6 | }, 7 | "Insert Simple Block": { 8 | "prefix": ["block", "::"], 9 | "body": ["::$1", "${0:${TM_SELECTED_TEXT}}", "::"], 10 | "description": "Insert Simple Block" 11 | }, 12 | "Insert Data Block": { 13 | "prefix": ["block", "::-"], 14 | "body": ["::$1", "---", "$2","---","${0:${TM_SELECTED_TEXT}}","::"], 15 | "description": "Insert Data Block" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.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": "dev", 9 | "isBackground": true, 10 | "presentation": { 11 | "reveal": "never" 12 | }, 13 | "problemMatcher": [ 14 | { 15 | "base": "$ts-webpack-watch", 16 | "background": { 17 | "activeOnStart": true, 18 | "beginsPattern": "Start build", 19 | "endsPattern": "Write syntaxes/mdc.tmLanguage.json" 20 | } 21 | } 22 | ], 23 | "group": "build" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | 7 | jobs: 8 | ci: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: lts/* 16 | 17 | - name: pnpm setup 18 | uses: pnpm/action-setup@v2 19 | 20 | - run: pnpm install 21 | 22 | - name: Generate extension 23 | run: pnpm generate 24 | 25 | - name: Publish to Visual Studio Marketplace 26 | uses: HaaLeo/publish-vscode-extension@v1 27 | with: 28 | pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} 29 | registryUrl: https://marketplace.visualstudio.com 30 | 31 | - name: Publish to Open VSX Marketplace 32 | uses: HaaLeo/publish-vscode-extension@v1 33 | with: 34 | pat: ${{ secrets.OPEN_VSX_TOKEN }} 35 | -------------------------------------------------------------------------------- /test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import { glob } from 'glob'; 4 | 5 | export async 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 | // Use the Promise-based glob API 15 | const files = await glob('**/*.test.js', { cwd: testsRoot }); 16 | // Add files to the test suite 17 | files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f))); 18 | 19 | return new Promise((resolve, reject) => { 20 | try { 21 | // Run the mocha test 22 | mocha.run((failures: number) => { 23 | if (failures > 0) { 24 | reject(new Error(`${failures} tests failed.`)); 25 | } else { 26 | resolve(); 27 | } 28 | }); 29 | } catch (err) { 30 | reject(err); 31 | } 32 | }); 33 | } -------------------------------------------------------------------------------- /test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import * as vscode from 'vscode'; 3 | 4 | async function activateMdcLanguage() { 5 | const doc = await vscode.workspace.openTextDocument({ language: 'mdc', content: '' }); 6 | await vscode.window.showTextDocument(doc); 7 | } 8 | 9 | suite('MDC Extension Test Suite', () => { 10 | test('Extension should be present', async () => { 11 | await activateMdcLanguage(); 12 | const ext = vscode.extensions.getExtension('nuxt.mdc'); 13 | assert.ok(ext, 'Extension nuxt.mdc should be present'); 14 | await ext?.activate(); 15 | }); 16 | 17 | test('mdc language should be registered', async () => { 18 | await activateMdcLanguage(); 19 | const ext = vscode.extensions.getExtension('nuxt.mdc'); 20 | await ext?.activate(); 21 | const languages = await vscode.languages.getLanguages(); 22 | assert.ok(languages.includes('mdc'), 'mdc language should be registered'); 23 | }); 24 | 25 | test('Sample test', () => { 26 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 27 | }); 28 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2022 NuxtLabs 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. -------------------------------------------------------------------------------- /test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as cp from 'child_process'; 3 | import { 4 | downloadAndUnzipVSCode, 5 | resolveCliArgsFromVSCodeExecutablePath, 6 | runTests 7 | } from '@vscode/test-electron'; 8 | 9 | async function main() { 10 | try { 11 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 12 | const extensionTestsPath = path.resolve(__dirname, './suite/index.js'); 13 | const vsixPath = path.resolve(__dirname, '../../../mdc-0.3.3.vsix'); 14 | const vscodeExecutablePath = await downloadAndUnzipVSCode(); 15 | const [cliPath, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); 16 | 17 | // Install the VSIX extension 18 | cp.spawnSync( 19 | cliPath, 20 | [...args, '--install-extension', vsixPath], 21 | { 22 | encoding: 'utf-8', 23 | stdio: 'inherit' 24 | } 25 | ); 26 | 27 | // Run the extension test 28 | await runTests({ 29 | vscodeExecutablePath, 30 | extensionDevelopmentPath, 31 | extensionTestsPath 32 | }); 33 | } catch (err) { 34 | console.error('Failed to run tests'); 35 | process.exit(1); 36 | } 37 | } 38 | 39 | main(); -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 4 | "blockComment": [ 5 | "" 7 | ] 8 | }, 9 | // symbols used as brackets 10 | "brackets": [ 11 | ["{", "}"], 12 | ["[", "]"], 13 | ["(", ")"] 14 | ], 15 | "autoClosingPairs": [ 16 | { 17 | "open": "{", 18 | "close": "}" 19 | }, 20 | { 21 | "open": "[", 22 | "close": "]" 23 | }, 24 | { 25 | "open": "(", 26 | "close": ")" 27 | }, 28 | { 29 | "open": "<", 30 | "close": ">", 31 | "notIn": [ 32 | "string" 33 | ] 34 | } 35 | ], 36 | "surroundingPairs": [ 37 | ["(", ")"], 38 | ["[", "]"], 39 | ["`", "`"], 40 | ["_", "_"], 41 | ["*", "*"], 42 | ["{", "}"], 43 | ["'", "'"], 44 | ["\"", "\""] 45 | ], 46 | "folding": { 47 | "offSide": true, 48 | "markers": { 49 | "start": "^\\s*", 50 | "end": "^\\s*" 51 | } 52 | }, 53 | "wordPattern": { "pattern": "(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})(((\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})|[_])?(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark}))*", "flags": "ug" }, 54 | } 55 | -------------------------------------------------------------------------------- /example.md: -------------------------------------------------------------------------------- 1 | # Hello 2 | 3 | _Hello_ **World** from [`vscode-mdc`](https://github.com/nuxtlabs/vscode-mdc) 4 | 5 | > This file contains the basic Markdown Components syntax 6 | 7 | ::hero 8 | :::card 9 | A nested card 10 | 11 | ::::card { title="Card title" .red} 12 | A **super** nested card 13 | :::: 14 | ::: 15 | :: 16 | 17 | ::hero 18 | Default slot text 19 | 20 | #description 21 | This will be rendered inside the `description` slot. 22 | :: 23 | 24 | - [x] List A 25 | - [ ] List B 26 | - [ ] List B.1 27 | 28 | ![](/af-logo-animated.svg){.w-30.mt--10.mb-5} 29 | 30 | ## Hello{.text-red} 31 | 32 | Hello World{class="text-green text-xl"} 33 | 34 | [Link](https://nuxt.com){class="nuxt"} 35 | 36 | ![Nuxt Logo](https://nuxt.com/assets/design-kit/logo/icon-green.svg){#nuxt-logo} 37 | 38 | `code`{style="color: red"} 39 | 40 | _italic_{style="color: blue"} 41 | 42 | **bold**{style="color: blue"} 43 | 44 | ```bash 45 | npm i nuxt 46 | ``` 47 | 48 | ```ts 49 | import MarkdownIt from 'markdown-it' 50 | import mdc from 'markdown-it-mdc' 51 | 52 | // :warning: this line should not be transformed 53 | const md = new MarkdownIt() 54 | .use(mdc) 55 | ``` 56 | 57 | 58 | 59 | 60 | Nested 61 | 62 | 63 | 68 | 69 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | let outputChannel: vscode.OutputChannel | null = null 4 | 5 | /** 6 | * Ensures that the output channel exists, creating it if necessary. 7 | * The output channel is used for logging messages from the MDC (Markdown Components) extension. 8 | * 9 | * @returns {vscode.OutputChannel} The existing or newly created output channel 10 | * @singleton Maintains a single instance of the output channel 11 | */ 12 | export function ensureOutputChannel (_outputChannel?: vscode.OutputChannel | null): vscode.OutputChannel { 13 | if (!outputChannel) { 14 | _outputChannel = vscode.window.createOutputChannel('MDC - Markdown Components') 15 | outputChannel = _outputChannel 16 | } 17 | return outputChannel 18 | } 19 | 20 | /** 21 | * Logs a message to the output channel if debug is enabled or force is true 22 | * @param message - The message to log 23 | * @param force - If true, shows the output channel and logs the message regardless of debug setting. Defaults to false. 24 | * @returns void 25 | */ 26 | export function logger (message: string, type: 'info' | 'error' = 'info', force = false): void { 27 | const channel = ensureOutputChannel() 28 | const config = vscode.workspace.getConfiguration('mdc') 29 | if (config.get('debug') || force) { 30 | const timestamp = new Date().toISOString() 31 | channel.appendLine(`${timestamp} [${type}]: ${message}`) 32 | if (force) { 33 | channel.show(true) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdc", 3 | "packageManager": "pnpm@9.15.6", 4 | "displayName": "MDC - Markdown Components", 5 | "description": "Provides syntax highlighting and colon matching for MDC (Markdown Components) files for Nuxt Content.", 6 | "version": "0.4.2", 7 | "publisher": "Nuxt", 8 | "icon": "images/icon.png", 9 | "repository": { 10 | "url": "https://github.com/nuxtlabs/vscode-mdc", 11 | "type": "git" 12 | }, 13 | "homepage": "https://github.com/nuxtlabs/vscode-mdc/blob/main/README.md", 14 | "license": "MIT", 15 | "engines": { 16 | "vscode": "^1.75.0" 17 | }, 18 | "categories": [ 19 | "Programming Languages", 20 | "Formatters", 21 | "Snippets" 22 | ], 23 | "main": "./out/extension.js", 24 | "contributes": { 25 | "languages": [ 26 | { 27 | "id": "mdc", 28 | "aliases": [ 29 | "Markdown Components", 30 | "MDC", 31 | "mdc", 32 | "Nuxt Content", 33 | "Nuxt MDC", 34 | "Nuxt Markdown Components" 35 | ], 36 | "filenamePatterns": [ 37 | "*.md", 38 | "*.mdc" 39 | ], 40 | "icon": { 41 | "dark": "./images/mdc.png", 42 | "light": "./images/mdc.png" 43 | }, 44 | "configuration": "./language-configuration.json", 45 | "extensions": [ 46 | ".mdc" 47 | ] 48 | } 49 | ], 50 | "grammars": [ 51 | { 52 | "language": "mdc", 53 | "scopeName": "text.markdown.mdc.standalone", 54 | "path": "./syntaxes/mdc.standalone.tmLanguage.json" 55 | }, 56 | { 57 | "scopeName": "text.markdown.mdc", 58 | "path": "./syntaxes/mdc.tmLanguage.json", 59 | "injectTo": [ 60 | "text.html.markdown", 61 | "text.html.markdown.jsx" 62 | ] 63 | } 64 | ], 65 | "menus": { 66 | "editor/title": [ 67 | { 68 | "command": "markdown.showPreviewToSide", 69 | "when": "editorLangId == mdc && !notebookEditorFocused && !hasCustomMarkdownPreview", 70 | "alt": "markdown.showPreview", 71 | "group": "navigation" 72 | } 73 | ], 74 | "explorer/context": [ 75 | { 76 | "command": "markdown.showPreview", 77 | "when": "resourceLangId == mdc && !hasCustomMarkdownPreview", 78 | "group": "navigation" 79 | }, 80 | { 81 | "command": "markdown.findAllFileReferences", 82 | "when": "resourceLangId == mdc", 83 | "group": "4_search" 84 | } 85 | ], 86 | "editor/title/context": [ 87 | { 88 | "command": "markdown.showPreview", 89 | "when": "resourceLangId == mdc && !hasCustomMarkdownPreview", 90 | "group": "1_open" 91 | }, 92 | { 93 | "command": "markdown.findAllFileReferences", 94 | "when": "resourceLangId == mdc" 95 | } 96 | ], 97 | "commandPalette": [ 98 | { 99 | "command": "markdown.showPreview", 100 | "when": "editorLangId == mdc && !notebookEditorFocused", 101 | "group": "navigation" 102 | }, 103 | { 104 | "command": "markdown.showPreviewToSide", 105 | "when": "editorLangId == mdc && !notebookEditorFocused", 106 | "group": "navigation" 107 | }, 108 | { 109 | "command": "markdown.showLockedPreviewToSide", 110 | "when": "editorLangId == mdc && !notebookEditorFocused", 111 | "group": "navigation" 112 | }, 113 | { 114 | "command": "markdown.showPreviewSecuritySelector", 115 | "when": "editorLangId == mdc && !notebookEditorFocused" 116 | }, 117 | { 118 | "command": "markdown.preview.refresh", 119 | "when": "editorLangId == mdc && !notebookEditorFocused" 120 | }, 121 | { 122 | "command": "markdown.findAllFileReferences", 123 | "when": "editorLangId == mdc" 124 | } 125 | ] 126 | }, 127 | "keybindings": [ 128 | { 129 | "command": "markdown.showPreview", 130 | "key": "shift+ctrl+v", 131 | "mac": "shift+cmd+v", 132 | "when": "editorLangId == mdc && !notebookEditorFocused" 133 | }, 134 | { 135 | "command": "markdown.showPreviewToSide", 136 | "key": "ctrl+k v", 137 | "mac": "cmd+k v", 138 | "when": "editorLangId == mdc && !notebookEditorFocused" 139 | } 140 | ], 141 | "snippets": [ 142 | { 143 | "language": "mdc", 144 | "path": "./snippets/markdown.code-snippets" 145 | } 146 | ], 147 | "configuration": { 148 | "title": "MDC - Markdown Components", 149 | "properties": { 150 | "mdc.enableFormatting": { 151 | "type": "boolean", 152 | "default": false, 153 | "description": "Enable MDC document formatting." 154 | }, 155 | "mdc.enableComponentMetadataCompletions": { 156 | "type": "boolean", 157 | "default": false, 158 | "description": "Enable MDC component name and prop auto-completions." 159 | }, 160 | "mdc.componentMetadataLocalFilePattern": { 161 | "type": "string", 162 | "default": "**/.nuxt/component-meta.mjs", 163 | "description": "A glob pattern to the local MDC component metadata. Defaults to: `**/.nuxt/component-meta.mjs`" 164 | }, 165 | "mdc.componentMetadataLocalExcludePattern": { 166 | "type": "string", 167 | "default": "{**/node_modules/**,**/dist/**,**/.output/**,**/.cache/**,**/.playground/**}", 168 | "description": "A glob pattern to exclude directories from the local MDC component metadata search. Defaults to: `{**/node_modules/**,**/dist/**,**/.output/**,**/.cache/**,**/.playground/**}`" 169 | }, 170 | "mdc.componentMetadataURL": { 171 | "type": "string", 172 | "default": "", 173 | "description": "A remote or localhost URL from which to fetch the MDC completion metadata. The endpoint must return valid JSON that matches one of the documented response interfaces. When set, will take precedence over the `mdc.componentMetadataLocalFilePattern` configuration." 174 | }, 175 | "mdc.componentMetadataCacheTTL": { 176 | "type": "number", 177 | "default": 30, 178 | "description": "The number of minutes to cache the MDC metadata. Defaults to 30 minutes." 179 | }, 180 | "mdc.debug": { 181 | "type": "boolean", 182 | "default": false, 183 | "description": "Enable extension debug logging." 184 | } 185 | } 186 | }, 187 | "commands": [ 188 | { 189 | "command": "mdc.showOutput", 190 | "title": "Show extension output", 191 | "category": "MDC" 192 | }, 193 | { 194 | "command": "mdc.refreshMetadata", 195 | "title": "Refresh component metadata", 196 | "category": "MDC" 197 | } 198 | ], 199 | "configurationDefaults": { 200 | "[mdc]": { 201 | "editor.folding": true 202 | } 203 | } 204 | }, 205 | "scripts": { 206 | "vscode:prepublish": "pnpm run build", 207 | "build": "tsc -p ./ && esno scripts/build.ts", 208 | "dev": "tsc -p ./ && esno watch scripts/build.ts", 209 | "lint": "eslint src --ext ts", 210 | "test:compile": "tsc -p tsconfig.test.json", 211 | "test": "pnpm run test:compile && node ./out-test/test/runTest.js", 212 | "release": "release-it", 213 | "generate": "vsce package", 214 | "publish": "vsce publish" 215 | }, 216 | "devDependencies": { 217 | "@nuxt/schema": "^3.15.4", 218 | "@nuxtjs/eslint-config-typescript": "^12.1.0", 219 | "@types/glob": "^8.1.0", 220 | "@types/mocha": "^10.0.10", 221 | "@types/node": "^18.19.70", 222 | "@types/vscode": "1.42.0", 223 | "@vscode/test-electron": "^2.5.2", 224 | "@vscode/vsce": "^2.32.0", 225 | "eslint": "^8.57.1", 226 | "esno": "^0.17.0", 227 | "glob": "^11.0.3", 228 | "mocha": "^11.7.1", 229 | "release-it": "^18.1.2", 230 | "typescript": "5.2.2", 231 | "vscode": "^1.1.37", 232 | "vscode-textmate": "^9.2.0", 233 | "vue-component-meta": "^2.2.4" 234 | }, 235 | "dependencies": { 236 | "@nuxtlabs/monarch-mdc": "^0.8.1", 237 | "scule": "^1.3.0", 238 | "vscode-textmate": "^9.2.0" 239 | }, 240 | "release-it": { 241 | "git": { 242 | "commitMessage": "chore(release): release v${version}" 243 | }, 244 | "github": { 245 | "release": true, 246 | "releaseName": "v${version}" 247 | }, 248 | "npm": { 249 | "publish": false 250 | }, 251 | "hooks": { 252 | "after:bump": "npx changelogen@latest --no-commit --no-tag --output --r $(node -p \"require('./package.json').version\")" 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { formatter, getDocumentFoldingRanges } from '@nuxtlabs/monarch-mdc' 3 | import { getMdcComponentCompletionItemProvider, getMdcComponentPropCompletionItemProvider, createCacheCleanupListeners } from './completion-providers' 4 | import { getComponentMetadata } from './component-metadata' 5 | import { ensureOutputChannel, logger } from './logger' 6 | 7 | let outputChannel: vscode.OutputChannel | null = null 8 | let showOutputCommand: vscode.Disposable | null = null 9 | let refreshMetadata: vscode.Disposable | null = null 10 | let formatters: vscode.Disposable[] = [] 11 | let mdcComponentCompletionProvider: vscode.Disposable | null = null 12 | let mdcComponentPropsCompletionProvider: vscode.Disposable | null = null 13 | 14 | /** 15 | * Formats the entire document using the specified formatter and returns the text edits. 16 | * 17 | * @param {vscode.TextDocument} document - The document to format. 18 | * @param {boolean} isFormatOnType - Whether the formatter is being used for on-type formatting. Defaults to `false`. 19 | * @returns {vscode.TextEdit[]} - An array of `vscode.TextEdit` objects representing the formatting changes. 20 | * 21 | * @remarks 22 | * - Retrieves the tab size from the active editor's options, defaulting to 2 if not set. 23 | * - Formats the entire document text using the `formatter` function. 24 | * - Creates a full document replacement edit with the formatted text. 25 | */ 26 | function getDocumentFormatter (document: vscode.TextDocument, isFormatOnType: boolean = false): vscode.TextEdit[] { 27 | // Get tab size from active editor 28 | const activeEditor = vscode.window.activeTextEditor 29 | const tabSize = Number(activeEditor?.options.tabSize ?? 2) 30 | 31 | // Format the entire document 32 | const text = document.getText() 33 | const formatted = formatter(text, { 34 | tabSize, 35 | isFormatOnType 36 | }) 37 | 38 | // Create a full document replacement edit 39 | const firstLine = document.lineAt(0) 40 | const lastLine = document.lineAt(document.lineCount - 1) 41 | const range = new vscode.Range( 42 | firstLine.range.start, 43 | lastLine.range.end 44 | ) 45 | 46 | return [vscode.TextEdit.replace(range, formatted)] 47 | } 48 | 49 | /** 50 | * Provides folding ranges for a given markdown document. 51 | * 52 | * This function scans through the document to identify custom folding regions 53 | * defined by specific start and end tags (e.g., "::container" and "::"). 54 | * 55 | * @param {vscode.TextDocument} document - The markdown document to provide folding ranges for. 56 | * @returns {vscode.FoldingRange[]} - An array of `vscode.FoldingRange` objects representing the folding regions. 57 | */ 58 | function provideFoldingRanges (document: vscode.TextDocument): vscode.FoldingRange[] { 59 | const documentAdapter = { 60 | getLine: (lineNumber: number) => document.lineAt(lineNumber).text, 61 | lineCount: document.lineCount 62 | } 63 | 64 | const ranges = getDocumentFoldingRanges(documentAdapter) 65 | 66 | return ranges.map(range => 67 | new vscode.FoldingRange(range.start, range.end) 68 | ) 69 | } 70 | 71 | const mdcDocumentSelector: vscode.DocumentSelector = [ 72 | { language: 'mdc', scheme: 'file' }, 73 | { language: 'mdc', scheme: 'untitled' }, 74 | { language: 'mdc', scheme: 'file', pattern: '**/.mdc' } 75 | ] 76 | 77 | export function activate (context: vscode.ExtensionContext) { 78 | try { 79 | // Initialize output channel 80 | outputChannel = ensureOutputChannel(outputChannel) 81 | context.subscriptions.push(outputChannel) 82 | 83 | logger('Activating MDC extension...') 84 | 85 | // Update any dynamic configuration settings 86 | function updateConfiguration () { 87 | // If already registered, dispose of existing command 88 | if (showOutputCommand) { 89 | showOutputCommand.dispose() 90 | } 91 | showOutputCommand = vscode.commands.registerCommand('mdc.showOutput', () => { 92 | ensureOutputChannel(outputChannel).show(true) 93 | }) 94 | // Register show output command 95 | context.subscriptions.push(showOutputCommand) 96 | 97 | // Dispose component completion providers 98 | if (mdcComponentCompletionProvider) { 99 | mdcComponentCompletionProvider.dispose() 100 | } 101 | if (mdcComponentPropsCompletionProvider) { 102 | mdcComponentPropsCompletionProvider.dispose() 103 | } 104 | 105 | // Dispose existing formatters 106 | formatters.forEach(f => f.dispose()) 107 | formatters = [] 108 | 109 | // Retrieve the `mdc` configuration settings 110 | const config = vscode.workspace.getConfiguration('mdc') 111 | const formattingEnabled = config.get('enableFormatting', false) 112 | const componentCompletionsEnabled = config.get('enableComponentMetadataCompletions', false) 113 | 114 | if (formattingEnabled) { 115 | logger('Registering MDC formatters...') 116 | formatters = [ 117 | // Register the document formatting provider 118 | vscode.languages.registerDocumentFormattingEditProvider(mdcDocumentSelector, { 119 | provideDocumentFormattingEdits: (document: vscode.TextDocument) => getDocumentFormatter(document, false) 120 | }), 121 | // Register the format on type provider 122 | vscode.languages.registerOnTypeFormattingEditProvider( 123 | mdcDocumentSelector, 124 | { provideOnTypeFormattingEdits: (document: vscode.TextDocument) => getDocumentFormatter(document, true) }, 125 | '\n' 126 | ) 127 | ] 128 | // Add formatters to subscriptions 129 | context.subscriptions.push(...formatters) 130 | logger('MDC formatters registered.') 131 | } 132 | 133 | if (componentCompletionsEnabled) { 134 | // Add cache cleanup listeners 135 | context.subscriptions.push(createCacheCleanupListeners()) 136 | 137 | // Initialize component name and prop completion providers 138 | getComponentMetadata(true).then(() => { 139 | logger('Initial MDC component metadata fetch completed.') 140 | 141 | mdcComponentCompletionProvider = vscode.languages.registerCompletionItemProvider(mdcDocumentSelector, { 142 | provideCompletionItems: async (document, position) => { 143 | const mdcComponents = await getComponentMetadata() 144 | // If no components, exit early 145 | if (!mdcComponents || !mdcComponents?.length) { 146 | return 147 | } 148 | return getMdcComponentCompletionItemProvider(mdcComponents, { document, position }) 149 | } 150 | }, 151 | ':' // Trigger on colon 152 | ) 153 | 154 | // Register MDC block component completion provider 155 | mdcComponentPropsCompletionProvider = vscode.languages.registerCompletionItemProvider(mdcDocumentSelector, { 156 | provideCompletionItems: async (document, position) => { 157 | const mdcComponents = await getComponentMetadata() 158 | // If no components, exit early 159 | if (!mdcComponents || !mdcComponents?.length) { 160 | return 161 | } 162 | return getMdcComponentPropCompletionItemProvider(mdcComponents, { document, position }) 163 | } 164 | }, 165 | '\n', // Trigger newline 166 | ' ' // Trigger on space character 167 | ) 168 | 169 | // Add to subscriptions for cleanup on deactivate 170 | context.subscriptions.push( 171 | mdcComponentCompletionProvider, 172 | mdcComponentPropsCompletionProvider 173 | ) 174 | 175 | // Dispose component metadata refresh command 176 | if (refreshMetadata) { 177 | refreshMetadata.dispose() 178 | } 179 | 180 | refreshMetadata = vscode.commands.registerCommand('mdc.refreshMetadata', async () => { 181 | await getComponentMetadata(true) 182 | }) 183 | // Register refresh metadata command 184 | context.subscriptions.push(refreshMetadata) 185 | }).catch((error) => { 186 | const errorMessage = `MDC: Error fetching component metadata: ${error.message}` 187 | if (outputChannel) { 188 | logger(errorMessage, 'error') 189 | } 190 | vscode.window.showErrorMessage(errorMessage) 191 | throw error // Re-throw to ensure VS Code knows the action failed 192 | }) 193 | } 194 | } 195 | 196 | logger('Registering MDC folding provider...') 197 | // Add static and config change subscriptions 198 | context.subscriptions.push( 199 | // Register folding range provider 200 | vscode.languages.registerFoldingRangeProvider(mdcDocumentSelector, { provideFoldingRanges }) 201 | ) 202 | logger('MDC folding provider registered.') 203 | 204 | // Register configuration change listener 205 | context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { 206 | if (e.affectsConfiguration('mdc')) { 207 | updateConfiguration() 208 | } 209 | })) 210 | 211 | // Initial setup 212 | updateConfiguration() 213 | } catch (error: any) { 214 | const errorMessage = `MDC: Error activating extension: ${error.message}` 215 | if (outputChannel) { 216 | logger(errorMessage, 'error') 217 | } 218 | vscode.window.showErrorMessage(errorMessage) 219 | throw error // Re-throw to ensure VS Code knows activation failed 220 | } 221 | } 222 | 223 | function disposeProviders () { 224 | if (mdcComponentCompletionProvider) { 225 | mdcComponentCompletionProvider.dispose() 226 | mdcComponentCompletionProvider = null 227 | } 228 | if (mdcComponentPropsCompletionProvider) { 229 | mdcComponentPropsCompletionProvider.dispose() 230 | mdcComponentPropsCompletionProvider = null 231 | } 232 | } 233 | 234 | export function deactivate (): void { 235 | disposeProviders() 236 | if (outputChannel) { 237 | outputChannel.dispose() 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## v0.4.2 6 | 7 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.4.1...v0.4.2) 8 | 9 | ### 🩹 Fixes 10 | 11 | - **deps:** Update mdc formatter for dynamic props ([#60](https://github.com/nuxtlabs/vscode-mdc/pull/60)) 12 | 13 | ### ❤️ Contributors 14 | 15 | - Adam DeHaven ([@adamdehaven](https://github.com/adamdehaven)) 16 | 17 | ## v0.4.1 18 | 19 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.4.0...v0.4.1) 20 | 21 | ### 🏡 Chore 22 | 23 | - Exclude types from build ([c580313](https://github.com/nuxtlabs/vscode-mdc/commit/c580313)) 24 | 25 | ### ❤️ Contributors 26 | 27 | - Farnabaz 28 | 29 | ## v0.4.0 30 | 31 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.3.3...v0.4.0) 32 | 33 | ### 🚀 Enhancements 34 | 35 | - **completions:** Array prop completion formats ([#57](https://github.com/nuxtlabs/vscode-mdc/pull/57)) 36 | 37 | ### 🩹 Fixes 38 | 39 | - **deps:** Update monarch-mdc ([#58](https://github.com/nuxtlabs/vscode-mdc/pull/58)) 40 | 41 | ### ✅ Tests 42 | 43 | - Init extension test ([b9f9e54](https://github.com/nuxtlabs/vscode-mdc/commit/b9f9e54)) 44 | 45 | ### ❤️ Contributors 46 | 47 | - Farnabaz 48 | - Adam DeHaven ([@adamdehaven](https://github.com/adamdehaven)) 49 | 50 | ## v0.3.3 51 | 52 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.3.2...v0.3.3) 53 | 54 | ### 🩹 Fixes 55 | 56 | - **deps:** Update monarch-mdc for formatter fixes ([#56](https://github.com/nuxtlabs/vscode-mdc/pull/56)) 57 | 58 | ### ❤️ Contributors 59 | 60 | - Adam DeHaven ([@adamdehaven](https://github.com/adamdehaven)) 61 | 62 | ## v0.3.2 63 | 64 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.3.1...v0.3.2) 65 | 66 | ### 🩹 Fixes 67 | 68 | - **deps:** Update monarch-mdc for formatter fixes ([#55](https://github.com/nuxtlabs/vscode-mdc/pull/55)) 69 | 70 | ### 📖 Documentation 71 | 72 | - Fix syntax reference link ([#54](https://github.com/nuxtlabs/vscode-mdc/pull/54)) 73 | 74 | ### ❤️ Contributors 75 | 76 | - Adam DeHaven ([@adamdehaven](https://github.com/adamdehaven)) 77 | - Yue JIN ([@kingyue737](https://github.com/kingyue737)) 78 | 79 | ## v0.3.1 80 | 81 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.3.0...v0.3.1) 82 | 83 | ### 🤖 CI 84 | 85 | - Enable dependencies on published versions ([1379070](https://github.com/nuxtlabs/vscode-mdc/commit/1379070)) 86 | 87 | ### ❤️ Contributors 88 | 89 | - Farnabaz 90 | 91 | ## v0.3.0 92 | 93 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.2.4...v0.3.0) 94 | 95 | ### 🚀 Enhancements 96 | 97 | - Component and prop suggestions ([#43](https://github.com/nuxtlabs/vscode-mdc/pull/43)) 98 | 99 | ### 🩹 Fixes 100 | 101 | - **completions:** Prop type value ([#50](https://github.com/nuxtlabs/vscode-mdc/pull/50)) 102 | - **completions:** Detect object prop type ([#51](https://github.com/nuxtlabs/vscode-mdc/pull/51)) 103 | 104 | ### 🤖 CI 105 | 106 | - Deploy to open vsx ([95a5f06](https://github.com/nuxtlabs/vscode-mdc/commit/95a5f06)) 107 | 108 | ### ❤️ Contributors 109 | 110 | - Farnabaz 111 | - Adam DeHaven ([@adamdehaven](https://github.com/adamdehaven)) 112 | 113 | ## v0.2.4 114 | 115 | [compare changes](https://github.com/nuxtlabs/vscode-mdc/compare/v0.2.3...v0.2.4) 116 | 117 | ### 🩹 Fixes 118 | 119 | - Show markdown preview menus ([#49](https://github.com/nuxtlabs/vscode-mdc/pull/49)) 120 | 121 | ### ❤️ Contributors 122 | 123 | - Farnabaz 124 | 125 | ## v0.2.3 126 | 127 | ### [0.2.3](https://github.com/nuxtlabs/vscode-mdc/compare/v0.2.0...v0.2.3) (2025-03-04) 128 | 129 | 130 | > [!IMPORTANT] 131 | > As of this version, the extension introduces executable code to format and fold documents ([#41](https://github.com/nuxtlabs/vscode-mdc/issues/41)). Due to this change, VSCode auto-update does not update the extension automatically; you need to manually update it. 132 | 133 | ### 🚀 Features 134 | 135 | * add document formatting and code folding providers ([#41](https://github.com/nuxtlabs/vscode-mdc/issues/41)) ([866de6e](https://github.com/nuxtlabs/vscode-mdc/commit/866de6e93440afc6f42594610ef1d0c22b661123)) 136 | 137 | 138 | ### 🩹 Bug Fixes 139 | 140 | * create `mdc.standalone` to use separately outside of markdown language ([45d4f42](https://github.com/nuxtlabs/vscode-mdc/commit/45d4f426aa51b8d6a6b2e719ed2bc8f617bd3919)) 141 | * **formatting:** make formatting opt-in ([#45](https://github.com/nuxtlabs/vscode-mdc/issues/45)) ([58a8bed](https://github.com/nuxtlabs/vscode-mdc/commit/58a8bedde552f14e89cc48b47606e077f2ee3e02)) 142 | * **packaging:** properly package dependencies ([#46](https://github.com/nuxtlabs/vscode-mdc/issues/46)) ([545791f](https://github.com/nuxtlabs/vscode-mdc/commit/545791f11561c4ce37ab5e7e9dc5959343613686)) 143 | * **standalone:** include inline syntax ([#42](https://github.com/nuxtlabs/vscode-mdc/issues/42)) ([96f619c](https://github.com/nuxtlabs/vscode-mdc/commit/96f619ccd8df724e5bd94eb28b87e03b769a8c41)) 144 | 145 | 146 | ## [0.2.0](https://github.com/nuxtlabs/vscode-mdc/compare/v0.1.8...v0.2.0) (2023-09-01) 147 | 148 | 149 | ### ⚠ BREAKING CHANGES 150 | 151 | * project overhaul (#33) 152 | 153 | ### Features 154 | 155 | * project overhaul ([#33](https://github.com/nuxtlabs/vscode-mdc/issues/33)) ([6294dbc](https://github.com/nuxtlabs/vscode-mdc/commit/6294dbce9706f538a521e7dccce9609e928dc3be)) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * **README.md:** MDC Syntax Reference link ([#31](https://github.com/nuxtlabs/vscode-mdc/issues/31)) ([5ff7fd7](https://github.com/nuxtlabs/vscode-mdc/commit/5ff7fd71a4e5c59b50962a1bd0ba0e50f66446d4)) 161 | * update icon ([#34](https://github.com/nuxtlabs/vscode-mdc/issues/34)) ([a18dea2](https://github.com/nuxtlabs/vscode-mdc/commit/a18dea2fb4263dff2d83866d596a7fe3573fca85)) 162 | 163 | ### [0.1.8](https://github.com/nuxtlabs/vscode-mdc/compare/v0.1.7...v0.1.8) (2022-09-14) 164 | 165 | 166 | ### Features 167 | 168 | * enable web extension ([2766128](https://github.com/nuxtlabs/vscode-mdc/commit/276612883090183f73598074e29930bc2dc8da0d)) 169 | 170 | 171 | ### Bug Fixes 172 | 173 | * **highlight:** broken highlighting of mustache syntax ([#27](https://github.com/nuxtlabs/vscode-mdc/issues/27)) ([7bf8ce3](https://github.com/nuxtlabs/vscode-mdc/commit/7bf8ce3dbda8f2d79431d80dc59c4e28b4fc745c)) 174 | 175 | ### [0.1.7](https://github.com/nuxtlabs/vscode-mdc/compare/v0.1.4...v0.1.7) (2022-05-02) 176 | 177 | 178 | ### Features 179 | 180 | * move to Nuxt and rename to MDC ([8e1b3d0](https://github.com/nuxtlabs/vscode-mdc/commit/8e1b3d04fae2883987c98c040ee5e98edfd7497f)) 181 | 182 | 183 | ### Bug Fixes 184 | 185 | * convert indendation to spaces ([e9f6fff](https://github.com/nuxtlabs/vscode-mdc/commit/e9f6fff0a2ffde4aee48850dbe1c8fb253fe9152)) 186 | * remove skipping tag ([0d0a2d2](https://github.com/nuxtlabs/vscode-mdc/commit/0d0a2d20a3d201c729a4f3f702a029809dceddd6)) 187 | 188 | ### [0.1.6](https://github.com/nuxtlabs/vscode-mdc/compare/v0.1.4...v0.1.6) (2022-05-02) 189 | 190 | 191 | ### Features 192 | 193 | * move to Nuxt and rename to MDC ([8e1b3d0](https://github.com/nuxtlabs/vscode-mdc/commit/8e1b3d04fae2883987c98c040ee5e98edfd7497f)) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * convert indendation to spaces ([e9f6fff](https://github.com/nuxtlabs/vscode-mdc/commit/e9f6fff0a2ffde4aee48850dbe1c8fb253fe9152)) 199 | 200 | ### [0.1.5](https://github.com/nuxtlabs/vscode-mdc/compare/v0.1.4...v0.1.5) (2022-05-02) 201 | 202 | 203 | ### Features 204 | 205 | * move to Nuxt and rename to MDC ([8e1b3d0](https://github.com/nuxtlabs/vscode-mdc/commit/8e1b3d04fae2883987c98c040ee5e98edfd7497f)) 206 | 207 | ### [0.1.4](https://github.com/docusgen/vscode-extension/compare/v0.1.2...v0.1.4) (2021-11-11) 208 | 209 | 210 | ### Features 211 | 212 | * init monarch grammar ([#3](https://github.com/docusgen/vscode-extension/issues/3)) ([e3350e6](https://github.com/docusgen/vscode-extension/commit/e3350e677c565daaa44aa8424dafca4d46f662ee)) 213 | 214 | 215 | ### Bug Fixes 216 | 217 | * activation events ([86c5123](https://github.com/docusgen/vscode-extension/commit/86c5123c303980f879a06b2b84f558b7dc63ce84)) 218 | * attributes colorring ([75eb0f5](https://github.com/docusgen/vscode-extension/commit/75eb0f5635ae90d144d83b18e4d5121acba50027)), closes [#18](https://github.com/docusgen/vscode-extension/issues/18) 219 | * handle nested components ([fe3663a](https://github.com/docusgen/vscode-extension/commit/fe3663a45cc68199d6f5f9f3a6de3d491b75da71)), closes [#4](https://github.com/docusgen/vscode-extension/issues/4) 220 | * improve block repository ([1303abd](https://github.com/docusgen/vscode-extension/commit/1303abd16342880a42a4d143a660da049c79ea6c)) 221 | * inline component attribute ([ca13391](https://github.com/docusgen/vscode-extension/commit/ca13391a6652a09ee5954573113101f96b04af68)) 222 | * support dash in slot name ([1c247d7](https://github.com/docusgen/vscode-extension/commit/1c247d701fe7139df9aaf6d92934a57b9447faf1)), closes [#4](https://github.com/docusgen/vscode-extension/issues/4) 223 | 224 | ### [0.1.3](https://github.com/docusgen/vscode-extension/compare/v0.1.2...v0.1.3) (2021-08-19) 225 | 226 | 227 | ### Bug Fixes 228 | 229 | * support dash in slot name ([1c247d7](https://github.com/docusgen/vscode-extension/commit/1c247d701fe7139df9aaf6d92934a57b9447faf1)), closes [#4](https://github.com/docusgen/vscode-extension/issues/4) 230 | 231 | ### [0.1.2](https://github.com/docusgen/vscode-extension/compare/v0.1.1...v0.1.2) (2021-07-06) 232 | 233 | ### 0.1.1 (2021-07-06) 234 | 235 | 236 | ### Features 237 | 238 | * add snippets ([150cea1](https://github.com/docusgen/vscode-extension/commit/150cea17170690f48ee89baab88a1934329c66a8)) 239 | * block slots ([8d3c8c8](https://github.com/docusgen/vscode-extension/commit/8d3c8c81148211866e22b9b6f60a3e89741d025f)) 240 | * highlight compoents sytax ([dfd6a60](https://github.com/docusgen/vscode-extension/commit/dfd6a608453599a2a7d05aa5ff3b636e95e90b99)) 241 | * span & inline components ([7b23c19](https://github.com/docusgen/vscode-extension/commit/7b23c199fc78a4db13450fd765048d6b432d8b0f)) 242 | 243 | 244 | ### Bug Fixes 245 | 246 | * attributes regex ([17f01f0](https://github.com/docusgen/vscode-extension/commit/17f01f0aea10c7b96a5d5114af4a31040b0f4678)) 247 | * improve block slot support ([8a60614](https://github.com/docusgen/vscode-extension/commit/8a60614600a9b0b05ca8f62693d48772eb7cedf8)) 248 | * prevent name conflict ([edf2d67](https://github.com/docusgen/vscode-extension/commit/edf2d67a25389184f0237eb9bdbadcf27ea3221b)) 249 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MDC - Markdown Components by NuxtLabs 2 | 3 | # MDC syntax highlight for Visual Studio Code 4 | 5 | [![MDC Extension for VS Code][extension-version-src]][extension-href] 6 | [![MDC Extension for VS Code][extension-downloads-src]][extension-href] 7 | [![MDC Extension for VS Code][extension-installs-src]][extension-href] 8 | 9 | Provides syntax highlighting and colon (`:`) matching for MDC (Markdown Components) files, as well as document folding and format providers, along with component name and prop suggestions. 10 | 11 | - [Download VS Code extension](https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc) 12 | 13 | Best used with: 14 | - [Remark MDC](https://github.com/nuxtlabs/remark-mdc) 15 | - [Markdown It MDC](https://github.com/antfu/markdown-it-mdc) 16 | 17 | Or with Nuxt modules: 18 | - [Nuxt MDC](https://github.com/nuxt-modules/mdc) 19 | - [Nuxt Content](https://content.nuxt.com) 20 | 21 | ## Features 22 | 23 | - [Block Components](#block-components) 24 | - [Inline Components](#inline-components) 25 | - [Span Text](#span-text) 26 | - [Attributes](#attributes) 27 | - [Document folding](#document-folding) 28 | - [Formatting](#formatting) 29 | - [Component name and prop suggestions](#component-name-and-prop-suggestions) 30 | 31 | ### Block Components 32 | 33 | ```md 34 | ::card 35 | --- 36 | icon: IconNuxt 37 | title: A complex card. 38 | --- 39 | 40 | Default slot 41 | 42 | #description 43 | ::alert 44 | Description slot 45 | :: 46 | :: 47 | ``` 48 | 49 | ### Inline Components 50 | 51 | ```md 52 | :button-link[A button link]{.text-bold} 53 | 54 | :button-link{.text-bold}[A button link] 55 | ``` 56 | 57 | ### Span Text 58 | 59 | ```md 60 | Hello [World]! 61 | ``` 62 | 63 | ### Attributes 64 | 65 | ```md 66 | Hello [World]{.text-primary-500}! 67 | 68 | [Link](#link){.text-primary-500 ref="noopener"}! 69 | 70 | **Bold Text**{style="color: tomato"} 71 | 72 | `Inline Code`{style="background: #333"} 73 | 74 | _Italic Text_{#italic_text} 75 | ``` 76 | 77 | ### Document folding 78 | 79 | The extension enables document code folding for MDC block components (and nested components). Simply hover over the gutter of the line you'd like to fold and click on the icon to expand or collapse the range. 80 | 81 | ![code folding animation](images/code-folding.gif) 82 | 83 | ### Formatting 84 | 85 | The plugin also enables a document format provider, disabled by default. 86 | 87 | To globally configure document formatting in VS Code, search for `mdc.enableFormatting` in Settings. 88 | 89 | Alternatively, to configure per-project, create or edit `.vscode/settings.json` in your project's root directory: 90 | 91 | ```jsonc 92 | { 93 | // Required for the extension 94 | "mdc.enableFormatting": true, 95 | // Recommended (for `mdc` and `md`, depending on your usage) 96 | "[mdc]": { 97 | "editor.tabSize": 2, 98 | "editor.insertSpaces": true, 99 | "editor.detectIndentation": false, 100 | "editor.formatOnPaste": true 101 | } 102 | } 103 | ``` 104 | 105 | > [!Note] 106 | > Since the format provider utilizes spaces for indention, you may also need to configure your project to insert spaces for tabs within `.mdc` or `.md` files as recommended above. 107 | 108 | ### Component name and prop suggestions 109 | 110 | The extension can provide intelligent auto-completion for MDC block components and their props by scanning your local project files or by fetching remote data when provided with an API URL in your VS Code settings. 111 | 112 | When typing a colon (`:`) in your MDC document, the extension will suggest available component names. Within MDC component YAML front matter sections (between `---`), the extension provides contextual prop suggestions (including nested props) with types and documentation where provided. 113 | 114 | To enable component name and prop suggestions, enable the `mdc.enableComponentMetadataCompletions` setting in in VS Code, and configure the other settings as described below depending on your preferred component metadata source. 115 | 116 | The extension supports two methods for providing component metadata: 117 | 118 | - Local component metadata provided by the `nuxt-component-meta` module, or a static JSON file in your project. 119 | - Remote component metadata provided by an API endpoint that returns JSON data. 120 | 121 | Regardless of the method you choose, the data source **must** match one of the following formats: 122 | 123 | 1. The default format used by `nuxt-component-meta` 124 | 125 | - If your data source matches this format, it will automatically be transformed into the interface documented below. 126 | 127 | 2. A custom format that matches the following TypeScript interface: 128 | 129 | ```typescript 130 | interface MDCComponentData { 131 | /** The kebab-case name of the markdown component */ 132 | mdc_name: string 133 | /** Component description */ 134 | description?: string 135 | /** Markdown-formatted documentation */ 136 | documentation_markdown?: string 137 | /** URL to component documentation */ 138 | docs_url?: string 139 | /** Component metadata interface from `nuxt-component-meta` */ 140 | component_meta: { 141 | mode?: string; 142 | global?: boolean 143 | filePath?: string 144 | pascalName?: string 145 | kebabName?: string 146 | chunkName?: string 147 | fullPath?: string 148 | shortPath?: string 149 | meta: ComponentMeta; // import type { ComponentMeta } from 'vue-component-meta' 150 | } 151 | } 152 | 153 | type MDCMetadataResponse = MDCComponentData[] 154 | ``` 155 | 156 | #### Local component metadata 157 | 158 | To enable local component metadata suggestions, you must first configure your project to provide component metadata in the format expected by the extension. You can do this by using the `nuxt-component-meta` module in your Nuxt project, or by providing a static JSON file that matches the interface described above. 159 | 160 | To enable automatic discovery via `nuxt-component-meta`, follow these steps: 161 | 162 | 1. Follow [the instructions to install `nuxt-component-meta`](https://github.com/nuxtlabs/nuxt-component-meta) in your Nuxt project. 163 | 164 | - Add `nuxt-component-meta` dependency to your project: 165 | 166 | ```shell 167 | # Using PNPM 168 | pnpm add nuxt-component-meta 169 | 170 | # Using NPM 171 | npm install nuxt-component-meta 172 | ``` 173 | 174 | - Add `nuxt-component-meta` to the `modules` section of your `nuxt.config.ts` and optionally configure the module: 175 | 176 | ```typescript 177 | export default defineNuxtConfig({ 178 | modules: ['nuxt-component-meta'], 179 | componentMeta: { 180 | // Options... see https://github.com/nuxtlabs/nuxt-component-meta 181 | } 182 | }) 183 | ``` 184 | 2. Once your project has been built or running on the dev server, the extension will automatically scan your project for component metadata and provide suggestions based on the components discovered with zero additional configuration. 185 | 186 | ##### Local component metadata options 187 | 188 | `mdc.componentMetadataLocalFilePattern` 189 | 190 | A glob pattern to the local MDC component metadata file. Defaults to: `**/.nuxt/component-meta.mjs` 191 | 192 | `mdc.componentMetadataLocalExcludePattern` 193 | 194 | A glob pattern to exclude directories from the local MDC component metadata search. Defaults to: `{**/node_modules/**,**/dist/**,**/.output/**,**/.cache/**,**/.playground/**}` 195 | 196 | You may customize the local component metadata options per project by creating or editing `.vscode/settings.json` in your project's root directory with the desired setting. It utilize glob patterns to match the file or directories containing your component metadata from the root of the workspace: 197 | 198 | ```jsonc 199 | { 200 | // Use a custom path to your component metadata file 201 | "mdc.componentMetadataLocalFilePattern": "path-to/your-static-file/component-metadata.json", 202 | // Or match a custom pattern 203 | "mdc.componentMetadataLocalFilePattern": "**/custom-component-metadata.json" 204 | } 205 | ``` 206 | 207 | #### Remote component metadata 208 | 209 | You can also choose to provide a URL to an API endpoint that returns component metadata in JSON format. The extension will fetch this data and provide suggestions based on the components found. 210 | 211 | > [!Note] 212 | > A configured remote component metadata URL will take precedence over local metadata when both are available. 213 | 214 | The endpoint provided to `mdc.componentMetadataURL` should return JSON data in one of the valid formats described above. 215 | 216 | - To globally configure the metadata URL for your project, search for `mdc.componentMetadataURL` in VS Code Settings. 217 | - To configure the metadata URL just for the scope of your active project, create or edit `.vscode/settings.json` in your project's root directory with the full URL to your API endpoint: 218 | 219 | ```jsonc 220 | { 221 | // Use your remote API endpoint to fetch component metadata 222 | "mdc.componentMetadataURL": "https://example.com/api/my-components/mdc/metadata", 223 | // Or even utilize a locally running server 224 | "mdc.componentMetadataURL": "http://localhost:3000/api/component-meta" 225 | } 226 | ``` 227 | 228 | The extension caches component metadata based on the the configurable TTL and provides a command `MDC: Refresh component metadata` to manually update the cache. To customize the cache TTL you may customize the value for `mdc.componentMetadataCacheTTL` in settings. Defaults to `30` minutes. 229 | 230 | ### For more information 231 | 232 | * [MDC Syntax Reference](https://content.nuxt.com/docs/files/markdown) 233 | 234 | ## Running Extension Tests 235 | 236 | To run the integration tests for this extension: 237 | 238 | 1. Install dependencies: 239 | ```sh 240 | pnpm install 241 | ``` 242 | 2. Build the extension and run tests: 243 | ```sh 244 | pnpm test 245 | ``` 246 | 247 | This will compile the source and test files, then run the integration tests using VS Code's test runner. 248 | 249 | 250 | [extension-href]: https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc 251 | [extension-version-src]: https://img.shields.io/visual-studio-marketplace/v/Nuxt.mdc?label=Visual%20Studio%20Code&style=flat&colorA=020420&colorB=28CF8D 252 | [extension-downloads-src]: https://img.shields.io/visual-studio-marketplace/d/Nuxt.mdc?style=flat&colorA=020420&colorB=28CF8D 253 | [extension-installs-src]: https://img.shields.io/visual-studio-marketplace/i/Nuxt.mdc?style=flat&colorA=020420&colorB=28CF8D 254 | -------------------------------------------------------------------------------- /src/component-metadata.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { kebabCase } from 'scule' 3 | import type { MDCComponentData } from './completion-providers' 4 | import { logger } from './logger' 5 | 6 | let metadataCache: MDCComponentData[] | null = null 7 | let lastSuccessfulFile: string | null = null 8 | let lastFetch = 0 9 | const DEFAULT_CACHE_TTL_MINUTES = 30 // 30 minutes 10 | 11 | /** 12 | * Fetches metadata from a specified URL with optional force parameter. 13 | * 14 | * @param {string} url - The URL endpoint to fetch metadata from 15 | * @param {boolean} force - When true, displays VS Code information messages during fetch process. Defaults to false. 16 | * @returns A Promise that resolves to the fetched metadata object, or null if the fetch fails 17 | * @throws Will throw an error if the fetch response is not ok 18 | */ 19 | async function fetchRemoteComponentMetadata (url: string, force = false): Promise { 20 | try { 21 | logger(`Fetching MDC component metadata from: ${url}`) 22 | vscode.window.showInformationMessage(`MDC: Fetching component metadata from: ${url}`) 23 | 24 | const response = await fetch(url) 25 | 26 | if (!response.ok) { 27 | throw new Error(`Failed to fetch MDC component metadata: ${response.statusText}`) 28 | } 29 | 30 | // Convert response to Uint8Array for consistency with file processing 31 | const data = new Uint8Array(await response.arrayBuffer()) 32 | const metadata = processMetadataFile({ 33 | metadataContent: data, 34 | filePath: url, 35 | force, 36 | source: 'remote' 37 | }) 38 | 39 | if (metadata) { 40 | const message = `MDC: Component metadata fetched successfully (${metadata.length} components found).` 41 | vscode.window.showInformationMessage(message) 42 | const config = vscode.workspace.getConfiguration('mdc') 43 | if (config.get('debug')) { 44 | logger(message) 45 | } 46 | } 47 | 48 | return metadata 49 | } catch (error: any) { 50 | const errorMessage = `MDC: ${error.message}` 51 | logger(errorMessage, 'error') 52 | vscode.window.showErrorMessage(errorMessage) 53 | return null 54 | } 55 | } 56 | 57 | interface ProcessMetadataOptions { 58 | /** The raw content of the metadata file */ 59 | metadataContent: Uint8Array 60 | /** The path or URL of the metadata file being processed */ 61 | filePath: string 62 | /** When true, displays VS Code information messages during processing */ 63 | force?: boolean 64 | /** Indicates whether the content is from a local file or remote URL */ 65 | source?: 'remote' | 'local' 66 | } 67 | 68 | /** 69 | * Processes a metadata file containing MDC component information. 70 | * 71 | * @param {ProcessMetadataOptions} options - Configuration options for processing metadata 72 | * @returns {MDCComponentData[]} An array of MDCComponentData objects sorted by component name, or null if processing fails. 73 | * 74 | * @description 75 | * This function performs the following steps: 76 | * 1. Decodes the file content from Uint8Array to text 77 | * 2. Extracts the default export object using regex 78 | * 3. Parses the JSON content 79 | * 4. Creates a map of component metadata, converting component names to kebab-case 80 | * 5. Sorts the components by name 81 | */ 82 | const processMetadataFile = ({ 83 | metadataContent, 84 | filePath, 85 | source = 'local', 86 | force = false 87 | }: ProcessMetadataOptions): MDCComponentData[] | null => { 88 | const config = vscode.workspace.getConfiguration('mdc') 89 | const textContent = new TextDecoder().decode(metadataContent) 90 | 91 | logger(`Processing ${source} metadata from: ${filePath}`) 92 | 93 | // Try parsing as direct JSON first 94 | try { 95 | const directContent = JSON.parse(textContent) 96 | if (Array.isArray(directContent) && 97 | directContent.length > 0 && 98 | 'mdc_name' in directContent[0] && 99 | 'component_meta' in directContent[0] && 100 | 'meta' in directContent[0].component_meta) { 101 | const config = vscode.workspace.getConfiguration('mdc') 102 | if (config.get('debug')) { 103 | logger(`${directContent.length} components found (no data transformation needed).`) 104 | } 105 | return directContent 106 | } 107 | 108 | // If we got here, the JSON was valid but needs transformation 109 | logger('Valid JSON found, attempting data transformation...') 110 | 111 | // Try transforming the JSON content 112 | const componentsMap = new Map() 113 | Object.values(directContent).forEach((componentMeta: any) => { 114 | const kebabCaseName = kebabCase(componentMeta.kebabName || componentMeta.pascalName) 115 | if (!componentsMap.has(kebabCaseName)) { 116 | componentsMap.set(kebabCaseName, { 117 | mdc_name: kebabCaseName, 118 | component_meta: componentMeta 119 | }) 120 | } 121 | }) 122 | 123 | // Sort the array of component metadata entries based on mdc_name. 124 | const metadata = Array.from(componentsMap.values()).sort((a, b) => a.mdc_name.localeCompare(b.mdc_name)) 125 | 126 | if (config.get('debug')) { 127 | logger(`${metadata?.length} components found (transformed data format).`) 128 | } 129 | 130 | return metadata 131 | } catch (error) { 132 | // JSON parse failed, if this is a local file, try export pattern 133 | if (source === 'local') { 134 | logger('Direct JSON parse failed, checking for export pattern...') 135 | 136 | const match = textContent.match(/export\s+default\s+([{[][\s\S]*?\n[}\]])/m) 137 | if (match?.[1]) { 138 | try { 139 | return processMetadataFile({ 140 | metadataContent: new TextEncoder().encode(match[1]), 141 | filePath, 142 | force, 143 | source: 'remote' // Treat exported content as if it were remote JSON 144 | }) 145 | } catch (exportError) { 146 | logger('Export pattern processing failed.', 'error') 147 | } 148 | } 149 | } 150 | } 151 | 152 | logger(`Unable to process ${source} metadata content from: ${filePath}`, 'error') 153 | return null 154 | } 155 | 156 | /** 157 | * Attempts to find and load local component metadata from `.nuxt/component-meta.mjs` files in the current workspace or its subdirectories. 158 | */ 159 | async function fetchLocalComponentMetadata (force = false): Promise { 160 | const config = vscode.workspace.getConfiguration('mdc') 161 | const metadataFilePattern = config.get('componentMetadataLocalFilePattern', '**/.nuxt/component-meta.mjs') 162 | const metadataExcludeDirectoriesPattern = config.get('componentMetadataLocalExcludePattern', '{**/node_modules/**,**/dist/**,**/.output/**,**/.cache/**,**/.playground/**}') 163 | 164 | try { 165 | const workspaceFolders = vscode.workspace.workspaceFolders 166 | if (!workspaceFolders?.length) { 167 | logger('No workspace folders found.') 168 | return null 169 | } 170 | 171 | // If we have a cached file path and this isn't a force refresh, try it first 172 | if (!force && lastSuccessfulFile) { 173 | try { 174 | const fileContent = await vscode.workspace.fs.readFile(vscode.Uri.file(lastSuccessfulFile)) 175 | const metadata = processMetadataFile({ 176 | metadataContent: fileContent, 177 | filePath: lastSuccessfulFile, 178 | force, 179 | source: 'local' 180 | }) 181 | if (metadata) { 182 | return metadata 183 | } 184 | // If processing failed, clear the cache and continue with full search 185 | lastSuccessfulFile = null 186 | } catch (error: any) { 187 | logger(`Cached file no longer accessible: ${error.message}`) 188 | lastSuccessfulFile = null 189 | } 190 | } 191 | 192 | // Full file search if needed 193 | const sourceFilePattern = new vscode.RelativePattern(workspaceFolders[0], metadataFilePattern) 194 | // Find files, excluding the provided directory patterns 195 | const files = await vscode.workspace.findFiles(sourceFilePattern, metadataExcludeDirectoriesPattern) 196 | if (!files.length) { 197 | logger('No files found.') 198 | return null 199 | } 200 | 201 | // Try each file until we find valid metadata 202 | for (const file of files) { 203 | logger(`Attempting to read metadata file: ${file.fsPath}`) 204 | try { 205 | const fileContent = await vscode.workspace.fs.readFile(file) 206 | const metadata = processMetadataFile({ 207 | metadataContent: fileContent, 208 | filePath: file.fsPath, 209 | force, 210 | source: 'local' 211 | }) 212 | if (metadata) { 213 | lastSuccessfulFile = file.fsPath 214 | return metadata 215 | } 216 | } catch (parseError) { 217 | logger(`Error parsing metadata from ${file.fsPath}: ${parseError}`, 'error') 218 | continue 219 | } 220 | } 221 | 222 | logger('No valid metadata found in available sources.') 223 | return null 224 | } catch (error: any) { 225 | logger(`Error loading local metadata: ${error.message}`, 'error') 226 | return null 227 | } 228 | } 229 | 230 | /** 231 | * Retrieves metadata from a configured URL with caching support. 232 | * @param {boolean} force - When true, bypasses cache and forces a new fetch. Defaults to false. 233 | * @returns {Promise} Promise resolving to metadata object, or null if URL is not configured or fetch fails 234 | * @throws {Error} Potentially throws if network request fails 235 | * 236 | * The function will: 237 | * - Check configuration for metadata URL and cache TTL 238 | * - Show warning if force=true but URL not configured 239 | * - Return cached data if within TTL period 240 | * - Fetch new data if cache expired or force=true 241 | * - Update cache with new data if fetch successful 242 | */ 243 | export async function getComponentMetadata (force = false): Promise { 244 | const config = vscode.workspace.getConfiguration('mdc') 245 | const componentCompletionsEnabled = config.get('enableComponentMetadataCompletions', false) 246 | const componentMetadataCacheTTL: number = 60 * 1000 * Number(config.get('componentMetadataCacheTTL') || DEFAULT_CACHE_TTL_MINUTES) 247 | const now = Date.now() 248 | 249 | // Check if we should skip fetching (not forced and within TTL) 250 | if (!force && lastFetch > 0 && (now - lastFetch) < componentMetadataCacheTTL) { 251 | if (metadataCache) { 252 | return metadataCache 253 | } 254 | return null 255 | } 256 | 257 | // If completions are disabled, update lastFetch and return 258 | if (!componentCompletionsEnabled) { 259 | const message = 'MDC: Component metadata suggestions are not enabled.' 260 | logger(message) 261 | vscode.window.showInformationMessage(message) 262 | lastFetch = now // Remember this check even if disabled 263 | return null 264 | } 265 | 266 | // Clear cache if forcing refresh 267 | if (force) { 268 | logger('Fetching MDC component metadata and clearing cache...') 269 | metadataCache = null 270 | lastFetch = 0 271 | } 272 | 273 | let metadata: MDCComponentData[] | null = null 274 | const componentMetadataURL = config.get('componentMetadataURL') 275 | 276 | // If URL is configured, only use remote metadata 277 | if (componentMetadataURL) { 278 | logger('Attempting to fetch remote metadata...') 279 | metadata = await fetchRemoteComponentMetadata(componentMetadataURL, force) 280 | } else { 281 | // Only try local metadata if no URL is configured 282 | metadata = await fetchLocalComponentMetadata(force) 283 | } 284 | 285 | // Always update lastFetch after any attempt 286 | lastFetch = now 287 | 288 | if (metadata) { 289 | logger('Metadata fetch successful, updating cache.') 290 | metadataCache = metadata 291 | return metadata 292 | } 293 | 294 | return null 295 | } 296 | -------------------------------------------------------------------------------- /syntaxes/mdc.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "mdc", 4 | "injectionSelector": "L:text.html.markdown", 5 | "scopeName": "text.markdown.mdc", 6 | "patterns": [ 7 | { 8 | "include": "#component_block" 9 | }, 10 | { 11 | "include": "#inline" 12 | } 13 | ], 14 | "repository": { 15 | "block": { 16 | "patterns": [ 17 | { 18 | "include": "#component_block" 19 | }, 20 | { 21 | "include": "text.html.markdown#separator" 22 | }, 23 | { 24 | "include": "#heading" 25 | }, 26 | { 27 | "include": "#blockquote" 28 | }, 29 | { 30 | "include": "#lists" 31 | }, 32 | { 33 | "include": "text.html.markdown#fenced_code_block" 34 | }, 35 | { 36 | "include": "text.html.markdown#link-def" 37 | }, 38 | { 39 | "include": "text.html.markdown#html" 40 | }, 41 | { 42 | "include": "#paragraph" 43 | } 44 | ] 45 | }, 46 | "inline": { 47 | "patterns": [ 48 | { 49 | "include": "#component_inline" 50 | }, 51 | { 52 | "include": "#span" 53 | }, 54 | { 55 | "include": "#attributes" 56 | } 57 | ] 58 | }, 59 | "span": { 60 | "match": "(?x)\n (\\[) # Open\n ([^]]*)\n (\\])\n ( # attributes\n ({)\n ([^{]*)\n (})\n )?\n \\s", 61 | "name": "span.component.mdc", 62 | "captures": { 63 | "1": { 64 | "name": "punctuation.definition.tag.start.component" 65 | }, 66 | "2": { 67 | "name": "string.other.link.description.title.markdown" 68 | }, 69 | "3": { 70 | "name": "punctuation.definition.tag.end.component" 71 | }, 72 | "4": { 73 | "patterns": [ 74 | { 75 | "include": "#attributes" 76 | } 77 | ] 78 | } 79 | } 80 | }, 81 | "attributes": { 82 | "match": "(?x)( # attributes\n ({)\n ([^{]*)\n (})\n )", 83 | "name": "attributes.mdc", 84 | "captures": { 85 | "1": { 86 | "name": "punctuation.definition.tag.start.component" 87 | }, 88 | "3": { 89 | "patterns": [ 90 | { 91 | "include": "#attribute" 92 | } 93 | ] 94 | }, 95 | "4": { 96 | "name": "punctuation.definition.tag.end.component" 97 | } 98 | } 99 | }, 100 | "component_inline": { 101 | "match": "(?x)\n (^|\\G|\\s+)\n (:) # component colon\n (?i: # component name\n (\\w[\\w\\d-]*)\n )\n (\n ({[^}]*}) # attributes\n (\\[[^\\]]*\\])? # slot\n # reverse order\n | (\\[[^\\]]*\\]) # slot\n ({[^}]*})? # attributes\n )?\n \\s", 102 | "name": "inline.component.mdc", 103 | "captures": { 104 | "2": { 105 | "name": "punctuation.definition.tag.start.component" 106 | }, 107 | "3": { 108 | "name": "entity.name.tag.component" 109 | }, 110 | "5": { 111 | "patterns": [ 112 | { 113 | "include": "#attributes" 114 | } 115 | ] 116 | }, 117 | "6": { 118 | "patterns": [ 119 | { 120 | "include": "#span" 121 | } 122 | ] 123 | }, 124 | "7": { 125 | "patterns": [ 126 | { 127 | "include": "#span" 128 | } 129 | ] 130 | }, 131 | "8": { 132 | "patterns": [ 133 | { 134 | "include": "#attributes" 135 | } 136 | ] 137 | } 138 | } 139 | }, 140 | "component_block": { 141 | "begin": "(?x)\n (^|\\G)(\\s*)\n (:{2,}) # component colons\n (?i:\n (\\w[\\w\\d-]+) # component name\n ( # folowing spaces or attributes\n \\s*\n | \\s*({[^{]*})\n )\n $\n )", 142 | "name": "block.component.mdc", 143 | "end": "(^|\\G)(\\2)(\\3)\\s*$", 144 | "beginCaptures": { 145 | "3": { 146 | "name": "punctuation.definition.tag.start.mdc" 147 | }, 148 | "4": { 149 | "name": "entity.name.tag.mdc" 150 | }, 151 | "5": { 152 | "patterns": [ 153 | { 154 | "include": "#attributes" 155 | } 156 | ] 157 | } 158 | }, 159 | "endCaptures": { 160 | "3": { 161 | "name": "punctuation.definition.tag.end.mdc" 162 | } 163 | }, 164 | "patterns": [ 165 | { 166 | "match": "(^|\\G)\\s*([:]{2,})$", 167 | "captures": { 168 | "2": { 169 | "name": "punctuation.definition.tag.end.mdc" 170 | } 171 | } 172 | }, 173 | { 174 | "begin": "(^|\\G)(\\s*)(-{3})(\\s*)$", 175 | "end": "(^|\\G)(\\s*(-{3})(\\s*)$)", 176 | "patterns": [ 177 | { 178 | "include": "source.yaml" 179 | } 180 | ] 181 | }, 182 | { 183 | "match": "^(\\s*)(#[\\w\\-\\_]*)\\s*()?$", 184 | "captures": { 185 | "2": { 186 | "name": "entity.other.attribute-name.html" 187 | }, 188 | "3": { 189 | "name": "comment.block.html" 190 | } 191 | } 192 | }, 193 | { 194 | "include": "#block" 195 | } 196 | ] 197 | }, 198 | "attribute": { 199 | "patterns": [ 200 | { 201 | "match": "(?x)\n (\n ([^=><\\s]*) # attribute name\n ( # attribute value\n =[\"]([^\"]*)([\"])|[']([^']*)(['])\n | =[^\\s'\"}]*\n )?\n \\s*\n )", 202 | "captures": { 203 | "2": { 204 | "name": "entity.other.attribute-name.html" 205 | }, 206 | "3": { 207 | "patterns": [ 208 | { 209 | "include": "#attribute-interior" 210 | } 211 | ] 212 | } 213 | } 214 | } 215 | ] 216 | }, 217 | "attribute-interior": { 218 | "patterns": [ 219 | { 220 | "begin": "=", 221 | "beginCaptures": { 222 | "0": { 223 | "name": "punctuation.separator.key-value.html" 224 | } 225 | }, 226 | "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", 227 | "patterns": [ 228 | { 229 | "match": "([^\\s\"'=<>`/]|/(?!>))+", 230 | "name": "string.unquoted.html" 231 | }, 232 | { 233 | "begin": "\"", 234 | "beginCaptures": { 235 | "0": { 236 | "name": "punctuation.definition.string.begin.html" 237 | } 238 | }, 239 | "end": "\"", 240 | "endCaptures": { 241 | "0": { 242 | "name": "punctuation.definition.string.end.html" 243 | } 244 | }, 245 | "name": "string.quoted.double.html", 246 | "patterns": [ 247 | { 248 | "include": "#entities" 249 | } 250 | ] 251 | }, 252 | { 253 | "begin": "'", 254 | "beginCaptures": { 255 | "0": { 256 | "name": "punctuation.definition.string.begin.html" 257 | } 258 | }, 259 | "end": "'", 260 | "endCaptures": { 261 | "0": { 262 | "name": "punctuation.definition.string.end.html" 263 | } 264 | }, 265 | "name": "string.quoted.single.html", 266 | "patterns": [ 267 | { 268 | "include": "#entities" 269 | } 270 | ] 271 | }, 272 | { 273 | "match": "=", 274 | "name": "invalid.illegal.unexpected-equals-sign.html" 275 | } 276 | ] 277 | } 278 | ] 279 | }, 280 | "entities": { 281 | "patterns": [ 282 | { 283 | "captures": { 284 | "1": { 285 | "name": "punctuation.definition.entity.html" 286 | }, 287 | "912": { 288 | "name": "punctuation.definition.entity.html" 289 | } 290 | }, 291 | "match": "(?x)\n\t\t\t\t\t\t(&)\t(?=[a-zA-Z])\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(a(s(ymp(eq)?|cr|t)|n(d(slope|d|v|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a(h|c|d|e|f|a|g|b))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|e|E|acir)?|elig|f(r)?|w(conint|int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve))\n\t\t\t\t\t\t | (B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h(d|u|D|U)?|times|H(d|u|D|U)?|d(R|l|r|L)|u(R|l|r|L)|plus|D(R|l|r|L)|v(R|h|H|l|r|L)?|U(R|l|r|L)|V(R|h|H|l|r|L)?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1(2|4)|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?))\n\t\t\t\t\t\t | (c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr(l|r)|p(s|c(up|ap)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w(conint|int)|lubs(uit)?|a(cute|p(s|c(up|ap)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly(DoubleQuote|Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross))\n\t\t\t\t\t\t | (d(s(c(y|r)|trok|ol)|har(l|r)|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up(DownArrow|Arrow)|VerticalBar|L(ong(RightArrow|Left(RightArrow|Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger)))\n\t\t\t\t\t\t | (e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t(h|a)|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(ot|Dot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1(3|4))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty(SmallSquare|VerySmallSquare)|acr)))\n\t\t\t\t\t\t | (f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(ig|lig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1(2|8|3|4|5|6)|78|2(3|5)|3(8|4|5)|45|5(8|6)))))|F(scr|cy|illed(SmallSquare|VerySmallSquare)|o(uriertrf|pf|rAll)|fr))\n\t\t\t\t\t\t | (G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im(e|l)?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(qless|less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l(j|E|a)?|a(cute|p|mma(d)?)|rave|g(g)?|breve))\n\t\t\t\t\t\t | (h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok(leftarrow|rightarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks(earow|warow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy))\n\t\t\t\t\t\t | (i(s(cr|in(s(v)?|dot|v|E)?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(nt|int)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f(f|r)|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?))\n\t\t\t\t\t\t | (j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr))\n\t\t\t\t\t\t | (k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa))\n\t\t\t\t\t\t | (l(s(h|cr|trok|im(e|g)?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(il|dil)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i(e|f)?|Par))?|Har|o(ng(left(arrow|rightarrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r(dhar|ushar))|ur(dshar|uhar)|jcy|par(lt)?|e(s(s(sim|dot|eq(qgtr|gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left(arrow|rightarrow)|rightarrow|Left(RightArrow|Arrow))|pf|wer(RightArrow|LeftArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot))\n\t\t\t\t\t\t | (M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u(ltimap|map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker)))\n\t\t\t\t\t\t | (n(s(hort(parallel|mid)|c(cue|e|r)?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|e|E)?|b(set(eq(q)?)?|e|E)?)|par|qsu(pe|be)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v(c|a|b))?|in(dot|v(c|a|b)|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g(t|e))|fr|w(near|ar(hk|r(ow)?)|Arr)|V(dash|Dash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft(arrow|rightarrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr(c|w)?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft(arrow|rightarrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes(SlantEqual|Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi(nSpace|ckSpace)|VeryThinSpace))|Jcy|fr|acute))\n\t\t\t\t\t\t | (o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|f|m)?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly(DoubleQuote|Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr)))\n\t\t\t\t\t\t | (p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d(o|u)|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?))\n\t\t\t\t\t\t | (q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr))\n\t\t\t\t\t\t | (R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(il|dil)|aron)|Barr|t(hree|imes|ri(e|f|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng(d|e|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr)))\n\t\t\t\t\t\t | (s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma(v|f)?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot(e|b)?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n(e|E)|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|n(e|E)|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar(e|f))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort(RightArrow|DownArrow|UpArrow|LeftArrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle))\n\t\t\t\t\t\t | (t(s(hcy|c(y|r)|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead(leftarrow|rightarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i(nSpace|ckSpace)|e(ta|refore))|c(y|edil|aron)|S(cy|Hcy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a(u|b)|ripleDot))\n\t\t\t\t\t\t | (u(scr|h(ar(l|r)|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per(RightArrow|LeftArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve)))\n\t\t\t\t\t\t | (v(s(cr|u(pn(e|E)|bn(e|E)))|nsu(p|b)|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar))\n\t\t\t\t\t\t | (w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr))\n\t\t\t\t\t\t | (X(scr|i|opf|fr)|x(s(cr|qcup)|h(arr|Arr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l(arr|Arr)|r(arr|Arr)|map))\n\t\t\t\t\t\t | (y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy))\n\t\t\t\t\t\t | (z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(nj|j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t(;)\n\t\t\t\t\t", 292 | "name": "constant.character.entity.named.$2.html" 293 | }, 294 | { 295 | "captures": { 296 | "1": { 297 | "name": "punctuation.definition.entity.html" 298 | }, 299 | "3": { 300 | "name": "punctuation.definition.entity.html" 301 | } 302 | }, 303 | "match": "(&)#[0-9]+(;)", 304 | "name": "constant.character.entity.numeric.decimal.html" 305 | }, 306 | { 307 | "captures": { 308 | "1": { 309 | "name": "punctuation.definition.entity.html" 310 | }, 311 | "3": { 312 | "name": "punctuation.definition.entity.html" 313 | } 314 | }, 315 | "match": "(&)#[xX][0-9a-fA-F]+(;)", 316 | "name": "constant.character.entity.numeric.hexadecimal.html" 317 | }, 318 | { 319 | "match": "&(?=[a-zA-Z0-9]+;)", 320 | "name": "invalid.illegal.ambiguous-ampersand.html" 321 | } 322 | ] 323 | }, 324 | "heading": { 325 | "match": "(?:^|\\G)[ ]*(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$", 326 | "captures": { 327 | "1": { 328 | "patterns": [ 329 | { 330 | "match": "(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$", 331 | "name": "heading.6.markdown", 332 | "captures": { 333 | "1": { 334 | "name": "punctuation.definition.heading.markdown" 335 | }, 336 | "2": { 337 | "name": "entity.name.section.markdown", 338 | "patterns": [ 339 | { 340 | "include": "text.html.markdown#inline" 341 | }, 342 | { 343 | "include": "text.html.derivative" 344 | } 345 | ] 346 | }, 347 | "3": { 348 | "name": "punctuation.definition.heading.markdown" 349 | } 350 | } 351 | }, 352 | { 353 | "match": "(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$", 354 | "name": "heading.5.markdown", 355 | "captures": { 356 | "1": { 357 | "name": "punctuation.definition.heading.markdown" 358 | }, 359 | "2": { 360 | "name": "entity.name.section.markdown", 361 | "patterns": [ 362 | { 363 | "include": "text.html.markdown#inline" 364 | }, 365 | { 366 | "include": "text.html.derivative" 367 | } 368 | ] 369 | }, 370 | "3": { 371 | "name": "punctuation.definition.heading.markdown" 372 | } 373 | } 374 | }, 375 | { 376 | "match": "(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$", 377 | "name": "heading.4.markdown", 378 | "captures": { 379 | "1": { 380 | "name": "punctuation.definition.heading.markdown" 381 | }, 382 | "2": { 383 | "name": "entity.name.section.markdown", 384 | "patterns": [ 385 | { 386 | "include": "text.html.markdown#inline" 387 | }, 388 | { 389 | "include": "text.html.derivative" 390 | } 391 | ] 392 | }, 393 | "3": { 394 | "name": "punctuation.definition.heading.markdown" 395 | } 396 | } 397 | }, 398 | { 399 | "match": "(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$", 400 | "name": "heading.3.markdown", 401 | "captures": { 402 | "1": { 403 | "name": "punctuation.definition.heading.markdown" 404 | }, 405 | "2": { 406 | "name": "entity.name.section.markdown", 407 | "patterns": [ 408 | { 409 | "include": "text.html.markdown#inline" 410 | }, 411 | { 412 | "include": "text.html.derivative" 413 | } 414 | ] 415 | }, 416 | "3": { 417 | "name": "punctuation.definition.heading.markdown" 418 | } 419 | } 420 | }, 421 | { 422 | "match": "(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$", 423 | "name": "heading.2.markdown", 424 | "captures": { 425 | "1": { 426 | "name": "punctuation.definition.heading.markdown" 427 | }, 428 | "2": { 429 | "name": "entity.name.section.markdown", 430 | "patterns": [ 431 | { 432 | "include": "text.html.markdown#inline" 433 | }, 434 | { 435 | "include": "text.html.derivative" 436 | } 437 | ] 438 | }, 439 | "3": { 440 | "name": "punctuation.definition.heading.markdown" 441 | } 442 | } 443 | }, 444 | { 445 | "match": "(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$", 446 | "name": "heading.1.markdown", 447 | "captures": { 448 | "1": { 449 | "name": "punctuation.definition.heading.markdown" 450 | }, 451 | "2": { 452 | "name": "entity.name.section.markdown", 453 | "patterns": [ 454 | { 455 | "include": "text.html.markdown#inline" 456 | }, 457 | { 458 | "include": "text.html.derivative" 459 | } 460 | ] 461 | }, 462 | "3": { 463 | "name": "punctuation.definition.heading.markdown" 464 | } 465 | } 466 | } 467 | ] 468 | } 469 | }, 470 | "name": "markup.heading.markdown", 471 | "patterns": [ 472 | { 473 | "include": "text.html.markdown#inline" 474 | } 475 | ] 476 | }, 477 | "heading-setext": { 478 | "patterns": [ 479 | { 480 | "match": "^(={3,})(?=[ \\t]*$\\n?)", 481 | "name": "markup.heading.setext.1.markdown" 482 | }, 483 | { 484 | "match": "^(-{3,})(?=[ \\t]*$\\n?)", 485 | "name": "markup.heading.setext.2.markdown" 486 | } 487 | ] 488 | }, 489 | "lists": { 490 | "patterns": [ 491 | { 492 | "begin": "(^|\\G)([ ]*)([*+-])([ \\t])", 493 | "beginCaptures": { 494 | "3": { 495 | "name": "punctuation.definition.list.begin.markdown" 496 | } 497 | }, 498 | "name": "markup.list.unnumbered.markdown", 499 | "patterns": [ 500 | { 501 | "include": "#block" 502 | }, 503 | { 504 | "include": "text.html.markdown#list_paragraph" 505 | } 506 | ], 507 | "while": "((^|\\G)([ ]*|\\t))|(^[ \\t]*$)" 508 | }, 509 | { 510 | "begin": "(^|\\G)([ ]*)([0-9]+\\.)([ \\t])", 511 | "beginCaptures": { 512 | "3": { 513 | "name": "punctuation.definition.list.begin.markdown" 514 | } 515 | }, 516 | "name": "markup.list.numbered.markdown", 517 | "patterns": [ 518 | { 519 | "include": "#block" 520 | }, 521 | { 522 | "include": "text.html.markdown#list_paragraph" 523 | } 524 | ], 525 | "while": "((^|\\G)([ ]*|\\t))|(^[ \\t]*$)" 526 | } 527 | ] 528 | }, 529 | "paragraph": { 530 | "begin": "(^|\\G)[ ]*(?=\\S)", 531 | "name": "meta.paragraph.markdown", 532 | "patterns": [ 533 | { 534 | "include": "text.html.markdown#inline" 535 | }, 536 | { 537 | "include": "text.html.derivative" 538 | }, 539 | { 540 | "include": "#heading-setext" 541 | } 542 | ], 543 | "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))" 544 | }, 545 | "blockquote": { 546 | "begin": "(^|\\G)[ ]*(>) ?", 547 | "captures": { 548 | "2": { 549 | "name": "punctuation.definition.quote.begin.markdown" 550 | } 551 | }, 552 | "name": "markup.quote.markdown", 553 | "patterns": [ 554 | { 555 | "include": "#block" 556 | } 557 | ], 558 | "while": "(^|\\G)\\s*(>) ?" 559 | } 560 | } 561 | } -------------------------------------------------------------------------------- /syntaxes/mdc.standalone.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "mdc", 4 | "displayName": "MDC - Markdown Components", 5 | "injectionSelector": "L:text.html.markdown", 6 | "scopeName": "text.markdown.mdc.standalone", 7 | "patterns": [ 8 | { 9 | "include": "text.html.markdown#frontMatter" 10 | }, 11 | { 12 | "include": "#block" 13 | } 14 | ], 15 | "repository": { 16 | "block": { 17 | "patterns": [ 18 | { 19 | "include": "#inline" 20 | }, 21 | { 22 | "include": "#component_block" 23 | }, 24 | { 25 | "include": "text.html.markdown#separator" 26 | }, 27 | { 28 | "include": "#heading" 29 | }, 30 | { 31 | "include": "#blockquote" 32 | }, 33 | { 34 | "include": "#lists" 35 | }, 36 | { 37 | "include": "text.html.markdown#fenced_code_block" 38 | }, 39 | { 40 | "include": "text.html.markdown#link-def" 41 | }, 42 | { 43 | "include": "text.html.markdown#html" 44 | }, 45 | { 46 | "include": "#paragraph" 47 | } 48 | ] 49 | }, 50 | "inline": { 51 | "patterns": [ 52 | { 53 | "include": "#component_inline" 54 | }, 55 | { 56 | "include": "#span" 57 | }, 58 | { 59 | "include": "#attributes" 60 | } 61 | ] 62 | }, 63 | "span": { 64 | "match": "(?x)\n (\\[) # Open\n ([^]]*)\n (\\])\n ( # attributes\n ({)\n ([^{]*)\n (})\n )?\n \\s", 65 | "name": "span.component.mdc", 66 | "captures": { 67 | "1": { 68 | "name": "punctuation.definition.tag.start.component" 69 | }, 70 | "2": { 71 | "name": "string.other.link.description.title.markdown" 72 | }, 73 | "3": { 74 | "name": "punctuation.definition.tag.end.component" 75 | }, 76 | "4": { 77 | "patterns": [ 78 | { 79 | "include": "#attributes" 80 | } 81 | ] 82 | } 83 | } 84 | }, 85 | "attributes": { 86 | "match": "(?x)( # attributes\n ({)\n ([^{]*)\n (})\n )", 87 | "name": "attributes.mdc", 88 | "captures": { 89 | "1": { 90 | "name": "punctuation.definition.tag.start.component" 91 | }, 92 | "3": { 93 | "patterns": [ 94 | { 95 | "include": "#attribute" 96 | } 97 | ] 98 | }, 99 | "4": { 100 | "name": "punctuation.definition.tag.end.component" 101 | } 102 | } 103 | }, 104 | "component_inline": { 105 | "match": "(?x)\n (^|\\G|\\s+)\n (:) # component colon\n (?i: # component name\n (\\w[\\w\\d-]*)\n )\n (\n ({[^}]*}) # attributes\n (\\[[^\\]]*\\])? # slot\n # reverse order\n | (\\[[^\\]]*\\]) # slot\n ({[^}]*})? # attributes\n )?\n \\s", 106 | "name": "inline.component.mdc", 107 | "captures": { 108 | "2": { 109 | "name": "punctuation.definition.tag.start.component" 110 | }, 111 | "3": { 112 | "name": "entity.name.tag.component" 113 | }, 114 | "5": { 115 | "patterns": [ 116 | { 117 | "include": "#attributes" 118 | } 119 | ] 120 | }, 121 | "6": { 122 | "patterns": [ 123 | { 124 | "include": "#span" 125 | } 126 | ] 127 | }, 128 | "7": { 129 | "patterns": [ 130 | { 131 | "include": "#span" 132 | } 133 | ] 134 | }, 135 | "8": { 136 | "patterns": [ 137 | { 138 | "include": "#attributes" 139 | } 140 | ] 141 | } 142 | } 143 | }, 144 | "component_block": { 145 | "begin": "(?x)\n (^|\\G)(\\s*)\n (:{2,}) # component colons\n (?i:\n (\\w[\\w\\d-]+) # component name\n ( # folowing spaces or attributes\n \\s*\n | \\s*({[^{]*})\n )\n $\n )", 146 | "name": "block.component.mdc", 147 | "end": "(^|\\G)(\\2)(\\3)\\s*$", 148 | "beginCaptures": { 149 | "3": { 150 | "name": "punctuation.definition.tag.start.mdc" 151 | }, 152 | "4": { 153 | "name": "entity.name.tag.mdc" 154 | }, 155 | "5": { 156 | "patterns": [ 157 | { 158 | "include": "#attributes" 159 | } 160 | ] 161 | } 162 | }, 163 | "endCaptures": { 164 | "3": { 165 | "name": "punctuation.definition.tag.end.mdc" 166 | } 167 | }, 168 | "patterns": [ 169 | { 170 | "match": "(^|\\G)\\s*([:]{2,})$", 171 | "captures": { 172 | "2": { 173 | "name": "punctuation.definition.tag.end.mdc" 174 | } 175 | } 176 | }, 177 | { 178 | "begin": "(^|\\G)(\\s*)(-{3})(\\s*)$", 179 | "end": "(^|\\G)(\\s*(-{3})(\\s*)$)", 180 | "patterns": [ 181 | { 182 | "include": "source.yaml" 183 | } 184 | ] 185 | }, 186 | { 187 | "match": "^(\\s*)(#[\\w\\-\\_]*)\\s*()?$", 188 | "captures": { 189 | "2": { 190 | "name": "entity.other.attribute-name.html" 191 | }, 192 | "3": { 193 | "name": "comment.block.html" 194 | } 195 | } 196 | }, 197 | { 198 | "include": "#block" 199 | } 200 | ] 201 | }, 202 | "attribute": { 203 | "patterns": [ 204 | { 205 | "match": "(?x)\n (\n ([^=><\\s]*) # attribute name\n ( # attribute value\n =[\"]([^\"]*)([\"])|[']([^']*)(['])\n | =[^\\s'\"}]*\n )?\n \\s*\n )", 206 | "captures": { 207 | "2": { 208 | "name": "entity.other.attribute-name.html" 209 | }, 210 | "3": { 211 | "patterns": [ 212 | { 213 | "include": "#attribute-interior" 214 | } 215 | ] 216 | } 217 | } 218 | } 219 | ] 220 | }, 221 | "attribute-interior": { 222 | "patterns": [ 223 | { 224 | "begin": "=", 225 | "beginCaptures": { 226 | "0": { 227 | "name": "punctuation.separator.key-value.html" 228 | } 229 | }, 230 | "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", 231 | "patterns": [ 232 | { 233 | "match": "([^\\s\"'=<>`/]|/(?!>))+", 234 | "name": "string.unquoted.html" 235 | }, 236 | { 237 | "begin": "\"", 238 | "beginCaptures": { 239 | "0": { 240 | "name": "punctuation.definition.string.begin.html" 241 | } 242 | }, 243 | "end": "\"", 244 | "endCaptures": { 245 | "0": { 246 | "name": "punctuation.definition.string.end.html" 247 | } 248 | }, 249 | "name": "string.quoted.double.html", 250 | "patterns": [ 251 | { 252 | "include": "#entities" 253 | } 254 | ] 255 | }, 256 | { 257 | "begin": "'", 258 | "beginCaptures": { 259 | "0": { 260 | "name": "punctuation.definition.string.begin.html" 261 | } 262 | }, 263 | "end": "'", 264 | "endCaptures": { 265 | "0": { 266 | "name": "punctuation.definition.string.end.html" 267 | } 268 | }, 269 | "name": "string.quoted.single.html", 270 | "patterns": [ 271 | { 272 | "include": "#entities" 273 | } 274 | ] 275 | }, 276 | { 277 | "match": "=", 278 | "name": "invalid.illegal.unexpected-equals-sign.html" 279 | } 280 | ] 281 | } 282 | ] 283 | }, 284 | "entities": { 285 | "patterns": [ 286 | { 287 | "captures": { 288 | "1": { 289 | "name": "punctuation.definition.entity.html" 290 | }, 291 | "912": { 292 | "name": "punctuation.definition.entity.html" 293 | } 294 | }, 295 | "match": "(?x)\n\t\t\t\t\t\t(&)\t(?=[a-zA-Z])\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(a(s(ymp(eq)?|cr|t)|n(d(slope|d|v|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a(h|c|d|e|f|a|g|b))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|e|E|acir)?|elig|f(r)?|w(conint|int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve))\n\t\t\t\t\t\t | (B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h(d|u|D|U)?|times|H(d|u|D|U)?|d(R|l|r|L)|u(R|l|r|L)|plus|D(R|l|r|L)|v(R|h|H|l|r|L)?|U(R|l|r|L)|V(R|h|H|l|r|L)?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1(2|4)|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?))\n\t\t\t\t\t\t | (c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr(l|r)|p(s|c(up|ap)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w(conint|int)|lubs(uit)?|a(cute|p(s|c(up|ap)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly(DoubleQuote|Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross))\n\t\t\t\t\t\t | (d(s(c(y|r)|trok|ol)|har(l|r)|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up(DownArrow|Arrow)|VerticalBar|L(ong(RightArrow|Left(RightArrow|Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger)))\n\t\t\t\t\t\t | (e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t(h|a)|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(ot|Dot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1(3|4))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty(SmallSquare|VerySmallSquare)|acr)))\n\t\t\t\t\t\t | (f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(ig|lig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1(2|8|3|4|5|6)|78|2(3|5)|3(8|4|5)|45|5(8|6)))))|F(scr|cy|illed(SmallSquare|VerySmallSquare)|o(uriertrf|pf|rAll)|fr))\n\t\t\t\t\t\t | (G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im(e|l)?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(qless|less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l(j|E|a)?|a(cute|p|mma(d)?)|rave|g(g)?|breve))\n\t\t\t\t\t\t | (h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok(leftarrow|rightarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks(earow|warow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy))\n\t\t\t\t\t\t | (i(s(cr|in(s(v)?|dot|v|E)?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(nt|int)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f(f|r)|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?))\n\t\t\t\t\t\t | (j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr))\n\t\t\t\t\t\t | (k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa))\n\t\t\t\t\t\t | (l(s(h|cr|trok|im(e|g)?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(il|dil)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i(e|f)?|Par))?|Har|o(ng(left(arrow|rightarrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r(dhar|ushar))|ur(dshar|uhar)|jcy|par(lt)?|e(s(s(sim|dot|eq(qgtr|gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left(arrow|rightarrow)|rightarrow|Left(RightArrow|Arrow))|pf|wer(RightArrow|LeftArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot))\n\t\t\t\t\t\t | (M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u(ltimap|map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker)))\n\t\t\t\t\t\t | (n(s(hort(parallel|mid)|c(cue|e|r)?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|e|E)?|b(set(eq(q)?)?|e|E)?)|par|qsu(pe|be)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v(c|a|b))?|in(dot|v(c|a|b)|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g(t|e))|fr|w(near|ar(hk|r(ow)?)|Arr)|V(dash|Dash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft(arrow|rightarrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr(c|w)?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft(arrow|rightarrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes(SlantEqual|Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi(nSpace|ckSpace)|VeryThinSpace))|Jcy|fr|acute))\n\t\t\t\t\t\t | (o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|f|m)?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly(DoubleQuote|Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr)))\n\t\t\t\t\t\t | (p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d(o|u)|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?))\n\t\t\t\t\t\t | (q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr))\n\t\t\t\t\t\t | (R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(il|dil)|aron)|Barr|t(hree|imes|ri(e|f|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng(d|e|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr)))\n\t\t\t\t\t\t | (s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma(v|f)?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot(e|b)?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n(e|E)|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|n(e|E)|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar(e|f))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort(RightArrow|DownArrow|UpArrow|LeftArrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle))\n\t\t\t\t\t\t | (t(s(hcy|c(y|r)|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead(leftarrow|rightarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i(nSpace|ckSpace)|e(ta|refore))|c(y|edil|aron)|S(cy|Hcy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a(u|b)|ripleDot))\n\t\t\t\t\t\t | (u(scr|h(ar(l|r)|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per(RightArrow|LeftArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve)))\n\t\t\t\t\t\t | (v(s(cr|u(pn(e|E)|bn(e|E)))|nsu(p|b)|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar))\n\t\t\t\t\t\t | (w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr))\n\t\t\t\t\t\t | (X(scr|i|opf|fr)|x(s(cr|qcup)|h(arr|Arr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l(arr|Arr)|r(arr|Arr)|map))\n\t\t\t\t\t\t | (y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy))\n\t\t\t\t\t\t | (z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(nj|j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t(;)\n\t\t\t\t\t", 296 | "name": "constant.character.entity.named.$2.html" 297 | }, 298 | { 299 | "captures": { 300 | "1": { 301 | "name": "punctuation.definition.entity.html" 302 | }, 303 | "3": { 304 | "name": "punctuation.definition.entity.html" 305 | } 306 | }, 307 | "match": "(&)#[0-9]+(;)", 308 | "name": "constant.character.entity.numeric.decimal.html" 309 | }, 310 | { 311 | "captures": { 312 | "1": { 313 | "name": "punctuation.definition.entity.html" 314 | }, 315 | "3": { 316 | "name": "punctuation.definition.entity.html" 317 | } 318 | }, 319 | "match": "(&)#[xX][0-9a-fA-F]+(;)", 320 | "name": "constant.character.entity.numeric.hexadecimal.html" 321 | }, 322 | { 323 | "match": "&(?=[a-zA-Z0-9]+;)", 324 | "name": "invalid.illegal.ambiguous-ampersand.html" 325 | } 326 | ] 327 | }, 328 | "heading": { 329 | "match": "(?:^|\\G)[ ]*(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$", 330 | "captures": { 331 | "1": { 332 | "patterns": [ 333 | { 334 | "match": "(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$", 335 | "name": "heading.6.markdown", 336 | "captures": { 337 | "1": { 338 | "name": "punctuation.definition.heading.markdown" 339 | }, 340 | "2": { 341 | "name": "entity.name.section.markdown", 342 | "patterns": [ 343 | { 344 | "include": "text.html.markdown#inline" 345 | }, 346 | { 347 | "include": "text.html.derivative" 348 | } 349 | ] 350 | }, 351 | "3": { 352 | "name": "punctuation.definition.heading.markdown" 353 | } 354 | } 355 | }, 356 | { 357 | "match": "(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$", 358 | "name": "heading.5.markdown", 359 | "captures": { 360 | "1": { 361 | "name": "punctuation.definition.heading.markdown" 362 | }, 363 | "2": { 364 | "name": "entity.name.section.markdown", 365 | "patterns": [ 366 | { 367 | "include": "text.html.markdown#inline" 368 | }, 369 | { 370 | "include": "text.html.derivative" 371 | } 372 | ] 373 | }, 374 | "3": { 375 | "name": "punctuation.definition.heading.markdown" 376 | } 377 | } 378 | }, 379 | { 380 | "match": "(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$", 381 | "name": "heading.4.markdown", 382 | "captures": { 383 | "1": { 384 | "name": "punctuation.definition.heading.markdown" 385 | }, 386 | "2": { 387 | "name": "entity.name.section.markdown", 388 | "patterns": [ 389 | { 390 | "include": "text.html.markdown#inline" 391 | }, 392 | { 393 | "include": "text.html.derivative" 394 | } 395 | ] 396 | }, 397 | "3": { 398 | "name": "punctuation.definition.heading.markdown" 399 | } 400 | } 401 | }, 402 | { 403 | "match": "(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$", 404 | "name": "heading.3.markdown", 405 | "captures": { 406 | "1": { 407 | "name": "punctuation.definition.heading.markdown" 408 | }, 409 | "2": { 410 | "name": "entity.name.section.markdown", 411 | "patterns": [ 412 | { 413 | "include": "text.html.markdown#inline" 414 | }, 415 | { 416 | "include": "text.html.derivative" 417 | } 418 | ] 419 | }, 420 | "3": { 421 | "name": "punctuation.definition.heading.markdown" 422 | } 423 | } 424 | }, 425 | { 426 | "match": "(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$", 427 | "name": "heading.2.markdown", 428 | "captures": { 429 | "1": { 430 | "name": "punctuation.definition.heading.markdown" 431 | }, 432 | "2": { 433 | "name": "entity.name.section.markdown", 434 | "patterns": [ 435 | { 436 | "include": "text.html.markdown#inline" 437 | }, 438 | { 439 | "include": "text.html.derivative" 440 | } 441 | ] 442 | }, 443 | "3": { 444 | "name": "punctuation.definition.heading.markdown" 445 | } 446 | } 447 | }, 448 | { 449 | "match": "(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$", 450 | "name": "heading.1.markdown", 451 | "captures": { 452 | "1": { 453 | "name": "punctuation.definition.heading.markdown" 454 | }, 455 | "2": { 456 | "name": "entity.name.section.markdown", 457 | "patterns": [ 458 | { 459 | "include": "text.html.markdown#inline" 460 | }, 461 | { 462 | "include": "text.html.derivative" 463 | } 464 | ] 465 | }, 466 | "3": { 467 | "name": "punctuation.definition.heading.markdown" 468 | } 469 | } 470 | } 471 | ] 472 | } 473 | }, 474 | "name": "markup.heading.markdown", 475 | "patterns": [ 476 | { 477 | "include": "text.html.markdown#inline" 478 | } 479 | ] 480 | }, 481 | "heading-setext": { 482 | "patterns": [ 483 | { 484 | "match": "^(={3,})(?=[ \\t]*$\\n?)", 485 | "name": "markup.heading.setext.1.markdown" 486 | }, 487 | { 488 | "match": "^(-{3,})(?=[ \\t]*$\\n?)", 489 | "name": "markup.heading.setext.2.markdown" 490 | } 491 | ] 492 | }, 493 | "lists": { 494 | "patterns": [ 495 | { 496 | "begin": "(^|\\G)([ ]*)([*+-])([ \\t])", 497 | "beginCaptures": { 498 | "3": { 499 | "name": "punctuation.definition.list.begin.markdown" 500 | } 501 | }, 502 | "name": "markup.list.unnumbered.markdown", 503 | "patterns": [ 504 | { 505 | "include": "#block" 506 | }, 507 | { 508 | "include": "text.html.markdown#list_paragraph" 509 | } 510 | ], 511 | "while": "((^|\\G)([ ]*|\\t))|(^[ \\t]*$)" 512 | }, 513 | { 514 | "begin": "(^|\\G)([ ]*)([0-9]+\\.)([ \\t])", 515 | "beginCaptures": { 516 | "3": { 517 | "name": "punctuation.definition.list.begin.markdown" 518 | } 519 | }, 520 | "name": "markup.list.numbered.markdown", 521 | "patterns": [ 522 | { 523 | "include": "#block" 524 | }, 525 | { 526 | "include": "text.html.markdown#list_paragraph" 527 | } 528 | ], 529 | "while": "((^|\\G)([ ]*|\\t))|(^[ \\t]*$)" 530 | } 531 | ] 532 | }, 533 | "paragraph": { 534 | "begin": "(^|\\G)[ ]*(?=\\S)", 535 | "name": "meta.paragraph.markdown", 536 | "patterns": [ 537 | { 538 | "include": "text.html.markdown#inline" 539 | }, 540 | { 541 | "include": "text.html.derivative" 542 | }, 543 | { 544 | "include": "#heading-setext" 545 | } 546 | ], 547 | "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))" 548 | }, 549 | "blockquote": { 550 | "begin": "(^|\\G)[ ]*(>) ?", 551 | "captures": { 552 | "2": { 553 | "name": "punctuation.definition.quote.begin.markdown" 554 | } 555 | }, 556 | "name": "markup.quote.markdown", 557 | "patterns": [ 558 | { 559 | "include": "#block" 560 | } 561 | ], 562 | "while": "(^|\\G)\\s*(>) ?" 563 | } 564 | } 565 | } -------------------------------------------------------------------------------- /src/grammar.ts: -------------------------------------------------------------------------------- 1 | import { IRawGrammar } from 'vscode-textmate' 2 | 3 | type IRawRepository = IRawGrammar['repository'] 4 | 5 | declare module 'vscode-textmate' { 6 | interface IRawRule { 7 | comment?: string 8 | } 9 | } 10 | 11 | export const grammar: IRawGrammar = { 12 | name: 'mdc', 13 | injectionSelector: 'L:text.html.markdown', 14 | scopeName: 'text.markdown.mdc', 15 | patterns: [ 16 | // Use `component_block` and `inline` to inject them into `text.html.markdown` 17 | // Using `block` here will cause hightlighting issues in `text.html.markdown` 18 | { 19 | include: '#component_block' 20 | }, 21 | { 22 | include: '#inline' 23 | } 24 | ], 25 | repository: { 26 | block: { 27 | // 'Same as `text.html.markdown#block`, but without `raw_block`', 28 | patterns: [ 29 | { 30 | include: '#component_block' 31 | }, 32 | { 33 | include: 'text.html.markdown#separator' 34 | }, 35 | { 36 | include: '#heading' 37 | }, 38 | { 39 | include: '#blockquote' 40 | }, 41 | { 42 | include: '#lists' 43 | }, 44 | { 45 | include: 'text.html.markdown#fenced_code_block' 46 | }, 47 | { 48 | include: 'text.html.markdown#link-def' 49 | }, 50 | { 51 | include: 'text.html.markdown#html' 52 | }, 53 | { 54 | include: '#paragraph' 55 | } 56 | ] 57 | }, 58 | inline: { 59 | patterns: [ 60 | { 61 | include: '#component_inline' 62 | }, 63 | { 64 | include: '#span' 65 | }, 66 | { 67 | include: '#attributes' 68 | } 69 | ] 70 | }, 71 | span: { 72 | match: `(?x) 73 | (\\[) # Open 74 | ([^]]*) 75 | (\\]) 76 | ( # attributes 77 | ({) 78 | ([^{]*) 79 | (}) 80 | )? 81 | \\s`, 82 | name: 'span.component.mdc', 83 | captures: { 84 | 1: { 85 | name: 'punctuation.definition.tag.start.component' 86 | }, 87 | 2: { 88 | name: 'string.other.link.description.title.markdown' 89 | }, 90 | 3: { 91 | name: 'punctuation.definition.tag.end.component' 92 | }, 93 | 4: { 94 | patterns: [ 95 | { 96 | include: '#attributes' 97 | } 98 | ] 99 | } 100 | } 101 | }, 102 | attributes: { 103 | match: `(?x)( # attributes 104 | ({) 105 | ([^{]*) 106 | (}) 107 | )`, 108 | name: 'attributes.mdc', 109 | captures: { 110 | 1: { 111 | name: 'punctuation.definition.tag.start.component' 112 | }, 113 | 3: { 114 | patterns: [ 115 | { 116 | include: '#attribute' 117 | } 118 | ] 119 | }, 120 | 4: { 121 | name: 'punctuation.definition.tag.end.component' 122 | } 123 | } 124 | }, 125 | component_inline: { 126 | match: `(?x) 127 | (^|\\G|\\s+) 128 | (:) # component colon 129 | (?i: # component name 130 | (\\w[\\w\\d-]*) 131 | ) 132 | ( 133 | ({[^}]*}) # attributes 134 | (\\[[^\\]]*\\])? # slot 135 | # reverse order 136 | | (\\[[^\\]]*\\]) # slot 137 | ({[^}]*})? # attributes 138 | )? 139 | \\s`, 140 | name: 'inline.component.mdc', 141 | captures: { 142 | 2: { 143 | name: 'punctuation.definition.tag.start.component' 144 | }, 145 | 3: { 146 | name: 'entity.name.tag.component' 147 | }, 148 | 5: { 149 | patterns: [ 150 | { 151 | include: '#attributes' 152 | } 153 | ] 154 | }, 155 | 6: { 156 | patterns: [ 157 | { 158 | include: '#span' 159 | } 160 | ] 161 | }, 162 | 7: { 163 | patterns: [ 164 | { 165 | include: '#span' 166 | } 167 | ] 168 | }, 169 | 8: { 170 | patterns: [ 171 | { 172 | include: '#attributes' 173 | } 174 | ] 175 | } 176 | } 177 | }, 178 | component_block: { 179 | begin: `(?x) 180 | (^|\\G)(\\s*) 181 | (:{2,}) # component colons 182 | (?i: 183 | (\\w[\\w\\d-]+) # component name 184 | ( # folowing spaces or attributes 185 | \\s* 186 | | \\s*({[^{]*}) 187 | ) 188 | $ 189 | )`, 190 | name: 'block.component.mdc', 191 | end: '(^|\\G)(\\2)(\\3)\\s*$', 192 | beginCaptures: { 193 | 3: { 194 | name: 'punctuation.definition.tag.start.mdc' 195 | }, 196 | 4: { 197 | name: 'entity.name.tag.mdc' 198 | }, 199 | 5: { 200 | patterns: [ 201 | { 202 | include: '#attributes' 203 | } 204 | ] 205 | } 206 | }, 207 | endCaptures: { 208 | 3: { 209 | name: 'punctuation.definition.tag.end.mdc' 210 | } 211 | }, 212 | patterns: [ 213 | { 214 | // 'Highlight ending tag of inner components', 215 | match: '(^|\\G)\\s*([:]{2,})$', 216 | captures: { 217 | 2: { 218 | name: 'punctuation.definition.tag.end.mdc' 219 | } 220 | } 221 | }, 222 | { 223 | begin: '(^|\\G)(\\s*)(-{3})(\\s*)$', 224 | end: '(^|\\G)(\\s*(-{3})(\\s*)$)', 225 | patterns: [ 226 | { 227 | include: 'source.yaml' 228 | } 229 | ] 230 | }, 231 | { 232 | match: '^(\\s*)(#[\\w\\-\\_]*)\\s*()?$', 233 | captures: { 234 | 2: { 235 | name: 'entity.other.attribute-name.html' 236 | }, 237 | 3: { 238 | name: 'comment.block.html' 239 | } 240 | } 241 | }, 242 | { 243 | include: '#block' 244 | } 245 | ] 246 | }, 247 | attribute: { 248 | patterns: [ 249 | { 250 | match: `(?x) 251 | ( 252 | ([^=><\\s]*) # attribute name 253 | ( # attribute value 254 | =["]([^"]*)(["])|[']([^']*)([']) 255 | | =[^\\s'"}]* 256 | )? 257 | \\s* 258 | )`, 259 | captures: { 260 | 2: { 261 | name: 'entity.other.attribute-name.html' 262 | }, 263 | 3: { 264 | patterns: [ 265 | { 266 | include: '#attribute-interior' 267 | } 268 | ] 269 | } 270 | } 271 | } 272 | ] 273 | }, 274 | 'attribute-interior': { 275 | // 'https://github.com/microsoft/vscode/blob/08d59c432609ae9306eb3889815977e93bb548de/extensions/html/syntaxes/html.tmLanguage.json#L376', 276 | patterns: [ 277 | { 278 | begin: '=', 279 | beginCaptures: { 280 | 0: { 281 | name: 'punctuation.separator.key-value.html' 282 | } 283 | }, 284 | end: '(?<=[^\\s=])(?!\\s*=)|(?=/?>)', 285 | patterns: [ 286 | { 287 | match: "([^\\s\"'=<>`/]|/(?!>))+", 288 | name: 'string.unquoted.html' 289 | }, 290 | { 291 | begin: '"', 292 | beginCaptures: { 293 | 0: { 294 | name: 'punctuation.definition.string.begin.html' 295 | } 296 | }, 297 | end: '"', 298 | endCaptures: { 299 | 0: { 300 | name: 'punctuation.definition.string.end.html' 301 | } 302 | }, 303 | name: 'string.quoted.double.html', 304 | patterns: [ 305 | { 306 | include: '#entities' 307 | } 308 | ] 309 | }, 310 | { 311 | begin: "'", 312 | beginCaptures: { 313 | 0: { 314 | name: 'punctuation.definition.string.begin.html' 315 | } 316 | }, 317 | end: "'", 318 | endCaptures: { 319 | 0: { 320 | name: 'punctuation.definition.string.end.html' 321 | } 322 | }, 323 | name: 'string.quoted.single.html', 324 | patterns: [ 325 | { 326 | include: '#entities' 327 | } 328 | ] 329 | }, 330 | { 331 | match: '=', 332 | name: 'invalid.illegal.unexpected-equals-sign.html' 333 | } 334 | ] 335 | } 336 | ] 337 | }, 338 | entities: { 339 | // 'https://github.com/microsoft/vscode/blob/08d59c432609ae9306eb3889815977e93bb548de/extensions/html/syntaxes/html.tmLanguage.json#L532', 340 | patterns: [ 341 | { 342 | captures: { 343 | 1: { 344 | name: 'punctuation.definition.entity.html' 345 | }, 346 | 912: { 347 | name: 'punctuation.definition.entity.html' 348 | } 349 | }, 350 | // 'Yes this is a bit ridiculous, there are quite a lot of these', 351 | match: `(?x) 352 | \t\t\t\t\t\t(&)\t(?=[a-zA-Z]) 353 | \t\t\t\t\t\t( 354 | \t\t\t\t\t\t\t(a(s(ymp(eq)?|cr|t)|n(d(slope|d|v|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a(h|c|d|e|f|a|g|b))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|e|E|acir)?|elig|f(r)?|w(conint|int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve)) 355 | \t\t\t\t\t\t | (B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h(d|u|D|U)?|times|H(d|u|D|U)?|d(R|l|r|L)|u(R|l|r|L)|plus|D(R|l|r|L)|v(R|h|H|l|r|L)?|U(R|l|r|L)|V(R|h|H|l|r|L)?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1(2|4)|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?)) 356 | \t\t\t\t\t\t | (c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr(l|r)|p(s|c(up|ap)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w(conint|int)|lubs(uit)?|a(cute|p(s|c(up|ap)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly(DoubleQuote|Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross)) 357 | \t\t\t\t\t\t | (d(s(c(y|r)|trok|ol)|har(l|r)|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up(DownArrow|Arrow)|VerticalBar|L(ong(RightArrow|Left(RightArrow|Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger))) 358 | \t\t\t\t\t\t | (e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t(h|a)|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(ot|Dot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1(3|4))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty(SmallSquare|VerySmallSquare)|acr))) 359 | \t\t\t\t\t\t | (f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(ig|lig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1(2|8|3|4|5|6)|78|2(3|5)|3(8|4|5)|45|5(8|6)))))|F(scr|cy|illed(SmallSquare|VerySmallSquare)|o(uriertrf|pf|rAll)|fr)) 360 | \t\t\t\t\t\t | (G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im(e|l)?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(qless|less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l(j|E|a)?|a(cute|p|mma(d)?)|rave|g(g)?|breve)) 361 | \t\t\t\t\t\t | (h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok(leftarrow|rightarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks(earow|warow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy)) 362 | \t\t\t\t\t\t | (i(s(cr|in(s(v)?|dot|v|E)?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(nt|int)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f(f|r)|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?)) 363 | \t\t\t\t\t\t | (j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr)) 364 | \t\t\t\t\t\t | (k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa)) 365 | \t\t\t\t\t\t | (l(s(h|cr|trok|im(e|g)?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(il|dil)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i(e|f)?|Par))?|Har|o(ng(left(arrow|rightarrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r(dhar|ushar))|ur(dshar|uhar)|jcy|par(lt)?|e(s(s(sim|dot|eq(qgtr|gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left(arrow|rightarrow)|rightarrow|Left(RightArrow|Arrow))|pf|wer(RightArrow|LeftArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot)) 366 | \t\t\t\t\t\t | (M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u(ltimap|map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker))) 367 | \t\t\t\t\t\t | (n(s(hort(parallel|mid)|c(cue|e|r)?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|e|E)?|b(set(eq(q)?)?|e|E)?)|par|qsu(pe|be)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v(c|a|b))?|in(dot|v(c|a|b)|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g(t|e))|fr|w(near|ar(hk|r(ow)?)|Arr)|V(dash|Dash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft(arrow|rightarrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr(c|w)?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft(arrow|rightarrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes(SlantEqual|Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi(nSpace|ckSpace)|VeryThinSpace))|Jcy|fr|acute)) 368 | \t\t\t\t\t\t | (o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|f|m)?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly(DoubleQuote|Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr))) 369 | \t\t\t\t\t\t | (p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d(o|u)|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?)) 370 | \t\t\t\t\t\t | (q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr)) 371 | \t\t\t\t\t\t | (R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(il|dil)|aron)|Barr|t(hree|imes|ri(e|f|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng(d|e|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))) 372 | \t\t\t\t\t\t | (s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma(v|f)?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot(e|b)?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n(e|E)|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|n(e|E)|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar(e|f))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort(RightArrow|DownArrow|UpArrow|LeftArrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle)) 373 | \t\t\t\t\t\t | (t(s(hcy|c(y|r)|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead(leftarrow|rightarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i(nSpace|ckSpace)|e(ta|refore))|c(y|edil|aron)|S(cy|Hcy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a(u|b)|ripleDot)) 374 | \t\t\t\t\t\t | (u(scr|h(ar(l|r)|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per(RightArrow|LeftArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve))) 375 | \t\t\t\t\t\t | (v(s(cr|u(pn(e|E)|bn(e|E)))|nsu(p|b)|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar)) 376 | \t\t\t\t\t\t | (w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr)) 377 | \t\t\t\t\t\t | (X(scr|i|opf|fr)|x(s(cr|qcup)|h(arr|Arr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l(arr|Arr)|r(arr|Arr)|map)) 378 | \t\t\t\t\t\t | (y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy)) 379 | \t\t\t\t\t\t | (z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(nj|j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute)) 380 | \t\t\t\t\t\t) 381 | \t\t\t\t\t\t(;) 382 | \t\t\t\t\t`, 383 | name: 'constant.character.entity.named.$2.html' 384 | }, 385 | { 386 | captures: { 387 | 1: { 388 | name: 'punctuation.definition.entity.html' 389 | }, 390 | 3: { 391 | name: 'punctuation.definition.entity.html' 392 | } 393 | }, 394 | match: '(&)#[0-9]+(;)', 395 | name: 'constant.character.entity.numeric.decimal.html' 396 | }, 397 | { 398 | captures: { 399 | 1: { 400 | name: 'punctuation.definition.entity.html' 401 | }, 402 | 3: { 403 | name: 'punctuation.definition.entity.html' 404 | } 405 | }, 406 | match: '(&)#[xX][0-9a-fA-F]+(;)', 407 | name: 'constant.character.entity.numeric.hexadecimal.html' 408 | }, 409 | { 410 | match: '&(?=[a-zA-Z0-9]+;)', 411 | name: 'invalid.illegal.ambiguous-ampersand.html' 412 | } 413 | ] 414 | }, 415 | heading: { 416 | match: '(?:^|\\G)[ ]*(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$', 417 | captures: { 418 | 1: { 419 | patterns: [ 420 | { 421 | match: '(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$', 422 | name: 'heading.6.markdown', 423 | captures: { 424 | 1: { 425 | name: 'punctuation.definition.heading.markdown' 426 | }, 427 | 2: { 428 | name: 'entity.name.section.markdown', 429 | patterns: [ 430 | { 431 | include: 'text.html.markdown#inline' 432 | }, 433 | { 434 | include: 'text.html.derivative' 435 | } 436 | ] 437 | }, 438 | 3: { 439 | name: 'punctuation.definition.heading.markdown' 440 | } 441 | } 442 | }, 443 | { 444 | match: '(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$', 445 | name: 'heading.5.markdown', 446 | captures: { 447 | 1: { 448 | name: 'punctuation.definition.heading.markdown' 449 | }, 450 | 2: { 451 | name: 'entity.name.section.markdown', 452 | patterns: [ 453 | { 454 | include: 'text.html.markdown#inline' 455 | }, 456 | { 457 | include: 'text.html.derivative' 458 | } 459 | ] 460 | }, 461 | 3: { 462 | name: 'punctuation.definition.heading.markdown' 463 | } 464 | } 465 | }, 466 | { 467 | match: '(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$', 468 | name: 'heading.4.markdown', 469 | captures: { 470 | 1: { 471 | name: 'punctuation.definition.heading.markdown' 472 | }, 473 | 2: { 474 | name: 'entity.name.section.markdown', 475 | patterns: [ 476 | { 477 | include: 'text.html.markdown#inline' 478 | }, 479 | { 480 | include: 'text.html.derivative' 481 | } 482 | ] 483 | }, 484 | 3: { 485 | name: 'punctuation.definition.heading.markdown' 486 | } 487 | } 488 | }, 489 | { 490 | match: '(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$', 491 | name: 'heading.3.markdown', 492 | captures: { 493 | 1: { 494 | name: 'punctuation.definition.heading.markdown' 495 | }, 496 | 2: { 497 | name: 'entity.name.section.markdown', 498 | patterns: [ 499 | { 500 | include: 'text.html.markdown#inline' 501 | }, 502 | { 503 | include: 'text.html.derivative' 504 | } 505 | ] 506 | }, 507 | 3: { 508 | name: 'punctuation.definition.heading.markdown' 509 | } 510 | } 511 | }, 512 | { 513 | match: '(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$', 514 | name: 'heading.2.markdown', 515 | captures: { 516 | 1: { 517 | name: 'punctuation.definition.heading.markdown' 518 | }, 519 | 2: { 520 | name: 'entity.name.section.markdown', 521 | patterns: [ 522 | { 523 | include: 'text.html.markdown#inline' 524 | }, 525 | { 526 | include: 'text.html.derivative' 527 | } 528 | ] 529 | }, 530 | 3: { 531 | name: 'punctuation.definition.heading.markdown' 532 | } 533 | } 534 | }, 535 | { 536 | match: '(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$', 537 | name: 'heading.1.markdown', 538 | captures: { 539 | 1: { 540 | name: 'punctuation.definition.heading.markdown' 541 | }, 542 | 2: { 543 | name: 'entity.name.section.markdown', 544 | patterns: [ 545 | { 546 | include: 'text.html.markdown#inline' 547 | }, 548 | { 549 | include: 'text.html.derivative' 550 | } 551 | ] 552 | }, 553 | 3: { 554 | name: 'punctuation.definition.heading.markdown' 555 | } 556 | } 557 | } 558 | ] 559 | } 560 | }, 561 | name: 'markup.heading.markdown', 562 | patterns: [ 563 | { 564 | include: 'text.html.markdown#inline' 565 | } 566 | ] 567 | }, 568 | 'heading-setext': { 569 | patterns: [ 570 | { 571 | match: '^(={3,})(?=[ \\t]*$\\n?)', 572 | name: 'markup.heading.setext.1.markdown' 573 | }, 574 | { 575 | match: '^(-{3,})(?=[ \\t]*$\\n?)', 576 | name: 'markup.heading.setext.2.markdown' 577 | } 578 | ] 579 | }, 580 | lists: { 581 | patterns: [ 582 | { 583 | begin: '(^|\\G)([ ]*)([*+-])([ \\t])', 584 | beginCaptures: { 585 | 3: { 586 | name: 'punctuation.definition.list.begin.markdown' 587 | } 588 | }, 589 | // 'Currently does not support un-indented second lines.', 590 | name: 'markup.list.unnumbered.markdown', 591 | patterns: [ 592 | { 593 | include: '#block' 594 | }, 595 | { 596 | include: 'text.html.markdown#list_paragraph' 597 | } 598 | ], 599 | while: '((^|\\G)([ ]*|\\t))|(^[ \\t]*$)' 600 | }, 601 | { 602 | begin: '(^|\\G)([ ]*)([0-9]+\\.)([ \\t])', 603 | beginCaptures: { 604 | 3: { 605 | name: 'punctuation.definition.list.begin.markdown' 606 | } 607 | }, 608 | name: 'markup.list.numbered.markdown', 609 | patterns: [ 610 | { 611 | include: '#block' 612 | }, 613 | { 614 | include: 'text.html.markdown#list_paragraph' 615 | } 616 | ], 617 | while: '((^|\\G)([ ]*|\\t))|(^[ \\t]*$)' 618 | } 619 | ] 620 | }, 621 | paragraph: { 622 | begin: '(^|\\G)[ ]*(?=\\S)', 623 | name: 'meta.paragraph.markdown', 624 | patterns: [ 625 | { 626 | include: 'text.html.markdown#inline' 627 | }, 628 | { 629 | include: 'text.html.derivative' 630 | }, 631 | { 632 | include: '#heading-setext' 633 | } 634 | ], 635 | while: '(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))' 636 | }, 637 | blockquote: { 638 | begin: '(^|\\G)[ ]*(>) ?', 639 | captures: { 640 | 2: { 641 | name: 'punctuation.definition.quote.begin.markdown' 642 | } 643 | }, 644 | name: 'markup.quote.markdown', 645 | patterns: [ 646 | { 647 | include: '#block' 648 | } 649 | ], 650 | while: '(^|\\G)\\s*(>) ?' 651 | } 652 | } as Partial as IRawRepository 653 | } 654 | -------------------------------------------------------------------------------- /src/completion-providers.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | import { kebabCase, camelCase } from 'scule' 3 | import * as vscode from 'vscode' 4 | import type { ComponentMeta } from 'vue-component-meta' 5 | import type { Component } from '@nuxt/schema' 6 | import { logger } from './logger' 7 | 8 | type MDCComponentMeta = Omit & { 9 | meta: ComponentMeta 10 | mode?: string 11 | global?: boolean 12 | pascalName?: string 13 | kebabName?: string 14 | chunkName?: string 15 | fullPath?: string 16 | filePath?: string 17 | shortPath?: string 18 | } 19 | 20 | type MdcPropType = 'string' | 'number' | 'boolean' | 'array' | 'array-unquoted' | 'object' 21 | 22 | export interface MDCComponentData { 23 | /** The kebab-case name of the component to be utilized in MDC. */ 24 | mdc_name: string 25 | /** A description of the MDC component as well as suggested use-cases, etc. */ 26 | description?: string 27 | /** Component documentation, provided via Markdown string. */ 28 | documentation_markdown?: string 29 | /** The documentation URL for the component. */ 30 | docs_url?: string 31 | /** MDC component meta */ 32 | component_meta: MDCComponentMeta 33 | } 34 | 35 | interface MdcCompletionItemProviderConfig { 36 | document: vscode.TextDocument 37 | position: vscode.Position 38 | } 39 | 40 | const MDC_COMPONENT_START_REGEX = /^\s*:{2,}([\w-]+)\s*$/ 41 | const MULTILINE_STRING_REGEX = /^([\w-]+):\s*[|>]/g 42 | 43 | /** Cache for storing model line content to avoid repeated getLinesContent() calls */ 44 | const lineContentCache = new WeakMap() 45 | /** Cache for storing prop name conversions between kebab-case and camelCase */ 46 | const propNameCache = new Map() 47 | /** Cache for storing nested props by component name */ 48 | const nestedPropsCache = new Map | null>>() 49 | /** Cache for storing prop value types by component name */ 50 | const propTypeCache = new Map>() 51 | /** Cache for storing documentation links by component name */ 52 | const docsLinkCache = new Map() 53 | /** Cache for storing YAML block boundaries by model and line number */ 54 | const yamlBlockBoundaryCache = new WeakMap>() 55 | 56 | /** 57 | * Retrieves the lines of content from a vscode document, using cache when available. 58 | * 59 | * @param {vscode.TextDocument} document - The VS Code text document 60 | * @returns {string[]} - Array of text lines from the document 61 | */ 62 | function getModelLines (document: vscode.TextDocument): string[] { 63 | let lines = lineContentCache.get(document) 64 | if (!lines) { 65 | lines = document.getText().split('\n') 66 | lineContentCache.set(document, lines) 67 | } 68 | return lines 69 | } 70 | 71 | /** 72 | * Invalidates all caches associated with a specific VS Code text document. 73 | * Should be called when the document content changes or when starting a new completion request. 74 | * 75 | * @param {vscode.TextDocument} document - The VS Code text document to invalidate caches for 76 | */ 77 | function invalidateLineCache (document: vscode.TextDocument): void { 78 | lineContentCache.delete(document) 79 | yamlBlockBoundaryCache.delete(document) 80 | } 81 | 82 | /** 83 | * Retrieves cached prop name conversions or generates new ones for a component's prop. 84 | * 85 | * @param {string} componentName - The MDC component name 86 | * @param {string} propName - The property name to convert 87 | * @returns {{ kebab: string; camel: string }} - Object containing both kebab-case and camelCase versions 88 | */ 89 | function getCachedPropNames (componentName: string, propName: string): { kebab: string, camel: string } { 90 | const cacheKey = `${componentName}:${propName}` 91 | let cached = propNameCache.get(cacheKey) 92 | if (!cached) { 93 | cached = { 94 | kebab: kebabCase(propName), 95 | camel: camelCase(propName) 96 | } 97 | propNameCache.set(cacheKey, cached) 98 | } 99 | return cached 100 | } 101 | 102 | /** 103 | * Retrieves nested props from a component's prop schema, using cache when available. 104 | * 105 | * @param {MDCComponentData} component - The MDC component 106 | * @param {any} prop - The prop to extract nested props from 107 | * @returns {Record | null} - Object containing nested props or null if none exist 108 | */ 109 | function getNestedProps (component: MDCComponentData, prop: any): Record | null { 110 | if (!component.mdc_name) { return null } 111 | 112 | let componentCache = nestedPropsCache.get(component.mdc_name) 113 | if (!componentCache) { 114 | componentCache = new Map() 115 | nestedPropsCache.set(component.mdc_name, componentCache) 116 | } 117 | 118 | const cacheKey = prop.name 119 | let cached = componentCache.get(cacheKey) 120 | if (cached === undefined) { 121 | if (!prop.schema?.schema) { 122 | cached = null 123 | } else { 124 | const objectSchema = Object.values(prop.schema.schema).find((s: any) => typeof s === 'object' && s?.kind === 'object') 125 | cached = objectSchema ? (objectSchema as { kind: string, type: string, schema: Record }).schema : null 126 | } 127 | componentCache.set(cacheKey, cached) 128 | } 129 | return cached 130 | } 131 | 132 | /** 133 | * Determines the value type of a prop, using cache when available. 134 | * 135 | * @param {MDCComponentData} component - The MDC component 136 | * @param {any} prop - The prop to determine the type for 137 | * @returns {MdcPropType} - The determined prop value type 138 | */ 139 | function getPropValueType (component: MDCComponentData, prop: any): MdcPropType { 140 | if (!component.mdc_name) { return 'string' } 141 | 142 | let componentCache = propTypeCache.get(component.mdc_name) 143 | if (!componentCache) { 144 | componentCache = new Map() 145 | propTypeCache.set(component.mdc_name, componentCache) 146 | } 147 | 148 | const cacheKey = prop.name 149 | let propType = componentCache.get(cacheKey) 150 | if (!propType) { 151 | if (prop.type?.includes('Record<') || prop.type?.toLowerCase()?.includes('object') || prop.type?.includes('Array | undefined)?.schema && Object.values((prop.schema as Record | undefined)?.schema).some((s: any) => typeof s === 'object' && s?.kind === 'object')) { 164 | propType = 'object' 165 | } else { 166 | propType = 'string' 167 | } 168 | componentCache.set(cacheKey, propType) 169 | } 170 | return propType 171 | } 172 | 173 | /** 174 | * Determines if the current position is inside a MDC block component. 175 | * 176 | * @param {vscode.TextDocument} document - The VS Code text document. 177 | * @param {number} lineNumber - The 1-based line number of the current cursor position. 178 | * @returns {boolean} - True if inside a MDC block component, false otherwise. 179 | */ 180 | function isInsideMDCComponent (document: vscode.TextDocument, lineNumber: number): boolean { 181 | const lines = getModelLines(document) 182 | const componentStack: string[] = [] 183 | 184 | for (let i = 0; i < lineNumber; i++) { 185 | const line = lines?.[i]?.trim() 186 | if (!line) { 187 | continue 188 | } 189 | 190 | // Check for component start 191 | const startMatch = line.match(MDC_COMPONENT_START_REGEX) 192 | if (startMatch) { 193 | componentStack.push(startMatch[1]) 194 | continue 195 | } 196 | 197 | // Check for component end 198 | if (line === '::') { 199 | componentStack.pop() 200 | } 201 | } 202 | 203 | // If stack has any components, we're inside at least one MDC component 204 | return componentStack.length > 0 205 | } 206 | 207 | /** 208 | * Determines if the current position is inside a YAML block. 209 | * 210 | * @param {vscode.TextDocument} document - The VS Code text document. 211 | * @param {number} lineNumber - The 1-based line number of the current cursor position. 212 | * @returns {boolean} - True if inside a YAML block, false otherwise. 213 | */ 214 | function isInsideYAMLBlock (document: vscode.TextDocument, lineNumber: number): boolean { 215 | const lines = getModelLines(document) 216 | let insideYAMLBlock = false 217 | 218 | for (let i = 0; i < lineNumber; i++) { 219 | const line = lines?.[i]?.trim() 220 | if (!line) { 221 | continue 222 | } 223 | // Toggle insideYAMLBlock flag when encountering YAML block delimiters (---) 224 | if (/^\s*---\s*$/.test(line)) { 225 | insideYAMLBlock = !insideYAMLBlock 226 | } 227 | } 228 | 229 | // Return true if inside a YAML block 230 | return insideYAMLBlock 231 | } 232 | 233 | /** 234 | * Determines if the current position is inside a markdown code block. 235 | * 236 | * @param {vscode.TextDocument} document - The VS Code text document. 237 | * @param {number} lineNumber - The 1-based line number of the current cursor position. 238 | * @returns {boolean} - True if inside a markdown code block, false otherwise. 239 | */ 240 | function isInsideCodeBlock (document: vscode.TextDocument, lineNumber: number): boolean { 241 | const lines = getModelLines(document) 242 | let insideCodeBlock = false 243 | 244 | for (let i = 0; i < lineNumber; i++) { 245 | const line = lines?.[i]?.trim() 246 | if (!line) { 247 | continue 248 | } 249 | // Toggle insideCodeBlock flag when encountering a code block delimiter (``` or ~~~) 250 | if (/^\s*(?:`{3,}|~{3,})/.test(line)) { 251 | insideCodeBlock = !insideCodeBlock 252 | } 253 | } 254 | 255 | // Return true if inside a markdown code block 256 | return insideCodeBlock 257 | } 258 | 259 | /** 260 | * Determines if the current position is inside a YAML multiline string. 261 | * Returns false when cursor returns to the same indentation level as the multiline property. 262 | * 263 | * @param {vscode.TextDocument} document - The VS Code text document. 264 | * @param {number} lineNumber - The 1-based line number of the current cursor position. 265 | * @returns {boolean} - True if inside a YAML multiline string, false otherwise. 266 | */ 267 | function isInsideYAMLMultilineString (document: vscode.TextDocument, lineNumber: number): boolean { 268 | const lines = getModelLines(document) 269 | const currentLine = lines[lineNumber - 1] 270 | const currentIndentation = currentLine.length - currentLine.trimStart().length 271 | let multilineStartIndentation: number | null = null 272 | 273 | // Work backwards from current line to find the last multiline string start 274 | for (let i = lineNumber - 1; i >= 0; i--) { 275 | const line = lines[i] 276 | const trimmedLine = line.trim() 277 | const lineIndentation = line.length - line.trimStart().length 278 | 279 | // If we find a YAML marker (---) before a multiline marker, we're not in a multiline string 280 | if (trimmedLine === '---') { 281 | return false 282 | } 283 | 284 | // Check for multiline string start 285 | MULTILINE_STRING_REGEX.lastIndex = 0 286 | if (MULTILINE_STRING_REGEX.test(trimmedLine)) { 287 | multilineStartIndentation = lineIndentation 288 | break 289 | } 290 | } 291 | 292 | // If we didn't find a multiline string start, or we're at the same indentation level 293 | // as the multiline property, we're not inside the multiline string 294 | if (multilineStartIndentation === null || currentIndentation <= multilineStartIndentation) { 295 | return false 296 | } 297 | 298 | return true 299 | } 300 | 301 | /** 302 | * Generates a string representing slot content. 303 | */ 304 | function getSlotContent (cursorIndex?: number): string { 305 | const placeholderText = '' 306 | // Two spaces before the content to auto-indent 307 | return typeof cursorIndex === 'number' ? '\n${' + cursorIndex + ':' + placeholderText + '}' : `\n${placeholderText}` 308 | } 309 | 310 | /** 311 | * Retrieves the name of the current MDC component at a given line number in a VS Code text document. 312 | * 313 | * This function scans through the lines of the provided text document up to the specified line number, 314 | * maintaining a stack of component names to determine the current nesting level of MDC components. 315 | * 316 | * @param {vscode.TextDocument} document - The VS Code text document. 317 | * @param {number} lineNumber - The 1-based line number of the current cursor position. 318 | * @returns {string} - The name of the current MDC component at the specified line number, or `undefined` if no component is found. 319 | */ 320 | function getCurrentMDCComponentName (document: vscode.TextDocument, lineNumber: number): string | undefined { 321 | const lines = getModelLines(document) 322 | const componentStack: Array<{ name: string, line: number }> = [] 323 | 324 | // Scan through lines up to current position 325 | for (let i = 0; i < lineNumber; i++) { 326 | const line = lines?.[i]?.trim() 327 | if (!line) { 328 | continue 329 | } 330 | 331 | // Check for component start 332 | const startMatch = line.match(MDC_COMPONENT_START_REGEX) 333 | if (startMatch) { 334 | componentStack.push({ name: startMatch[1], line: i }) 335 | continue 336 | } 337 | 338 | // Check for component end 339 | if (line === '::') { 340 | componentStack.pop() 341 | } 342 | } 343 | 344 | // Get the most recently added component (last item in stack) 345 | // This will be the innermost component at the cursor position 346 | const currentComponent = componentStack[componentStack.length - 1] 347 | return currentComponent?.name 348 | } 349 | 350 | /** 351 | * Gets the documentation link for a component, using cache when available. 352 | * 353 | * @param {MDCComponentData | undefined} component - The MDC component 354 | * @returns {string} - Markdown formatted documentation link or empty string 355 | */ 356 | function getComponentDocsLink (component?: MDCComponentData): string { 357 | if (!component?.mdc_name) { return '' } 358 | 359 | let cached = docsLinkCache.get(component.mdc_name) 360 | if (cached === undefined) { 361 | cached = (!component.docs_url) 362 | ? '' 363 | : `[View the '${component.mdc_name}' docs ↗](${component.docs_url})` 364 | docsLinkCache.set(component.mdc_name, cached) 365 | } 366 | 367 | return cached 368 | } 369 | 370 | /** 371 | * Gets the boundaries of a YAML block at a specific line, using cache when available. 372 | * 373 | * @param {vscode.TextDocument} document - The VS Code text document 374 | * @param {number} lineNumber - The 1-based line number to get YAML block boundaries for 375 | * @returns {{ start: number; end: number } | undefined} - Object containing block boundaries or undefined if not in a YAML block 376 | */ 377 | function getYAMLBlockBoundaries (document: vscode.TextDocument, lineNumber: number): { start: number, end: number } | undefined { 378 | let documentCache = yamlBlockBoundaryCache.get(document) 379 | if (!documentCache) { 380 | documentCache = new Map() 381 | yamlBlockBoundaryCache.set(document, documentCache) 382 | } 383 | 384 | let boundaries = documentCache.get(lineNumber) 385 | if (!boundaries) { 386 | const lines = getModelLines(document) 387 | let blockStart = -1 388 | let blockEnd = lines.length 389 | 390 | // Find start of current YAML block 391 | for (let i = lineNumber - 1; i >= 0; i--) { 392 | if (lines[i].trim() === '---') { 393 | blockStart = i 394 | break 395 | } 396 | } 397 | 398 | // Find end of current YAML block 399 | for (let i = lineNumber; i < lines.length; i++) { 400 | if (lines[i].trim() === '---') { 401 | blockEnd = i 402 | break 403 | } 404 | } 405 | 406 | if (blockStart !== -1) { 407 | boundaries = { start: blockStart, end: blockEnd } 408 | documentCache.set(lineNumber, boundaries) 409 | } 410 | } 411 | 412 | return boundaries 413 | } 414 | 415 | /** 416 | * Get the current YAML path based on indentation levels and MDC component context 417 | */ 418 | function getCurrentYAMLPath (document: vscode.TextDocument, lineNumber: number): string[] { 419 | const lines = getModelLines(document) 420 | const path: string[] = [] 421 | 422 | // Handle case where we're at line start 423 | if (lineNumber <= 0 || lineNumber > lines.length) { return path } 424 | 425 | // Get current line, do **not** need to subtract 1 426 | const currentLine = lines[lineNumber] 427 | const currentIndentation = currentLine.length - currentLine.trimStart().length 428 | 429 | const boundaries = getYAMLBlockBoundaries(document, lineNumber) 430 | if (!boundaries) { return path } 431 | 432 | let lastIndentation = currentIndentation 433 | // Scan backwards to find parent prop 434 | for (let i = lineNumber - 1; i >= boundaries.start; i--) { 435 | const line = lines[i] 436 | const lineIndentation = line.length - line.trimStart().length 437 | const trimmedLine = line.trim() 438 | 439 | // Skip empty lines, YAML block markers, or lines beyond the block end 440 | if (!trimmedLine || trimmedLine === '---' || i > boundaries.end) { continue } 441 | 442 | // A line is a potential parent if: 443 | // 1. It ends with a colon (allowing for whitespace) 444 | // 2. It has less indentation than current line 445 | const isParentProp = trimmedLine.match(/:\s*$/) && lineIndentation < lastIndentation 446 | 447 | if (isParentProp) { 448 | const propMatch = trimmedLine.match(/^([\w-]+):/)?.[1] 449 | if (propMatch) { 450 | path.unshift(propMatch) 451 | lastIndentation = lineIndentation 452 | } 453 | } 454 | } 455 | 456 | return path 457 | } 458 | 459 | /** 460 | * Get existing props from the current YAML block 461 | */ 462 | function getCurrentYAMLBlockProps (document: vscode.TextDocument, lineNumber: number): Set { 463 | const lines = getModelLines(document) 464 | const existingProps = new Set() 465 | const currentIndentation = lines[lineNumber - 1].length - lines[lineNumber - 1].trimStart().length 466 | 467 | const boundaries = getYAMLBlockBoundaries(document, lineNumber) 468 | if (!boundaries) { return existingProps } 469 | 470 | // Extract props at the same indentation level from the current YAML block 471 | for (let i = boundaries.start + 1; i < boundaries.end; i++) { 472 | const line = lines[i] 473 | const lineIndentation = line.length - line.trimStart().length 474 | 475 | // Only process lines at the same indentation level as the cursor 476 | if (lineIndentation === currentIndentation) { 477 | const match = line.trim().match(/^([\w-]+):/) 478 | if (match) { 479 | const propName = match[1] 480 | existingProps.add(kebabCase(propName)) 481 | existingProps.add(camelCase(propName)) 482 | } 483 | } 484 | } 485 | 486 | return existingProps 487 | } 488 | 489 | /** 490 | * Generate the VS Code completion item provider for MDC components. 491 | * 492 | * @param {MDCComponentData[]} componentData - The MDC component data 493 | * @param {MdcCompletionItemProviderConfig} { document, position } 494 | * @return {*} {(vscode.CompletionItem[] | undefined)} 495 | */ 496 | export function getMdcComponentCompletionItemProvider (componentData: MDCComponentData[] = [], { document, position }: MdcCompletionItemProviderConfig): vscode.CompletionItem[] | undefined { 497 | // Get the text until the current cursor position 498 | const lineContent = document.lineAt(position.line).text 499 | const textUntilPosition = lineContent.slice(0, position.character) 500 | 501 | /** 502 | * Define basic pattern to identify MDC component usage, 503 | * meaning a line that starts with a colon or double colon. 504 | * 505 | * Example: `:` or `::` - it will then suggest MDC component names. 506 | */ 507 | const mdcPattern = /^\s*:{1,}[a-zA-Z-]{0,}\s*$/ 508 | 509 | // If conditions not met, exit early 510 | if ( 511 | !mdcPattern.test(textUntilPosition) || // If it doesn't match the syntax 512 | componentData.length === 0 || // If there is no component data 513 | isInsideYAMLBlock(document, position.line) || // If inside a YAML block 514 | isInsideCodeBlock(document, position.line) // If inside a code block 515 | ) { 516 | return 517 | } 518 | 519 | // Count the number of `:` colon characters in the input text in order to properly match in the output 520 | const colonCharacterCount = (textUntilPosition.match(/:/g) || []).length 521 | // Ensure there is always a minimum of 2 colons 522 | const blockSeparator = ':'.repeat(Math.max(2, colonCharacterCount)) 523 | 524 | function getMdcComponentInsertText ({ name, contentPlaceholder = '' }: { 525 | /** The MDC component name */ 526 | name: string 527 | contentPlaceholder?: string 528 | }) { 529 | const componentName = kebabCase(name) 530 | const propsPlaceholder = '\n---${1:}\n---' 531 | 532 | // If the colon character count is 1, add a colon before the component name to make it a block component 533 | return `${colonCharacterCount === 1 ? ':' : ''}${componentName}${propsPlaceholder}${contentPlaceholder}\n${blockSeparator}\n` 534 | } 535 | 536 | // Get the word at current position 537 | const wordRange = document.getWordRangeAtPosition(position) 538 | // const wordInfo = wordRange ? document.getText(wordRange) : '' 539 | // Create a Map to store unique suggestions 540 | const uniqueSuggestions = new Map() 541 | 542 | // Loop through the component data and generate suggestions 543 | for (const component of componentData) { 544 | if (!!component.mdc_name && !uniqueSuggestions.has(component.mdc_name)) { 545 | const docsMarkdownLink = getComponentDocsLink(component) 546 | const documentationMarkdown = component.documentation_markdown ? component.documentation_markdown : component.docs_url ? docsMarkdownLink : undefined 547 | 548 | uniqueSuggestions.set(component.mdc_name, { 549 | label: component.mdc_name, 550 | kind: vscode.CompletionItemKind.Function, 551 | range: wordRange, 552 | insertText: new vscode.SnippetString(getMdcComponentInsertText({ 553 | name: component.mdc_name, 554 | // Conditionally render the default slot content placeholder if the component has slots 555 | contentPlaceholder: component.component_meta?.meta?.slots?.length ? getSlotContent(2) : '' 556 | })), 557 | detail: component.description, 558 | documentation: documentationMarkdown 559 | ? new vscode.MarkdownString(documentationMarkdown) 560 | : undefined, 561 | command: { 562 | command: 'editor.action.formatDocument', 563 | title: 'Format Document' 564 | } 565 | }) 566 | } 567 | } 568 | 569 | // Create an array of unique suggestions from the Map 570 | const suggestions = Array.from(uniqueSuggestions.values()) 571 | 572 | return suggestions 573 | } 574 | 575 | /** 576 | * Generate the VS Code completion item provider for MDC component props. 577 | * 578 | * @param {MDCComponentData[]} componentData - The MDC component data 579 | * @param {MdcCompletionItemProviderConfig} { document, position }: MdcCompletionItemProviderConfig 580 | * @return {*} {(vscode.CompletionItem[] | undefined)} 581 | */ 582 | export function getMdcComponentPropCompletionItemProvider (componentData: MDCComponentData[] = [], { document, position }: MdcCompletionItemProviderConfig): vscode.CompletionItem[] | undefined { 583 | // Invalidate cache at the start of a new completion request 584 | invalidateLineCache(document) 585 | // Get the text until the current cursor position 586 | const lineContent = document.lineAt(position.line).text 587 | const textUntilPosition = lineContent.slice(0, position.character) 588 | 589 | /** 590 | * Define basic pattern to identify MDC component prop usage, 591 | * meaning a line that starts with at least one alphabetic character. 592 | * 593 | * Example: `a` or ` a` - it will then suggest MDC component props. 594 | */ 595 | const propNamePattern = /^\s*[a-zA-Z-]{0,}$/ 596 | 597 | // If conditions not met, exit early 598 | if ( 599 | !propNamePattern.test(textUntilPosition) || // If it doesn't match the syntax 600 | componentData.length === 0 || // If there is no component data 601 | !isInsideMDCComponent(document, position.line) || // If NOT inside a MDC block component 602 | !isInsideYAMLBlock(document, position.line) || // If NOT inside a YAML block 603 | isInsideCodeBlock(document, position.line) || // If inside a code block 604 | isInsideYAMLMultilineString(document, position.line) // If inside a YAML multiline string 605 | ) { 606 | return 607 | } 608 | 609 | const currentYAMLPath = getCurrentYAMLPath(document, position.line) 610 | const currentMdcBlockName = getCurrentMDCComponentName(document, position.line) 611 | 612 | if (!currentMdcBlockName) { return } 613 | 614 | const mdcComponent = componentData.find(c => c.mdc_name === currentMdcBlockName) 615 | const docsMarkdownLink = getComponentDocsLink(mdcComponent) 616 | if (!mdcComponent?.component_meta?.meta?.props) { return } 617 | 618 | // Get the word at current position 619 | const wordRange = document.getWordRangeAtPosition(position) 620 | // const wordInfo = wordRange ? document.getText(wordRange) : '' 621 | const suggestionsByComponent = new Map() 622 | suggestionsByComponent.set(currentMdcBlockName, []) 623 | 624 | // Get either top-level props or nested props based on YAML path 625 | let propsToSuggest: any[] = [] 626 | if (currentYAMLPath.length > 0) { 627 | // Find the parent prop 628 | const parentProp = mdcComponent.component_meta.meta.props.find(p => 629 | kebabCase(p.name) === currentYAMLPath[0] 630 | ) 631 | 632 | if (parentProp) { 633 | const nestedProps = getNestedProps(mdcComponent, parentProp) 634 | if (nestedProps) { 635 | // Convert nested props schema to array format 636 | propsToSuggest = Object.entries(nestedProps).map(([name, schema]) => ({ 637 | name, 638 | ...(schema as object) 639 | })) 640 | } 641 | } 642 | } else { 643 | // Use top-level props 644 | propsToSuggest = mdcComponent.component_meta.meta.props 645 | } 646 | 647 | const existingProps = getCurrentYAMLBlockProps(document, position.line) 648 | 649 | for (const prop of propsToSuggest) { 650 | const { kebab: propNameKebab, camel: propNameCamel } = getCachedPropNames(currentMdcBlockName, prop.name) 651 | 652 | // Skip existing props 653 | if (existingProps.has(propNameKebab) || existingProps.has(propNameCamel)) { continue } 654 | 655 | // Determine the prop value type 656 | const propValueType = getPropValueType(mdcComponent, prop) 657 | 658 | function getPropInsertText (name: string, type: string) { 659 | if (prop.name === 'styles') { 660 | // Special handling for the `styles` prop since it's always a multiline string 661 | return `${name}: |\n` + ' ${0:/** Add CSS */}' 662 | } else if (type === 'boolean') { 663 | return `${name}: ` + '${0:true}' 664 | } else if (type === 'number') { 665 | return `${name}: ` + '${0:}' 666 | } else if (type === 'array') { 667 | return `${name}: ` + '["${0:}"]' 668 | } else if (type === 'array-unquoted') { 669 | return `${name}: ` + '[${0:}]' 670 | } else if (type === 'string') { 671 | return `${name}: ` + '"${0:}"' 672 | } else { 673 | // Object 674 | return `${name}: ` + '\n ${0:}' 675 | } 676 | } 677 | 678 | suggestionsByComponent.get(currentMdcBlockName)!.push({ 679 | // @ts-ignore - This satisfies the `CompletionItemLabel` interface: https://code.visualstudio.com/api/references/vscode-api#CompletionItemLabel 680 | label: { 681 | label: propNameKebab, 682 | description: prop.type?.replace('| undefined', '').replace('| null', ''), // Shows up as gray text next to the label 683 | detail: prop.required ? ' (required)' : undefined // Shows up as dimmed text after description 684 | }, 685 | filterText: `${propNameKebab} ${propNameCamel}`, 686 | sortText: prop.required ? '_' + propNameKebab : propNameKebab, // Force "required" props to the top 687 | kind: vscode.CompletionItemKind.Property, 688 | range: wordRange, 689 | insertText: new vscode.SnippetString(getPropInsertText(propNameKebab, propValueType)), // Always insert kebab-case 690 | detail: prop.description, // Shows up in the details section of the completion item 691 | documentation: new vscode.MarkdownString(docsMarkdownLink), 692 | command: { 693 | command: 'editor.action.triggerSuggest', 694 | title: 'Trigger Suggestions' 695 | } 696 | }) 697 | } 698 | 699 | return Array.from(suggestionsByComponent.values()).flat() 700 | } 701 | 702 | /** 703 | * Creates document lifecycle listeners to manage cache cleanup 704 | */ 705 | export function createCacheCleanupListeners (): vscode.Disposable { 706 | const watchedDocuments = new Set() 707 | 708 | /** 709 | * Check if the document is a markdown or MDC file by checking both extension and language ID 710 | */ 711 | function isMarkdownOrMDCDocument (document: vscode.TextDocument): boolean { 712 | const extension = document.uri.fsPath.toLowerCase() 713 | const isMDCOrMDFile = extension.endsWith('.mdc') || extension.endsWith('.md') 714 | const isMDCOrMDLanguage = document.languageId === 'mdc' || document.languageId === 'markdown' 715 | return isMDCOrMDFile || isMDCOrMDLanguage 716 | } 717 | 718 | const disposable = vscode.Disposable.from( 719 | // Listen for document close events 720 | vscode.workspace.onDidCloseTextDocument((document) => { 721 | if (!isMarkdownOrMDCDocument(document)) { return } 722 | 723 | const uri = document.uri.toString() 724 | if (watchedDocuments.has(uri)) { 725 | cleanupDocumentCaches(document) 726 | watchedDocuments.delete(uri) 727 | } 728 | }), 729 | 730 | // Listen for document open events 731 | vscode.workspace.onDidOpenTextDocument((document) => { 732 | if (!isMarkdownOrMDCDocument(document)) { return } 733 | 734 | watchedDocuments.add(document.uri.toString()) 735 | }) 736 | ) 737 | 738 | // Return disposable that cleans up everything 739 | return { 740 | dispose: () => { 741 | disposable.dispose() 742 | // Clean up any remaining caches 743 | propNameCache.clear() 744 | nestedPropsCache.clear() 745 | propTypeCache.clear() 746 | docsLinkCache.clear() 747 | watchedDocuments.clear() 748 | } 749 | } 750 | } 751 | 752 | /** 753 | * Cleans up all caches associated with a document 754 | */ 755 | function cleanupDocumentCaches (document: vscode.TextDocument): void { 756 | logger('Clean up caches for document: ' + document.uri.toString()) 757 | // Clear document-specific caches 758 | lineContentCache.delete(document) 759 | yamlBlockBoundaryCache.delete(document) 760 | 761 | // Clear component-related caches if this is the last document 762 | if (vscode.workspace.textDocuments.length <= 1) { 763 | logger('Clean up component-related caches') 764 | propNameCache.clear() 765 | nestedPropsCache.clear() 766 | propTypeCache.clear() 767 | docsLinkCache.clear() 768 | } 769 | } 770 | --------------------------------------------------------------------------------