├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json └── src ├── README.md ├── vue.grammar ├── vue.grammar.d.ts └── vue.ts /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | package-lock.json 3 | /dist 4 | /test/*.js 5 | /test/*.d.ts 6 | /test/*.d.ts.map 7 | .tern-* 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /src 2 | /test 3 | /node_modules 4 | .tern-* 5 | rollup.config.js 6 | tsconfig.json 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.3 (2023-12-28) 2 | 3 | ### Bug fixes 4 | 5 | Tag interpolations and attributes as isolating for the purpose of bidirectional text. 6 | 7 | ## 0.1.2 (2023-06-13) 8 | 9 | ### Bug fixes 10 | 11 | HTML support extensions now work in Vue mode. 12 | 13 | ### New features 14 | 15 | `vue` now allows a base HTML configuration to be passed in. 16 | 17 | ## 0.1.1 (2023-01-16) 18 | 19 | ### Bug fixes 20 | 21 | Support self-closing tags in templates. 22 | 23 | ## 0.1.0 (2023-01-13) 24 | 25 | ### Breaking changes 26 | 27 | First numbered release. 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2018-2021 by Marijn Haverbeke and others 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # @codemirror/lang-vue [![NPM version](https://img.shields.io/npm/v/@codemirror/lang-vue.svg)](https://www.npmjs.org/package/@codemirror/lang-vue) 4 | 5 | [ [**WEBSITE**](https://codemirror.net/) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lang-vue/blob/main/CHANGELOG.md) ] 6 | 7 | This package implements Vue template support for the 8 | [CodeMirror](https://codemirror.net/) code editor. 9 | 10 | The [project page](https://codemirror.net/) has more information, a 11 | number of [examples](https://codemirror.net/examples/) and the 12 | [documentation](https://codemirror.net/docs/). 13 | 14 | This code is released under an 15 | [MIT license](https://github.com/codemirror/lang-vue/tree/main/LICENSE). 16 | 17 | We aim to be an inclusive, welcoming community. To make that explicit, 18 | we have a [code of 19 | conduct](http://contributor-covenant.org/version/1/1/0/) that applies 20 | to communication around the project. 21 | 22 | ## Usage 23 | 24 | ```javascript 25 | import {EditorView, basicSetup} from "codemirror" 26 | import {vue} from "@codemirror/lang-vue" 27 | 28 | const view = new EditorView({ 29 | parent: document.body, 30 | doc: `

{{ message }}

`, 31 | extensions: [basicSetup, vue()] 32 | }) 33 | ``` 34 | 35 | ## API Reference 36 | 37 |
38 |
39 | vue(config⁠?: Object = {}) → LanguageSupport
40 | 41 |

Vue template support.

42 |
43 | config
44 | 45 |
46 | base⁠?: LanguageSupport
47 | 48 |

Provide an HTML language configuration to use as a base. Must 49 | be the result of calling html() from @codemirror/lang-html, 50 | not just any LanguageSupport object.

51 |
52 |
53 | vueLanguage: LRLanguage
54 | 55 |

A language provider for Vue templates.

56 |
57 |
58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codemirror/lang-vue", 3 | "version": "0.1.3", 4 | "description": "Vue template support for the CodeMirror code editor", 5 | "scripts": { 6 | "test": "cm-runtests", 7 | "prepare": "cm-buildhelper src/vue.ts" 8 | }, 9 | "keywords": [ 10 | "editor", 11 | "code" 12 | ], 13 | "author": { 14 | "name": "Marijn Haverbeke", 15 | "email": "marijn@haverbeke.berlin", 16 | "url": "http://marijnhaverbeke.nl" 17 | }, 18 | "type": "module", 19 | "main": "dist/index.cjs", 20 | "exports": { 21 | "import": "./dist/index.js", 22 | "require": "./dist/index.cjs" 23 | }, 24 | "types": "dist/index.d.ts", 25 | "module": "dist/index.js", 26 | "sideEffects": false, 27 | "license": "MIT", 28 | "dependencies": { 29 | "@codemirror/language": "^6.0.0", 30 | "@codemirror/lang-html": "^6.0.0", 31 | "@codemirror/lang-javascript": "^6.1.2", 32 | "@lezer/lr": "^1.3.1", 33 | "@lezer/common": "^1.2.0", 34 | "@lezer/highlight": "^1.0.0" 35 | }, 36 | "devDependencies": { 37 | "@codemirror/buildhelper": "^1.0.0" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/codemirror/lang-vue.git" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # @codemirror/lang-vue [![NPM version](https://img.shields.io/npm/v/@codemirror/lang-vue.svg)](https://www.npmjs.org/package/@codemirror/lang-vue) 4 | 5 | [ [**WEBSITE**](https://codemirror.net/) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lang-vue/blob/main/CHANGELOG.md) ] 6 | 7 | This package implements Vue template support for the 8 | [CodeMirror](https://codemirror.net/) code editor. 9 | 10 | The [project page](https://codemirror.net/) has more information, a 11 | number of [examples](https://codemirror.net/examples/) and the 12 | [documentation](https://codemirror.net/docs/). 13 | 14 | This code is released under an 15 | [MIT license](https://github.com/codemirror/lang-vue/tree/main/LICENSE). 16 | 17 | We aim to be an inclusive, welcoming community. To make that explicit, 18 | we have a [code of 19 | conduct](http://contributor-covenant.org/version/1/1/0/) that applies 20 | to communication around the project. 21 | 22 | ## Usage 23 | 24 | ```javascript 25 | import {EditorView, basicSetup} from "codemirror" 26 | import {vue} from "@codemirror/lang-vue" 27 | 28 | const view = new EditorView({ 29 | parent: document.body, 30 | doc: `

{{ message }}

`, 31 | extensions: [basicSetup, vue()] 32 | }) 33 | ``` 34 | 35 | ## API Reference 36 | 37 | @vue 38 | 39 | @vueLanguage 40 | -------------------------------------------------------------------------------- /src/vue.grammar: -------------------------------------------------------------------------------- 1 | @top Content { 2 | (Text | Interpolation | Entity)+ 3 | } 4 | 5 | @skip { space } { 6 | @top Attribute { 7 | (VueAttributeName (":" Identifier)? ("." Identifier)* | 8 | (":" | "@") Identifier ("." Identifier)*) (Is ScriptAttributeValue)? | 9 | AttributeName (Is AttributeValue)? 10 | } 11 | } 12 | 13 | AttributeValue[isolate] { 14 | '"' (attrContentDouble | attrEntityDouble)* endAttributeDouble | 15 | "'" (attrContentSingle | attrEntitySingle)* endAttributeSingle 16 | } 17 | 18 | ScriptAttributeValue[isolate] { 19 | '"' scriptAttrContentDouble? endScriptAttrDouble | 20 | "'" scriptAttrContentSingle? endScriptAttrSingle 21 | } 22 | 23 | Interpolation[isolate] { 24 | interpolationStart InterpolationContent? interpolationEnd 25 | } 26 | 27 | @local tokens { 28 | interpolationEnd[@name="}}"] { "}}" } 29 | @else InterpolationContent 30 | } 31 | 32 | @local tokens { 33 | interpolationStart { "{{" } 34 | Entity { "&" (![; ]+ ";")? } 35 | @else Text 36 | } 37 | 38 | @local tokens { 39 | endScriptAttrSingle { "'" } 40 | @else scriptAttrContentSingle[@name=AttributeScript] 41 | } 42 | 43 | @local tokens { 44 | endScriptAttrDouble { '"' } 45 | @else scriptAttrContentDouble[@name=AttributeScript] 46 | } 47 | 48 | @local tokens { 49 | endAttributeSingle { "'" } 50 | attrEntitySingle[@name=Entity] { "&" (![; ]+ ";")? } 51 | @else attrContentSingle 52 | } 53 | 54 | @local tokens { 55 | endAttributeDouble { '"' } 56 | attrEntityDouble[@name=Entity] { "&" (![; ]+ ";")? } 57 | @else attrContentDouble 58 | } 59 | 60 | @tokens { 61 | AttributeName { (":" | "-" | @asciiLetter | @digit | "_" | $[\u00C0-\u{EFFFF}])+ } 62 | 63 | VueAttributeName { "v-" @asciiLetter+ } 64 | 65 | @precedence { VueAttributeName AttributeName } 66 | 67 | @precedence { ":" AttributeName } 68 | 69 | Identifier { (@asciiLetter | @digit | "_" | $[\u00C0-\u{EFFFF}])+ } 70 | 71 | space { (" " | "\t" | "\r" | "\n")+ } 72 | 73 | Is { "=" } 74 | 75 | "{{" "}}" "@" ":" 76 | } 77 | 78 | @detectDelim 79 | -------------------------------------------------------------------------------- /src/vue.grammar.d.ts: -------------------------------------------------------------------------------- 1 | import {LRParser} from "@lezer/lr" 2 | export declare const parser: LRParser 3 | -------------------------------------------------------------------------------- /src/vue.ts: -------------------------------------------------------------------------------- 1 | import {LRLanguage, LanguageSupport} from "@codemirror/language" 2 | import {html} from "@codemirror/lang-html" 3 | import {javascriptLanguage} from "@codemirror/lang-javascript" 4 | import {styleTags, tags as t} from "@lezer/highlight" 5 | import {parseMixed, SyntaxNodeRef, Input} from "@lezer/common" 6 | import {parser} from "./vue.grammar" 7 | 8 | const exprParser = javascriptLanguage.parser.configure({ 9 | top: "SingleExpression" 10 | }) 11 | 12 | const baseParser = parser.configure({ 13 | props: [ 14 | styleTags({ 15 | Text: t.content, 16 | Is: t.definitionOperator, 17 | AttributeName: t.attributeName, 18 | VueAttributeName: t.keyword, 19 | Identifier: t.variableName, 20 | "AttributeValue ScriptAttributeValue": t.attributeValue, 21 | Entity: t.character, 22 | "{{ }}": t.brace, 23 | "@ :": t.punctuation 24 | }) 25 | ] 26 | }) 27 | 28 | const exprMixed = {parser: exprParser} 29 | 30 | const textParser = baseParser.configure({ 31 | wrap: parseMixed((node, input) => node.name == "InterpolationContent" ? exprMixed : null), 32 | }) 33 | 34 | const attrParser = baseParser.configure({ 35 | wrap: parseMixed((node, input) => node.name == "AttributeScript" ? exprMixed : null), 36 | top: "Attribute" 37 | }) 38 | 39 | const textMixed = {parser: textParser}, attrMixed = {parser: attrParser} 40 | 41 | const baseHTML = html() 42 | 43 | function makeVue(base: LRLanguage) { 44 | return base.configure({ 45 | dialect: "selfClosing", 46 | wrap: parseMixed(mixVue) 47 | }, "vue") 48 | } 49 | 50 | /// A language provider for Vue templates. 51 | export const vueLanguage = makeVue(baseHTML.language as LRLanguage) 52 | 53 | function mixVue(node: SyntaxNodeRef, input: Input) { 54 | switch (node.name) { 55 | case "Attribute": 56 | return /^(@|:|v-)/.test(input.read(node.from, node.from + 2)) ? attrMixed : null 57 | case "Text": 58 | return textMixed 59 | } 60 | return null 61 | } 62 | 63 | /// Vue template support. 64 | export function vue(config: { 65 | /// Provide an HTML language configuration to use as a base. _Must_ 66 | /// be the result of calling `html()` from `@codemirror/lang-html`, 67 | /// not just any `LanguageSupport` object. 68 | base?: LanguageSupport 69 | } = {}) { 70 | let base = baseHTML 71 | if (config.base) { 72 | if (config.base.language.name != "html" || !(config.base.language instanceof LRLanguage)) 73 | throw new RangeError("The base option must be the result of calling html(...)") 74 | base = config.base 75 | } 76 | return new LanguageSupport(base.language == baseHTML.language ? vueLanguage : makeVue(base.language as LRLanguage), [ 77 | base.support, 78 | base.language.data.of({closeBrackets: {brackets: ["{", '"']}}) 79 | ]) 80 | } 81 | --------------------------------------------------------------------------------