✨ feat(vscode): 代码阅读关键字按语境翻译

This commit is contained in:
cruldra committed 2026-09-25 01:58:07 +08:00
1 parent 72d33032f0
commit d134837c17
5 files changed
+155 -19

No files matched your search

@@ -1,7 +1,16 @@
import type { CodeBlock, Node, Segment, SourceSpan, TokenRole, Translator } from '../contract'
/** 按父节点类型区分译法的关键字,例如 Python 的 in:for 头部里「取自」,比较里「在」。 */
export interface ContextualWord { zh: string, byParent: Record<string, string> }
/** 按所在语法节点区分译法的关键字,例如 Python 的 else:if 后「否则」,for / while 后「未中断时」。 */
export interface ContextualWord {
/** 没有匹配的语境时用的译法。 */
zh: string
/**
* 语境 → 译法。语境是关键字节点往上的节点类型链,外层在前、用 ` > ` 连接:
* `raise_statement` 指父节点,`for_statement > else_clause` 指父节点 else_clause 且祖父节点 for_statement。
* 同时匹配多条时取最长的那条。
*/
byContext: Record<string, string>
}
/** 一种语言的中文直译表。引擎只替换表里的三类东西,其余原样照抄。 */
export interface ZhTable {
@@ -10,7 +19,7 @@ export interface ZhTable {
languageIds: string[]
/** 未翻译的词按源码着色的词性上色;关键字译文的词性也取它。 */
tokenRole: (leaf: Node) => TokenRole
/** 关键字:按语法树里匿名节点的类型查(Python 的 `not in` 这类组合关键字本身也是一个匿名节点)。 */
/** 关键字:按语法树里匿名节点的类型查(Python 的 `not in` 这类组合关键字本身也是一个匿名节点);随所在语法节点换译法的写成 ContextualWord。 */
keywords: Record<string, string | ContextualWord>
/** 字面量关键字:具名的叶子节点(true / None …),按节点类型查。 */
literals: Record<string, string>
@@ -38,6 +47,7 @@ interface Token {
raw: string
}
const CONTEXT_SEPARATOR = ' > '
const CLOSING = new Set([')', ']', '}'])
const HEADER_OPENERS = new Set(['{', ':'])
@@ -70,6 +80,22 @@ function oneLine(text: string): string {
export function createZhTemplateTranslator(table: ZhTable): Translator {
const operatorParents = new Set(table.operatorParents)
const stringNodes = new Set(table.stringNodes)
const contextDepth = Math.max(0, ...Object.values(table.keywords).flatMap(word =>
typeof word === 'string' ? [] : Object.keys(word.byContext).map(context => context.split(CONTEXT_SEPARATOR).length)))
/** 从父节点往上逐层拼出语境去查,更长(更具体)的匹配覆盖更短的。 */
function keywordText(word: string | ContextualWord, node: Node): string {
if (typeof word === 'string')
return word
let text = word.zh
let context = ''
let ancestor = node.parent
for (let level = 0; ancestor && level < contextDepth; level++, ancestor = ancestor.parent) {
context = context ? `${ancestor.type}${CONTEXT_SEPARATOR}${context}` : ancestor.type
text = word.byContext[context] ?? text
}
return text
}
/** 能整个换成中文的节点 → 译文和词性。 */
function translateNode(node: Node): { text: string, role: TokenRole } | undefined {
@@ -83,10 +109,8 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
if (parent && operatorParents.has(parent.type) && table.operators[node.type])
return { text: table.operators[node.type], role: 'operator' }
const word = table.keywords[node.type]
if (word !== undefined) {
const text = typeof word === 'string' ? word : (parent && word.byParent[parent.type]) || word.zh
return { text, role: table.tokenRole(firstLeaf(node)) }
}
if (word !== undefined)
return { text: keywordText(word, node), role: table.tokenRole(firstLeaf(node)) }
return undefined
}
if (node.childCount === 0 && table.literals[node.type])