✨ feat(vscode): 代码阅读把说明里的 @param / @return / @throws 拆到参数表、输出、异常表

This commit is contained in:
cruldra committed 2026-10-08 07:17:26 +08:00
1 parent c2982537d7
commit 0958506681
43 files changed
+1300 -323

No files matched your search

@@ -1,7 +1,8 @@
import type { CSSProperties, ReactElement, ReactNode } from 'react'
import type { BlockView, Inline, InlinePart, ItemDiff, ItemsDiff, KindBadge, PropertyItem, PropertyRow, Removed, RowsDiff } from './types'
import type { BlockView, ItemDiff, ItemsDiff, KindBadge, PreviewText, PropertyItem, PropertyRow, Removed, RowDiff, RowsDiff, TextDiff } from './types'
import { ChevronDown, ChevronRight } from 'lucide-react'
import { Fragment } from 'react'
import { cn } from '@/lib/utils'
import { DiffInline, DiffText } from './DiffText'
import { inlineText } from './segments'
import { SegmentText } from './SegmentText'
@@ -51,23 +52,51 @@ function removedAt<T>(removed: readonly Removed<T>[] | undefined, after: number)
return (removed ?? []).flatMap((entry, index) => entry.after === after ? [{ value: entry.value, index }] : [])
}
/** 一格的字:这一格的值改了就逐截标(DiffInline),否则照常。 */
function CellText({ cell, parts }: { cell: Inline[], parts: InlinePart[] | undefined }): ReactElement {
return parts ? <DiffInline parts={parts} /> : <SegmentText segments={cell} />
/** 一格是不是成块排的 Markdown 说明(参数的说明、异常的触发条件):它不能放进 .cr-code 的段落里。 */
function isBlock(cell: PreviewText, parts: TextDiff | undefined): boolean {
return 'markdown' in cell || (parts !== undefined && 'markdown' in parts)
}
/** 一格的字:着色的词照常,改了的逐截标(DiffInline);Markdown 说明照常排版,改了的逐词标(DiffText)。 */
function CellText({ cell, parts }: { cell: PreviewText, parts: TextDiff | undefined }): ReactElement {
if (parts)
return 'inline' in parts ? <DiffInline parts={parts.inline} /> : <DiffText diff={parts} className="min-w-0" />
return 'markdown' in cell ? <TextPreview text={cell} className="min-w-0" /> : <SegmentText segments={cell} />
}
/** 一格连外面那层:着色的词排在 .cr-code 的段落里,Markdown 说明是块,lead(差异记号、展开按钮)排在它左边。 */
function CellBox({ cell, parts, className, lead, ...attrs }: { cell: PreviewText, parts: TextDiff | undefined, className?: string, lead?: ReactNode, role?: string }): ReactElement {
if (isBlock(cell, parts)) {
return (
<div className={cn('flex min-w-0 items-start gap-1', className)} {...attrs}>
{lead}
<CellText cell={cell} parts={parts} />
</div>
)
}
return (
<p className={cn('cr-code min-w-0', className)} {...attrs}>
{lead}
<CellText cell={cell} parts={parts} />
</p>
)
}
/** 格子写成一串字(读屏念的展开 / 折叠按钮名):着色的词拼起来,Markdown 取原文。 */
function cellLabel(cell: PreviewText): string {
return 'markdown' in cell ? cell.markdown : inlineText(cell)
}
/**
* 子行的第一列(字典的键):带下一层的子行前面是折叠箭头(图标、大小、颜色同属性行的),点箭头或这一列切换;
* 同一层里不带下一层的子行在同样的位置留空,第一列才对得齐。
*/
function ItemHead({ id, cell, foldable, folding }: { id: string, cell: Inline[], foldable: boolean, folding: Folding }): ReactElement {
function ItemHead({ id, cell, foldable, folding }: { id: string, cell: PreviewText, foldable: boolean, folding: Folding }): ReactElement {
if (!foldable) {
return (
<div className="flex min-w-0 gap-1">
<span className="w-3.5 shrink-0" />
<p className="cr-code min-w-0">
<SegmentText segments={cell} />
</p>
<CellBox cell={cell} parts={undefined} />
</div>
)
}
@@ -80,15 +109,15 @@ function ItemHead({ id, cell, foldable, folding }: { id: string, cell: Inline[],
<button
type="button"
className="flex h-5 w-3.5 shrink-0 items-center rounded-sm"
aria-label={`${folded ? '展开' : '折叠'}${inlineText(cell)}`}
aria-label={`${folded ? '展开' : '折叠'}${cellLabel(cell)}`}
aria-expanded={!folded}
onClick={toggle}
>
<Chevron size={14} strokeWidth={1.8} className="text-[var(--vscode-descriptionForeground)] group-hover:text-[var(--vscode-foreground)]" />
</button>
<p className="cr-code min-w-0 cursor-pointer" onClick={toggle}>
<SegmentText segments={cell} />
</p>
<div className="min-w-0 cursor-pointer" onClick={toggle}>
<CellBox cell={cell} parts={undefined} />
</div>
</div>
)
}
@@ -114,11 +143,7 @@ function PropertyItems({ parentId, items, folding, marks }: { parentId: string,
>
{item.cells.map((cell, i) => i === 0 && nested
? <ItemHead key={i} id={id} cell={cell} foldable={children.length > 0 && change !== 'del'} folding={folds} />
: (
<p key={i} className="cr-code min-w-0">
<CellText cell={cell} parts={mark?.cells?.[i]} />
</p>
))}
: <CellBox key={i} cell={cell} parts={mark?.cells?.[i]} />)}
</div>
{children.length > 0 && !folds.collapsed.has(id) && (change === 'del'
? (
@@ -171,18 +196,18 @@ export function PropertyTable({ parentId, columns, items, marks, toggles }: {
/** 一行:删掉的(change 是 del)不带 id,套着的表不再标;toggle 是这一行讲的子块,第一格成了展开它的按钮 */
const tableRow = (item: PropertyItem, id: string, change: 'add' | 'del' | undefined, mark: ItemDiff | undefined, toggle?: ItemToggle): ReactElement => {
const cells = tableCells(item, columns)
const text = (cell: Inline[], i: number): ReactElement => (
<>
{signed && i === 0 && <span className="cr-diff-sign" data-change={change} aria-hidden>{SIGNS[change ?? 'none']}</span>}
{toggle && i === 0
? (
<button type="button" className="cr-item-toggle" aria-expanded={toggle.open} aria-label={`${toggle.open ? '收起' : '展开'}${inlineText(cell)}`} onClick={toggle.onToggle}>
<CellText cell={cell} parts={mark?.cells?.[i]} />
</button>
)
: <CellText cell={cell} parts={mark?.cells?.[i]} />}
</>
)
const sign = (i: number): ReactNode => signed && i === 0 && <span className="cr-diff-sign" data-change={change} aria-hidden>{SIGNS[change ?? 'none']}</span>
/** 一格:讲子块的那一格是展开它的按钮(只放着色的词),别的照常(见 CellBox) */
const cellBox = (cell: PreviewText, i: number, className?: string): ReactElement => toggle && i === 0
? (
<p className={cn('cr-code min-w-0', className)} role="cell">
{sign(i)}
<button type="button" className="cr-item-toggle" aria-expanded={toggle.open} aria-label={`${toggle.open ? '收起' : '展开'}${cellLabel(cell)}`} onClick={toggle.onToggle}>
<CellText cell={cell} parts={mark?.cells?.[i]} />
</button>
</p>
)
: <CellBox cell={cell} parts={mark?.cells?.[i]} className={className} lead={sign(i)} role="cell" />
const tint = toggle?.open && toggle.badge
? { 'data-badge': '', 'style': { '--kind-dark': toggle.badge.color.dark, '--kind-light': toggle.badge.color.light } as CSSProperties }
: {}
@@ -191,11 +216,11 @@ export function PropertyTable({ parentId, columns, items, marks, toggles }: {
{cells.map((cell, i) => item.table && i === cells.length - 1
? (
<div key={i} className="cr-table-cell flex min-w-0 flex-col gap-[3px]" role="cell">
<p className="cr-code">{text(cell, i)}</p>
<CellBox cell={cell} parts={mark?.cells?.[i]} lead={sign(i)} />
<PropertyTable parentId={tableId(id)} columns={item.table.columns} items={item.table.items} marks={mark?.table} />
</div>
)
: <p key={i} className="cr-table-cell cr-code min-w-0" role="cell">{text(cell, i)}</p>)}
: <Fragment key={i}>{cellBox(cell, i, 'cr-table-cell')}</Fragment>)}
</div>
)
}
@@ -271,7 +296,7 @@ export function PropertyGrid({ blockId, rows, collapsed, onToggleFold, marks, sl
</button>
)
: <span className="cr-prop-label flex h-5 items-center">{row.label}</span>}
{mark?.value ? <DiffText diff={mark.value} className="min-w-0" /> : <TextPreview text={row.value} className="min-w-0" />}
<RowValue row={row} mark={mark} />
</div>
{!folded && layout === 'table' && <PropertyTable parentId={id} columns={row.columns ?? []} items={row.items ?? []} marks={mark?.items} toggles={toggles} />}
{!folded && layout === 'list' && <PropertyItems parentId={id} items={row.items ?? []} folding={{ collapsed, onToggleFold }} marks={mark?.items} />}
@@ -284,6 +309,22 @@ export function PropertyGrid({ blockId, rows, collapsed, onToggleFold, marks, sl
)
}
/**
* 一行的值:着色的词或 Markdown(改了的逐截 / 逐词标);跟着说明(PropertyRow.note,输出参数的 `@return`)时说明接在值后面,
* 同一行放得下就并排,剩的地方不到 12 个字宽时换到下一行。
*/
function RowValue({ row, mark }: { row: PropertyRow, mark: RowDiff | undefined }): ReactElement {
const value = mark?.value ? <DiffText diff={mark.value} className="min-w-0" /> : <TextPreview text={row.value} className="min-w-0" />
if (!row.note)
return value
return (
<div className="flex min-w-0 flex-wrap items-baseline gap-x-2">
{value}
{mark?.note ? <DiffText diff={mark.note} className="min-w-0 flex-[1_1_12em]" /> : <TextPreview text={row.note} className="min-w-0 flex-[1_1_12em]" />}
</div>
)
}
/** 属性表里删掉的一行:整行淡红底、划掉,左边 −;它下面的子行照样摊开、划掉。 */
function RemovedRow({ id, row }: { id: string, row: PropertyRow }): ReactElement {
const layout = subRowsLayout(row)
@@ -291,7 +332,7 @@ function RemovedRow({ id, row }: { id: string, row: PropertyRow }): ReactElement
<>
<div className="cr-prop-row grid grid-cols-subgrid" data-diff="del">
<span className="cr-prop-label flex h-5 items-center">{row.label}</span>
<TextPreview text={row.value} className="min-w-0" />
<RowValue row={row} mark={undefined} />
</div>
{layout && (
<div className="cr-diff-gone">
@@ -11,6 +11,7 @@ import { HighlightContext } from './useOccurrences'
* 按词性给每一段上色,颜色在 codeReader.css 的 .cr-role-* 里按主题切换,带自己颜色(color)的段用它(.cr-colored);带源码下标的名字记在 data-offset 上
* (讲别处名字的段记那个名字的下标,见 sourceOf),跳到定义、高亮各处出现用;是眼下高亮着的名字的一处出现时 data-occurrence 标出底色,是当前那一处(点中的、用键盘跳到的)时
* 再标 data-current。起了中文名的名字显示中文名(data-name),悬停看原名,定义处本身在后面淡淡写原名;下标照旧,跳到定义、高亮照常。
* 带悬停提示(tooltip)的段悬停看它。
* 框起来的一组词画成小框,框里的词同样处理。
*/
export function SegmentText({ segments }: { segments: Inline[] }): ReactElement {
@@ -31,7 +32,7 @@ export function SegmentText({ segments }: { segments: Inline[] }): ReactElement
data-occurrence={highlight && segmentOccurrence(piece, highlight.occurrences)}
data-current={highlight && isCurrent(piece, highlight) ? '' : undefined}
data-name={shown?.name}
data-tooltip={shown && `原名 ${piece.text}`}
data-tooltip={shown ? `原名 ${piece.text}` : piece.tooltip}
>
{shown ? shown.name : piece.text}
{shown?.definition && <span className="cr-original">{piece.text}</span>}
+2 -2
View File
@@ -1,4 +1,4 @@
import type { Inline, PropertyItem, PropertyRow } from './types'
import type { PreviewText, PropertyItem, PropertyRow } from './types'
/**
* 一行下面展开的子行怎么排:翻译插件给了列名的(输入参数)排成带表头和边框的表格,没给的(导入项、字典 / 对象的各项)排成列表;
@@ -21,6 +21,6 @@ export function tableTemplate(count: number): string {
}
/** 表格里一个子行的格子:按列名的列数取,子行少给的格子留空、多给的不显示。 */
export function tableCells(item: PropertyItem, columns: string[]): Inline[][] {
export function tableCells(item: PropertyItem, columns: string[]): PreviewText[] {
return columns.map((_, i) => item.cells[i] ?? [])
}
+13 -6
View File
@@ -23,8 +23,9 @@ export type TokenRole
* reference:库规则自己查定义的名字(Celery 的任务名)才有;webview 不看它,照样只报 offset,扩展端按 offset 认出来交给规则查。
* color:这段自己的颜色(库规则按库自己的配色给的,例如 HTTP 方法),盖过词性的颜色,按主题挑暗色、亮色。
* refers:这段讲的是源码里别处的一个名字(写在字符串里的名字讲的参数):跳到定义、高亮各处出现都当它是那个名字,见 sourceOf
* tooltip:悬停这段时看的字(说明里写了、参数表里没有的参数名:「没有这个参数」)
*/
export interface Segment { text: string, role: TokenRole, offset?: number, reference?: RuleReference, color?: ThemeColor, refers?: SourceRange }
export interface Segment { text: string, role: TokenRole, offset?: number, reference?: RuleReference, color?: ThemeColor, refers?: SourceRange, tooltip?: string }
/** 规则自己查得到定义的一个名字:哪个规则、规则自己认的键 */
export interface RuleReference { rule: string, key: string }
@@ -51,7 +52,10 @@ export interface SegmentGroup {
/** 译文里的一段:着色的词,或框起来的一组词 */
export type Inline = Segment | SegmentGroup
/** 一段 Markdown 说明文字:注释、JSDoc、docstring 去掉注释符号、引号和共同缩进后的原文,按 Markdown 渲染成注释色的段落、列表、代码块 */
/**
* 一段 Markdown 说明文字:注释、JSDoc、docstring 去掉注释符号、引号和共同缩进后的原文,按 Markdown 渲染成注释色的段落、列表、代码块;
* 不带路径的引用(`[名字]`、`[类.成员]`)问扩展端它指的定义,问到了能点
*/
export interface Markdown { markdown: string }
/** 预览里显示的一段文字:着色的词(一行译文、属性值),或整段 Markdown 说明(注释块、函数和类的说明) */
@@ -66,9 +70,9 @@ export interface SourceRange { startIndex: number, endIndex: number }
*/
export interface Defines { defines?: SourceRange }
/** 属性表里展开的一个子行:几列对齐的格子,例如参数的名称 / 类型 / 默认值、字典一项的键 / 值 */
/** 属性表里展开的一个子行:几列对齐的格子,例如参数的名称 / 类型 / 默认值、字典一项的键 / 值;格子是着色的词或 Markdown(参数的说明、异常的触发条件) */
export interface PropertyItem extends Defines {
cells: Inline[][]
cells: PreviewText[]
/** 展开在这个子行下面的下一层子行(字典 / 对象的值又是字典 / 对象时,它的各项),一层层可以一直往下套 */
items?: PropertyItem[]
/** 排在这一行最后一格里、那格文字下面的表(实参的值里跨几行写的调用的实参表),表里的行又可以带 table;总是展开 */
@@ -82,6 +86,8 @@ export interface PropertyItem extends Defines {
export interface PropertyRow extends Defines {
label: string
value: PreviewText
/** 跟在值后面的一段说明(Markdown,注释色):函数输出参数那一行,返回类型后面是说明里的 `@return` */
note?: Markdown
items?: PropertyItem[]
columns?: string[]
}
@@ -324,7 +330,7 @@ export interface ItemsDiff {
/** 一个子行的改动:整行新加的(它下面各层也都是),或按列标的格子、下一层、套着的表 */
export interface ItemDiff {
added?: true
cells?: Record<number, InlinePart[]>
cells?: Record<number, TextDiff>
items?: ItemsDiff
table?: ItemsDiff
}
@@ -335,10 +341,11 @@ export interface RowsDiff {
removed: Removed<PropertyRow>[]
}
/** 属性表一行的改动:整行新加的(它下面的子行也都是),或值、子行改了 */
/** 属性表一行的改动:整行新加的(它下面的子行也都是),或值、跟在值后面的说明、子行改了 */
export interface RowDiff {
added?: true
value?: TextDiff
note?: TextDiff
items?: ItemsDiff
}