Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow no v-pre on code blocks (#413) #415

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/guide/using-vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,43 @@ The compiled component does not have any private data but does have access to th
}
```

### Interpolation in Code Blocks

By default, Vuepress will add `v-pre` directive to code blocks to avoid it being compiled by Vue. If you really want to use interpolation in code blocks, add `{no-v-pre}` after the language identifier, or just use `{no-v-pre}` as the language identifier. For example:

**Input**

```` md
``` vue{no-v-pre}
<div class="with-syntax-highlighting">
One plus two equals: {{ 1 + 2 }}
</div>
```
````

```` md
``` {no-v-pre}
One plus two equals: {{ 1 + 2 }}
```
````

**Output**

``` vue{no-v-pre}
<div class="with-syntax-highlighting">
One plus two equals: {{ 1 + 2 }}
</div>
```

``` {no-v-pre}
One plus two equals: {{ 1 + 2 }}
```

::: warning IMPORTANT
For some languages, the highlighter will add extra `<span>` tag around `{` and `}`, in which cases `{no-v-pre}` won't work. If you use `{no-v-pre}` with those languages, Vuepress will disable syntax highlighting when compiling that code block. If you need syntax highlighting anyway, you have to process it youself in your client code for now.
Currently, `{no-v-pre}` and syntax highlighting only works with `md`, `markdown`, `vue`, `html`, `markup`.
:::

## Escaping

By default, fenced code blocks are automatically wrapped with `v-pre`. If you want to display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container:
Expand Down
37 changes: 37 additions & 0 deletions docs/zh/guide/using-vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,43 @@ export default {
}
```

### 在代码块中插值

Vuepress默认会在代码块添加`v-pre`指令,使其避免被Vue编译。如果你确实需要在代码块中使用插值,可以在代码块的语言标记后面添加`{no-v-pre}`,或者直接把`{no-v-pre}`作为语言标记。例如:

**Input**

```` md
``` vue{no-v-pre}
<div class="with-syntax-highlighting">
一加二等于: {{ 1 + 2 }}
</div>
```
````

```` md
``` {no-v-pre}
一加二等于:{{ 1 + 2 }}
```
````

**Output**

``` vue{no-v-pre}
<div class="with-syntax-highlighting">
一加二等于: {{ 1 + 2 }}
</div>
```

``` {no-v-pre}
一加二等于:{{ 1 + 2 }}
```

::: warning 重要!
Markdown在解析某些语言的代码块时,为了支持代码高亮,会在`{`和`}`外添加额外的`<span>`标签,这将导致Vue的模板插值失效。如果你在这些语言的代码块上使用`{no-v-pre}`,Vuepress默认会在解析代码块时禁用代码高亮。如果你一定要在这些语言中使用模板插值和代码高亮的话,你需要在客户端代码中进行额外的代码高亮处理。
目前,`{no-v-pre}`和代码高亮可以在这些语言中共同使用:`md`, `markdown`, `vue`, `html`, `markup`
:::

## Escaping

默认情况下,块级 (block) 的代码块将会被自动包裹在 `v-pre` 中。如果你想要在内联 (inline) 的代码块或者普通文本中显示原始的大括号,或者一些 Vue 特定的语法,你需要使用自定义容器 `v-pre` 来包裹:
Expand Down
41 changes: 35 additions & 6 deletions lib/markdown/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,41 @@ const loadLanguages = require('prismjs/components/index')
const escapeHtml = require('escape-html')
const logger = require('../util/logger')

const noVPreRE = /^(.*){no-v-pre}$/

// highlight will be disabled with {no-v-pre} except for these languages
const highlightableLanguagesWithNoVPre = [
'markup',
'markdown'
]

// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])

function wrap (code, lang) {
function wrap (code, lang, noVPre = false) {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre class="language-${lang}"><code>${code}</code></pre>`
const vPreAttr = noVPre ? '' : 'v-pre'
return `<pre ${vPreAttr} class="language-${lang}"><code>${code}</code></pre>`
}

module.exports = (str, lang) => {
if (!lang) {
return wrap(str, 'text')
// check if {no-v-pre} appears
const noVPreMatch = lang.match(noVPreRE)
const noVPre = noVPreMatch !== null
if (noVPre) {
lang = noVPreMatch[1]
}

// normalize the language name for prism
lang = lang.toLowerCase()

// if no language is specified, treat as 'text'
if (!lang) {
return wrap(str, 'text', noVPre)
}

const rawLang = lang
if (lang === 'vue' || lang === 'html') {
lang = 'markup'
Expand All @@ -32,6 +52,13 @@ module.exports = (str, lang) => {
if (lang === 'py') {
lang = 'python'
}

// if the language is not supported highlighting with {no-v-pre}
if (noVPre && !highlightableLanguagesWithNoVPre.includes(lang)) {
return wrap(str, 'text', noVPre)
}

// try to highlighting the code
if (!prism.languages[lang]) {
try {
loadLanguages([lang])
Expand All @@ -41,7 +68,9 @@ module.exports = (str, lang) => {
}
if (prism.languages[lang]) {
const code = prism.highlight(str, prism.languages[lang], lang)
return wrap(code, rawLang)
return wrap(code, rawLang, noVPre)
}
return wrap(str, 'text')

// if the language is not supported by prism, treat as 'text'
return wrap(str, 'text', noVPre)
}
10 changes: 8 additions & 2 deletions lib/markdown/preWrapper.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
// markdown-it plugin for wrapping <pre> ... </pre>.
//
// If your plugin was chained before preWrapper, you can add additional eleemnt directly.
// If your plugin was chained before preWrapper, you can add additional element directly.
// If your plugin was chained after preWrapper, you can use these slots:
// 1. <!--beforebegin-->
// 2. <!--afterbegin-->
// 3. <!--beforeend-->
// 4. <!--afterend-->

const noVPreRE = /^(.*){no-v-pre}$/

module.exports = md => {
const fence = md.renderer.rules.fence
md.renderer.rules.fence = (...args) => {
const [tokens, idx] = args
const token = tokens[idx]
const rawCode = fence(...args)
return `<!--beforebegin--><div class="language-${token.info.trim()} extra-class">` +
let lang = token.info.replace(noVPreRE, '$1').trim()
if (!lang) {
lang = 'text'
}
return `<!--beforebegin--><div class="language-${lang} extra-class">` +
`<!--afterbegin-->${rawCode}<!--beforeend--></div><!--afterend-->`
}
}
6 changes: 6 additions & 0 deletions test/markdown/__snapshots__/noVPre.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`no-v-pre should not have v-pre with {no-v-pre} 1`] = `
<pre class="language-vue"><code>{{ 1 + 1 }}
</code></pre>
`;
3 changes: 3 additions & 0 deletions test/markdown/fragments/code-no-v-pre.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``` vue{no-v-pre}
{{ 1 + 1 }}
```
12 changes: 12 additions & 0 deletions test/markdown/noVPre.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Md, getFragment } from './util'
import highlight from '@/markdown/highlight.js'

const md = Md().set({ highlight })

describe('no-v-pre', () => {
test('should not have v-pre with {no-v-pre}', async () => {
const input = await getFragment('code-no-v-pre')
const output = md.render(input)
expect(output).toMatchSnapshot()
})
})