✨ feat(vscode): 代码阅读认出代码里起的中文名(Annotated[…, Comment("…")]、@Comment 装饰器、行尾 # @Comment、docstring / JSDoc 的参数说明),预览里定义和用到它的各处都写中文名,悬停看原名,定义处后面淡淡写原名
This commit is contained in:
1 parent
1b1299dc1f
commit
44c221ea2b
45 files changed
+1685
-128
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import type { DisplayName, DisplayNameFacts, Node, SourceRange } from '../contract'
|
||||
|
||||
/** 给定义起中文名的那个调用 / 装饰器 / 注释记号的名字:`Comment("当前用户")`、`@Comment("…")`、`# @Comment 当前用户`。 */
|
||||
export const COMMENT_MARK = 'Comment'
|
||||
|
||||
/** 说明文字里的一句话最多几个字才当中文名 */
|
||||
export const CLAUSE_LIMIT = 12
|
||||
|
||||
/** 说明文字的第一小句(到第一个 ,。,.;;:: 为止)去掉首尾空白;空的、超过 CLAUSE_LIMIT 个字的不当中文名。 */
|
||||
export function firstClause(text: string): string | undefined {
|
||||
const clause = text.split(/[,。,.;;::]/, 1)[0].trim()
|
||||
return clause && [...clause].length <= CLAUSE_LIMIT ? clause : undefined
|
||||
}
|
||||
|
||||
/** 行注释里的中文名记号 `@Comment 名字`(名字一直到注释结尾):名字,和记号从注释文字的第几个字开始。 */
|
||||
export function commentTag(text: string): { text: string, start: number } | undefined {
|
||||
const match = new RegExp(`@${COMMENT_MARK}[ \\t]+(\\S.*)$`).exec(text)
|
||||
return match ? { text: match[1].trimEnd(), start: match.index } : undefined
|
||||
}
|
||||
|
||||
/** 一个定义和语言插件从代码里读到的中文名。 */
|
||||
export interface NameCandidate {
|
||||
/** 定义处那个名字 */
|
||||
name: Node
|
||||
/** 写在代码里的记号给的:`Annotated[T, Comment("…")]`、`@Comment("…")` */
|
||||
marked?: string
|
||||
/** 说明文字里取的:docstring 的参数说明、JSDoc 的 @param */
|
||||
described?: string
|
||||
/** 行尾的 `@Comment` 记号能给它:参数、变量、字段、枚举成员、属性(函数、类这些用装饰器) */
|
||||
trailing?: boolean
|
||||
}
|
||||
|
||||
function rangeOf(node: Node): SourceRange {
|
||||
return { startIndex: node.startIndex, endIndex: node.endIndex }
|
||||
}
|
||||
|
||||
/** 注释前面同一行有代码:是写在行尾的注释。 */
|
||||
function isTrailing(comment: Node, source: string): boolean {
|
||||
const lineStart = source.lastIndexOf('\n', comment.startIndex - 1) + 1
|
||||
return source.slice(lineStart, comment.startIndex).trim() !== ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总一份源码里的中文名。行尾注释里的记号(`x = 1 # @Comment 数量`)给那一行第一个能带行尾记号的定义(多行的参数表里就是那一行的参数),
|
||||
* 记号都算 markers(翻注释、说明时去掉),不管这个定义最后取没取它。
|
||||
* 每个定义取哪个:写在代码里的记号,其次行尾记号,最后才是说明文字。
|
||||
*/
|
||||
export function displayNameFacts(candidates: readonly NameCandidate[], lineComments: readonly Node[], source: string): DisplayNameFacts {
|
||||
const byStart = new Map<number, NameCandidate & { tagged?: string }>()
|
||||
for (const candidate of candidates) {
|
||||
const known = byStart.get(candidate.name.startIndex)
|
||||
byStart.set(candidate.name.startIndex, {
|
||||
name: candidate.name,
|
||||
marked: known?.marked ?? candidate.marked,
|
||||
described: known?.described ?? candidate.described,
|
||||
trailing: known?.trailing || candidate.trailing,
|
||||
})
|
||||
}
|
||||
const ordered = [...byStart.values()].sort((a, b) => a.name.startIndex - b.name.startIndex)
|
||||
const markers: SourceRange[] = []
|
||||
for (const comment of lineComments) {
|
||||
const tag = isTrailing(comment, source) ? commentTag(comment.text) : undefined
|
||||
const owner = tag && ordered.find(candidate => candidate.trailing && candidate.name.startPosition.row === comment.startPosition.row)
|
||||
if (!tag || !owner)
|
||||
continue
|
||||
owner.tagged ??= tag.text
|
||||
markers.push({ startIndex: comment.startIndex + tag.start, endIndex: comment.endIndex })
|
||||
}
|
||||
const names = ordered.flatMap((candidate): DisplayName[] => {
|
||||
const text = candidate.marked ?? candidate.tagged ?? candidate.described
|
||||
return text ? [{ name: rangeOf(candidate.name), text }] : []
|
||||
})
|
||||
return { names, markers }
|
||||
}
|
||||
Reference in new issue
Block a user