阅读侧(syntect)此前对 ```vue 回退纯文本:syntect 默认集无 Vue
语法、syntaxes/ 无定义、别名表无 vue 条目。
官方 Vue Component.sublime-syntax 因 extends: Packages/HTML/...
用 Sublime Text 包路径,syntect 的 add_from_folder 解析不了(实测
panic),无法直接采用。改为自包含实现:
- syntaxes/Vue.sublime-syntax:自包含 Vue SFC 语法。<template>/
<script>/<style> 三段在 main 入口按标签名分流(syntect context
切换后无法回看标签名)。script 按 lang 属性经前瞻分流
ts/tsx→source.ts、其余→source.js;style 统一 embed source.css
(scss/less 无对应 scope,因 CSS 是超集兼容);template 内 {{ }}
插值 embed source.js,其余走内置 HTML 语法。
- 关键约束(syntect 5.3 实测):embed 规则必须用纯净 match+embed+
escape 写法,附加 pop/embed_scope 会破坏 embed 使其静默回退纯文本;
escape 用 lookahead 不消费闭合标签。
编辑器侧(lowlight@3.3.0)与阅读侧是独立链路。lowlight 依赖的
highlight.js 11 早已移除 vue grammar,common/all 均不含;
highlightjs-vue 包停在 2019 年 1.0.0 且用旧版 hljsDefineVue 全局
API,与 lowlight@3 的 ESM register 不兼容。故编辑器侧用 registerAlias
把 vue 注册为 xml 别名兜底(template 段按 XML 着色,script/style 段
不精确但不抛 Unknown language);读者看到的阅读侧仍是完整高亮。
src/highlight.rs 别名表加 vue 条目(兜底大写 ```Vue 边界),新增 3 个
Vue 高亮单测(SFC 三段 / lang=ts 走 TS / 大小写别名)。
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { common, createLowlight } from 'lowlight';
|
||
|
||
const base = createLowlight(common);
|
||
|
||
// Vue SFC 别名:lowlight 依赖的 highlight.js 11 早已移除 vue grammar,
|
||
// common/all 均不含 vue。这里把 vue 注册为 xml 的别名,使 ```vue 代码块
|
||
// 在编辑器写作时按 XML/HTML 着色(template 段正常,script/style 段不精确
|
||
// 但不抛 Unknown language)。阅读侧(读者看到的)走 syntect 自包含 Vue
|
||
// 语法(syntaxes/Vue.sublime-syntax),三段都有完整高亮,两侧独立互不影响。
|
||
base.registerAlias({ xml: ['vue'] });
|
||
|
||
/**
|
||
* 从完整 fence info string 提取语言名(首个 token,小写化)。
|
||
*
|
||
* 与后端 parse_fence_info(src/api/code_runner/languages.rs:85)对齐:
|
||
* info string 形如 `python runnable {"timeout_secs":10}`,语言是首个空白分隔的 token,
|
||
* 再 to_lowercase(lowlight 注册名均为小写,大写会导致 Unknown language 抛错)。
|
||
*/
|
||
export function extractLang(info: string): string {
|
||
const first = info.trim().split(/\s+/)[0];
|
||
return first ? first.toLowerCase() : '';
|
||
}
|
||
|
||
/**
|
||
* 从完整 fence info string 提取 overrides JSON 字符串。
|
||
*
|
||
* 与后端 parse_fence_info(src/api/code_runner/languages.rs:93-97)对齐:
|
||
* info string 形如 `python runnable {"timeout_secs":10}`,提取以 `{` 开头的 token。
|
||
* 无 overrides 时返回空串(Rust 侧空串视为 None)。
|
||
*/
|
||
export function extractOverridesJson(info: string): string {
|
||
const token = info
|
||
.trim()
|
||
.split(/\s+/)
|
||
.find((t) => t.startsWith('{'));
|
||
return token ?? '';
|
||
}
|
||
|
||
/**
|
||
* lowlight 实例,包装 highlight 方法以处理完整 info string。
|
||
*
|
||
* CodeBlockLowlight 取 `block.node.attrs.language`(如 `python runnable {...}`)
|
||
* 直接调 `lowlight.highlight(language, code)`,但 lowlight 只认 `python` 这个 token。
|
||
* wrapper 先 extractLang 提取首 token,再高亮,使 runnable 块也能按正确语言着色。
|
||
*
|
||
* 普通 code block(language='python')也兼容:extractLang('python') → 'python'。
|
||
*/
|
||
export const lowlight = {
|
||
...base,
|
||
highlight(language: string, value: string) {
|
||
return base.highlight(extractLang(language), value);
|
||
},
|
||
};
|