✨ feat(vscode): 代码阅读预览按住 ⌘ / Ctrl 点名字跳到定义
This commit is contained in:
1 parent
e5db1a2e65
commit
4e146eb48f
20 files changed
+870
-60
No files matched your search
@@ -56,6 +56,8 @@ interface Token {
|
||||
raw: string
|
||||
/** 前面要空一格:源码里它前面有空白,或者模板在它前面插了别的项。 */
|
||||
gap: boolean
|
||||
/** 照抄的名字、字符串在源码里的起始下标,见 Segment.offset。 */
|
||||
offset?: number
|
||||
}
|
||||
|
||||
/** 按译文顺序收集词:cursor 是上一个词在源码里的结尾,gap 是下一个词前面要不要空一格。 */
|
||||
@@ -69,6 +71,8 @@ interface Collector {
|
||||
const CONTEXT_SEPARATOR = ' > '
|
||||
const CLOSING = new Set([')', ']', '}'])
|
||||
const HEADER_OPENERS = new Set(['{', ':'])
|
||||
/** 源码着色里代表名字的词性:着这几种色的具名叶子是标识符、属性名、类型名,能查定义。 */
|
||||
const NAME_ROLES = new Set<TokenRole>(['variable', 'function', 'type'])
|
||||
|
||||
function firstLeaf(node: Node): Node {
|
||||
const first = node.child(0)
|
||||
@@ -123,6 +127,12 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return text
|
||||
}
|
||||
|
||||
/** 照抄这个节点时带不带源码下标:名字(按名字着色的具名叶子)和整段字符串带,标点、数字、关键字不带。 */
|
||||
function offsetOf(node: Node): number | undefined {
|
||||
const linkable = stringNodes.has(node.type) || (node.childCount === 0 && node.isNamed && NAME_ROLES.has(table.tokenRole(node)))
|
||||
return linkable ? node.startIndex : undefined
|
||||
}
|
||||
|
||||
/** 能整个换成中文的节点 → 译文和词性。 */
|
||||
function translateNode(node: Node): { text: string, role: TokenRole } | undefined {
|
||||
if (node.childCount === 0 && table.isTypePosition(node)) {
|
||||
@@ -201,7 +211,7 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
}
|
||||
if (node.childCount === 0 || stringNodes.has(node.type)) {
|
||||
const role = stringNodes.has(node.type) ? 'string' : table.tokenRole(node)
|
||||
emitSource(out, node, { text: oneLine(node.text), role, translated: false })
|
||||
emitSource(out, node, { text: oneLine(node.text), role, translated: false, offset: offsetOf(node) })
|
||||
return
|
||||
}
|
||||
for (const child of node.children) {
|
||||
@@ -217,8 +227,8 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return out.tokens
|
||||
}
|
||||
|
||||
/** node 的源码原样照抄:叶子按源码着色,叶子之间的空白、标点照抄。 */
|
||||
function verbatim(node: Node, source: string): Segment[] {
|
||||
/** node 的源码原样照抄:叶子按源码着色(给了 role 就都按它),叶子之间的空白、标点照抄。 */
|
||||
function verbatim(node: Node, source: string, role?: TokenRole): Segment[] {
|
||||
const segments: Segment[] = []
|
||||
let cursor = node.startIndex
|
||||
const visit = (current: Node): void => {
|
||||
@@ -232,7 +242,7 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return
|
||||
}
|
||||
pushSegment(segments, source.slice(cursor, current.startIndex), 'plain')
|
||||
pushSegment(segments, current.text, table.tokenRole(current))
|
||||
pushSegment(segments, current.text, role ?? table.tokenRole(current), offsetOf(current))
|
||||
cursor = current.endIndex
|
||||
}
|
||||
visit(node)
|
||||
@@ -274,7 +284,9 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return joinTokens(tokensIn(node, span, source))
|
||||
},
|
||||
verbatim: node => verbatim(node, source),
|
||||
module: node => [{ text: node.text, role: stringNodes.has(node.type) ? 'string' : 'type' }],
|
||||
// 点分模块名逐节照抄,每一节各自能跳到定义,和 VS Code 里一样
|
||||
module: node => stringNodes.has(node.type) ? [segment(node.text, 'string', offsetOf(node))] : verbatim(node, source, 'type'),
|
||||
name: (node, role) => [segment(node.text, role, offsetOf(node))],
|
||||
}
|
||||
switch (block.kind) {
|
||||
case 'function':
|
||||
@@ -289,14 +301,18 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return { id: table.id, label: table.label, languageIds: table.languageIds, translate, describe }
|
||||
}
|
||||
|
||||
function pushSegment(segments: Segment[], text: string, role: TokenRole): void {
|
||||
function segment(text: string, role: TokenRole, offset: number | undefined): Segment {
|
||||
return offset === undefined ? { text, role } : { text, role, offset }
|
||||
}
|
||||
|
||||
function pushSegment(segments: Segment[], text: string, role: TokenRole, offset?: number): void {
|
||||
if (!text)
|
||||
return
|
||||
const prev = segments.at(-1)
|
||||
if (role === 'plain' && prev?.role === 'plain')
|
||||
prev.text += text
|
||||
else
|
||||
segments.push({ text, role })
|
||||
segments.push(segment(text, role, offset))
|
||||
}
|
||||
|
||||
/** 词连成译文:源码里隔着空白的词之间空一格,注释不进译文,右括号前的尾逗号去掉。 */
|
||||
@@ -312,7 +328,7 @@ function joinTokens(tokens: Token[]): Segment[] {
|
||||
}
|
||||
if (gap && segments.length > 0)
|
||||
pushSegment(segments, ' ', 'plain')
|
||||
pushSegment(segments, token.text, token.role)
|
||||
pushSegment(segments, token.text, token.role, token.offset)
|
||||
// 「非」是个词,后面补一个空格,不和操作数粘在一起
|
||||
gap = token.translated && token.raw === '!'
|
||||
})
|
||||
@@ -329,6 +345,8 @@ interface Phrases {
|
||||
verbatim: (node: Node) => Segment[]
|
||||
/** 导入来源:字符串字面量按字符串着色,模块名按类型着色。 */
|
||||
module: (node: Node) => Segment[]
|
||||
/** 源码里的一个名字原样照抄、按 role 着色(函数名称)。 */
|
||||
name: (node: Node, role: TokenRole) => Segment[]
|
||||
}
|
||||
|
||||
const ACCESS_ZH: Record<Access, string> = { public: '公共', private: '私有', protected: '受保护' }
|
||||
@@ -363,7 +381,7 @@ function aliased(item: Segment[], alias: Node | undefined, say: Phrases): Segmen
|
||||
function functionRows(facts: FunctionFacts, say: Phrases): PropertyRow[] {
|
||||
const decorators = facts.decorators.map(say.verbatim)
|
||||
return [
|
||||
{ label: '名称', value: facts.name ? [{ text: facts.name.text, role: 'function' }] : [hint('匿名')] },
|
||||
{ label: '名称', value: facts.name ? say.name(facts.name, 'function') : [hint('匿名')] },
|
||||
...accessRows(facts.access),
|
||||
{ label: '异步', value: yesNo(facts.async) },
|
||||
...(decorators.length > 0 ? [{ label: '装饰器', value: joined(decorators, '\n') }] : []),
|
||||
|
||||
Reference in new issue
Block a user