* feat(plugin-md-power): enable python code repl(#585) * fix(plugin-md-power): correct grammars * fix(plugin-md-power): modify pnpm-lock.yaml * feat: tweak * chore: tweak --------- Co-authored-by: Shuo Liu <sliu84@outlook.com> Co-authored-by: pengzhanbo <volodymyr@foxmail.com>
This commit is contained in:
parent
c21c9bdefa
commit
1f89d7f515
@ -140,6 +140,7 @@ export default defineUserConfig({
|
||||
// go: true, // ::: go-repl
|
||||
// rust: true, // ::: rust-repl
|
||||
// kotlin: true, // ::: kotlin-repl
|
||||
// python: true, // ::: python-repl
|
||||
// },
|
||||
// math: { // 启用数学公式
|
||||
// type: 'katex',
|
||||
|
||||
@ -80,6 +80,7 @@ export const themeGuide: ThemeNote = defineNoteConfig({
|
||||
'rust',
|
||||
'golang',
|
||||
'kotlin',
|
||||
'python',
|
||||
'codepen',
|
||||
'jsFiddle',
|
||||
'codeSandbox',
|
||||
|
||||
@ -50,6 +50,7 @@ export const theme: Theme = plumeTheme({
|
||||
go: true,
|
||||
rust: true,
|
||||
kotlin: true,
|
||||
python: true,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@ -190,6 +190,12 @@ __语法:__
|
||||
// kotlin code
|
||||
```
|
||||
:::
|
||||
|
||||
::: python-repl
|
||||
```python
|
||||
# python code
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
请查看完整使用文档:
|
||||
@ -197,6 +203,7 @@ __语法:__
|
||||
- [代码演示 > Rust](../../guide/repl/rust.md)
|
||||
- [代码演示 > Golang](../../guide/repl/golang.md)
|
||||
- [代码演示 > Kotlin](../../guide/repl/kotlin.md)
|
||||
- [代码演示 > Python](../../guide/repl/python.md)
|
||||
|
||||
### Plot 隐秘文本
|
||||
|
||||
|
||||
153
docs/notes/theme/guide/repl/python.md
Normal file
153
docs/notes/theme/guide/repl/python.md
Normal file
@ -0,0 +1,153 @@
|
||||
---
|
||||
title: Python
|
||||
icon: devicon-plain:python
|
||||
createTime: 2025/05/03 21:53:58
|
||||
permalink: /guide/repl/python/
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
主题提供了 Python 代码演示,支持在线运行 Python 代码。
|
||||
|
||||
## 安装
|
||||
|
||||
python 在线执行由 [pyodide](https://pyodide.org/en/latest/) 提供,使用前请确保有 `pyodide` 可用
|
||||
|
||||
::: npm-to
|
||||
|
||||
```sh
|
||||
npm install pyodide
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 配置
|
||||
|
||||
该功能默认不启用,你可以通过配置来启用它。
|
||||
|
||||
```ts title=".vuepress/config.ts"
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
markdown: {
|
||||
repl: {
|
||||
python: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
使用 `::: python-repl` 容器语法 将 python 代码块包裹起来。主题会检查代码块并添加执行按钮。
|
||||
|
||||
::: warning python-repl 的支持是有限的,目前只支持:
|
||||
|
||||
- 基本 Python 语法的执行(不依赖后端)
|
||||
- 可导入 Python 基本库
|
||||
- 标准输出流(stdout)捕获输出
|
||||
- 最后一条语句是一个表达式(且代码不以分号结尾),则会返回该表达式的值。
|
||||
- 异常信息输出
|
||||
|
||||
:::
|
||||
|
||||
### 只读代码演示
|
||||
|
||||
Python 代码演示默认是只读的,不可编辑。
|
||||
|
||||
````md
|
||||
::: python-repl title="自定义标题"
|
||||
```python
|
||||
// your python code
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
### 可编辑代码演示
|
||||
|
||||
如果需要在线编辑并执行,需要将代码块包裹在 `::: python-repl editable` 容器语法中。
|
||||
|
||||
````md
|
||||
::: python-repl editable title="自定义标题"
|
||||
```python
|
||||
// your python code
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
## 示例
|
||||
|
||||
### 打印内容
|
||||
|
||||
**输入:**
|
||||
|
||||
````md
|
||||
::: python-repl
|
||||
```python
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
hello_world()
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
**输出:**
|
||||
|
||||
::: python-repl
|
||||
|
||||
```python
|
||||
def hello_world():
|
||||
print('Hello World!')
|
||||
|
||||
hello_world()
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 运算
|
||||
|
||||
::: python-repl
|
||||
|
||||
```python
|
||||
def mul(a: int, b: int) -> int:
|
||||
return a * b
|
||||
|
||||
print(mul(-2, 4))
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 可编辑代码演示
|
||||
|
||||
**输入:**
|
||||
|
||||
````md
|
||||
::: python-repl editable
|
||||
```python
|
||||
class Contact:
|
||||
def __init__(self, id: int, email: str):
|
||||
self.id = id
|
||||
self.email = email
|
||||
|
||||
contact = Contact(1, 'mary@gmail.com')
|
||||
print(contact.id)
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
**输出:**
|
||||
|
||||
::: python-repl editable
|
||||
|
||||
```python
|
||||
class Contact:
|
||||
def __init__(self, id: int, email: str):
|
||||
self.id = id
|
||||
self.email = email
|
||||
|
||||
contact = Contact(1, 'mary@gmail.com')
|
||||
print(contact.id)
|
||||
```
|
||||
|
||||
:::
|
||||
@ -47,6 +47,7 @@
|
||||
"less": "catalog:dev",
|
||||
"markdown-it": "catalog:dev",
|
||||
"mpegts.js": "^1.7.3",
|
||||
"pyodide": "catalog:peer",
|
||||
"sass": "catalog:peer",
|
||||
"sass-embedded": "catalog:peer",
|
||||
"stylus": "catalog:dev",
|
||||
@ -67,6 +68,9 @@
|
||||
},
|
||||
"mpegts.js": {
|
||||
"optional": true
|
||||
},
|
||||
"pyodide": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -11,7 +11,7 @@ let container: HTMLPreElement | null = null
|
||||
let lineNumbers: HTMLDivElement | null = null
|
||||
const { grammars, theme } = editorData
|
||||
|
||||
const lang = ref<'go' | 'rust' | 'kotlin'>()
|
||||
const lang = ref<'go' | 'rust' | 'kotlin' | 'python'>()
|
||||
|
||||
const editorEl = shallowRef<HTMLDivElement>()
|
||||
const textAreaEl = shallowRef<HTMLTextAreaElement>()
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { PyodideInterface } from 'pyodide'
|
||||
import type { Ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { http } from '../utils/http.js'
|
||||
@ -10,8 +11,9 @@ const api = {
|
||||
go: 'https://api.pengzhanbo.cn/repl/golang/run',
|
||||
kotlin: 'https://api.pengzhanbo.cn/repl/kotlin/run',
|
||||
}
|
||||
let pyodide: PyodideInterface | null = null
|
||||
|
||||
type Lang = 'kotlin' | 'go' | 'rust'
|
||||
type Lang = 'kotlin' | 'go' | 'rust' | 'python'
|
||||
type ExecuteFn = (code: string) => Promise<any>
|
||||
type ExecuteMap = Record<Lang, ExecuteFn>
|
||||
|
||||
@ -21,9 +23,11 @@ const langAlias: Record<string, string> = {
|
||||
go: 'go',
|
||||
rust: 'rust',
|
||||
rs: 'rust',
|
||||
py: 'python',
|
||||
python: 'python',
|
||||
}
|
||||
|
||||
const supportLang: Lang[] = ['kotlin', 'go', 'rust']
|
||||
const supportLang: Lang[] = ['kotlin', 'go', 'rust', 'python']
|
||||
|
||||
function resolveLang(lang?: string) {
|
||||
return lang ? langAlias[lang] || lang : ''
|
||||
@ -88,6 +92,7 @@ export function useCodeRepl(el: Ref<HTMLDivElement | null>): UseCodeReplResult {
|
||||
kotlin: executeKotlin,
|
||||
go: executeGolang,
|
||||
rust: executeRust,
|
||||
python: executePython,
|
||||
}
|
||||
|
||||
function onCleanRun(): void {
|
||||
@ -190,6 +195,24 @@ export function useCodeRepl(el: Ref<HTMLDivElement | null>): UseCodeReplResult {
|
||||
})
|
||||
}
|
||||
|
||||
async function executePython(code: string) {
|
||||
loaded.value = false
|
||||
finished.value = false
|
||||
if (pyodide === null) {
|
||||
const { loadPyodide, version } = await import(/* webpackChunkName: "pyodide" */ 'pyodide')
|
||||
pyodide = await loadPyodide({ indexURL: `https://cdn.jsdelivr.net/pyodide/v${version}/full/` })
|
||||
}
|
||||
pyodide.setStdout({ batched: msg => stdout.value.push(msg) })
|
||||
try {
|
||||
stdout.value.push(pyodide.runPython(code))
|
||||
}
|
||||
catch (e: unknown) {
|
||||
stderr.value.push(String(e as Error))
|
||||
}
|
||||
loaded.value = true
|
||||
finished.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
onRunCode,
|
||||
onCleanRun,
|
||||
|
||||
@ -44,7 +44,7 @@ export async function containerPlugin(
|
||||
}
|
||||
|
||||
if (options.repl)
|
||||
// ::: rust-repl / go-repl / kotlin-repl
|
||||
// ::: rust-repl / go-repl / kotlin-repl / python-repl
|
||||
await langReplPlugin(app, md, options.repl)
|
||||
|
||||
if (options.fileTree) {
|
||||
|
||||
@ -18,6 +18,7 @@ export async function langReplPlugin(app: App, md: markdownIt, {
|
||||
go = false,
|
||||
kotlin = false,
|
||||
rust = false,
|
||||
python = false,
|
||||
}: ReplOptions): Promise<void> {
|
||||
const container = (lang: string): void => createContainerPlugin(md, `${lang}-repl`, {
|
||||
before(info) {
|
||||
@ -37,6 +38,9 @@ export async function langReplPlugin(app: App, md: markdownIt, {
|
||||
if (rust)
|
||||
container('rust')
|
||||
|
||||
if (python)
|
||||
container('python')
|
||||
|
||||
theme ??= { light: 'github-light', dark: 'github-dark' }
|
||||
|
||||
const data: ReplEditorData = { grammars: {} } as ReplEditorData
|
||||
@ -66,6 +70,9 @@ export async function langReplPlugin(app: App, md: markdownIt, {
|
||||
|
||||
if (rust)
|
||||
data.grammars.rust = await readGrammar('rust')
|
||||
|
||||
if (python)
|
||||
data.grammars.python = await readGrammar('python')
|
||||
}
|
||||
catch {
|
||||
/* istanbul ignore next -- @preserve */
|
||||
|
||||
@ -30,6 +30,9 @@ export function markdownPowerPlugin(
|
||||
app,
|
||||
['shiki/core', 'shiki/wasm', 'shiki/engine/oniguruma'],
|
||||
)
|
||||
|
||||
if (options.repl.python)
|
||||
addViteOptimizeDepsInclude(bundlerOptions, app, ['pyodide'])
|
||||
}
|
||||
if (options.artPlayer) {
|
||||
addViteOptimizeDepsInclude(
|
||||
|
||||
@ -13,6 +13,7 @@ export interface ReplOptions {
|
||||
go?: boolean
|
||||
kotlin?: boolean
|
||||
rust?: boolean
|
||||
python?: boolean
|
||||
}
|
||||
|
||||
export interface ReplEditorData {
|
||||
@ -20,6 +21,7 @@ export interface ReplEditorData {
|
||||
go?: any
|
||||
kotlin?: any
|
||||
rust?: any
|
||||
python?: any
|
||||
}
|
||||
theme: ThemeRegistration | {
|
||||
light: ThemeRegistration
|
||||
|
||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@ -142,6 +142,9 @@ catalogs:
|
||||
mpegts.js:
|
||||
specifier: 1.7.3
|
||||
version: 1.7.3
|
||||
pyodide:
|
||||
specifier: ^0.27.6
|
||||
version: 0.27.6
|
||||
sass-loader:
|
||||
specifier: ^16.0.5
|
||||
version: 16.0.5
|
||||
@ -647,6 +650,9 @@ importers:
|
||||
nanoid:
|
||||
specifier: catalog:prod
|
||||
version: 5.1.5
|
||||
pyodide:
|
||||
specifier: catalog:peer
|
||||
version: 0.27.6
|
||||
sass:
|
||||
specifier: ^1.89.0
|
||||
version: 1.89.0
|
||||
@ -5787,6 +5793,10 @@ packages:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
pyodide@0.27.6:
|
||||
resolution: {integrity: sha512-ahiSHHs6iFKl2f8aO1wALINAlMNDLAtb44xCI87GQyH2tLDk8F8VWip3u1ZNIyglGSCYAOSFzWKwS1f9gBFVdg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
qs@6.13.1:
|
||||
resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==}
|
||||
engines: {node: '>=0.6'}
|
||||
@ -6979,6 +6989,18 @@ packages:
|
||||
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
|
||||
ws@8.18.2:
|
||||
resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xml-name-validator@4.0.0:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -12569,6 +12591,13 @@ snapshots:
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
pyodide@0.27.6:
|
||||
dependencies:
|
||||
ws: 8.18.2
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
qs@6.13.1:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
@ -13798,6 +13827,8 @@ snapshots:
|
||||
imurmurhash: 0.1.4
|
||||
signal-exit: 4.1.0
|
||||
|
||||
ws@8.18.2: {}
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
xmldom-sre@0.1.31: {}
|
||||
|
||||
@ -61,6 +61,7 @@ catalogs:
|
||||
hls.js: ^1.6.2
|
||||
mathjax-full: ^3.2.2
|
||||
mpegts.js: 1.7.3
|
||||
pyodide: ^0.27.6
|
||||
sass: ^1.89.0
|
||||
sass-embedded: ^1.89.0
|
||||
sass-loader: ^16.0.5
|
||||
|
||||
@ -7,6 +7,7 @@ import { createTranslate, logger } from '../utils/index.js'
|
||||
|
||||
const DEPENDENCIES: Record<string, string[]> = {
|
||||
twoslash: ['@vuepress/shiki-twoslash'],
|
||||
pythonRepl: ['pyodide'],
|
||||
|
||||
chartjs: ['chart.js'],
|
||||
echarts: ['echarts'],
|
||||
@ -49,6 +50,9 @@ export function detectDependencies(options: ThemeOptions, plugins: ThemeBuiltinP
|
||||
if (options.codeHighlighter && options.codeHighlighter.twoslash)
|
||||
add('twoslash')
|
||||
|
||||
if (markdown.repl && markdown.repl.python)
|
||||
add('pythonRepl')
|
||||
|
||||
;['chartjs', 'echarts', 'markmap', 'mermaid', 'flowchart'].forEach((dep) => {
|
||||
if (markdown[dep] || mdEnhance[dep])
|
||||
add(dep)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user